Handling Credentials
Getting your first token is only the beginning. Once an OAuth application is live, its credentials need to be stored safely, rotated occasionally, and revoked quickly if something goes wrong. These are the exact questions a security review asks. This page covers all three, plus what a leaked token can actually reach and what to do if one leaks.
Where a client secret belongs
Section titled “Where a client secret belongs”- Store it in an environment variable or a secrets manager (AWS Secrets Manager, Vault, or similar), never in source control, and never in a config file that gets committed alongside your code.
- Never ship it inside a desktop application or a browser-based client. Anything installed on a user’s machine or served to a browser can have its bundle inspected, so any secret embedded in it has to be treated as public the moment it ships.
- This is exactly why the Native Application flow issues no client secret at all: it’s built for desktop and CLI integrations that have no way to keep one confidential, so it relies on PKCE (a per-request proof tied to the authorization code) instead. The Authorization Code + custom HTTPS redirect flow also uses PKCE, and lets you add a client secret only for confidential clients whose redirect handler runs on a server you control. A browser-only (SPA) implementation of that flow should leave the secret off, for the same reason the Native flow never has one. See Getting Started: Choose an OAuth flow.
- Client Credentials is a server-to-server flow and always requires a secret: store it with the same rules as any other server-side credential.
Rotating a client secret without downtime
Section titled “Rotating a client secret without downtime”Regenerating a secret from Settings → OAuth on your application replaces it outright. There is no dual-secret window where the old and new secret are both valid: a caller still presenting the old secret is rejected starting with its very next token or refresh request.
That doesn’t mean every in-flight request breaks the instant you rotate, though. An access token already issued keeps working until its own TTL runs out (about an hour, see Token Lifecycle), regardless of what happens afterward to the secret that originally minted it. Only a new token request or refresh needs the new secret.
Recommended cutover sequence:
- Regenerate the secret.
- In the same change, update every place your integration stores it (config, secrets manager, every instance or worker) before any of them is next due to mint or refresh a token.
- Confirm new token requests succeed with the new secret.
If you can’t update every instance atomically, plan the rotation as a coordinated deploy rather than a gradual rollout: an instance still holding the old secret fails outright the next time it needs a token, it doesn’t fall back gracefully.
Revoking access
Section titled “Revoking access”There are two ways to cut off access, and both stop new access:
- Revoke a single authorization from Settings → Third-party access. This stops the application from refreshing or obtaining tokens through that authorization, without affecting the application for anyone else.
- Delete the OAuth application from Settings → OAuth. This revokes it for everyone: no further access or refresh tokens can be minted for it, and its client secret stops working the same way a rotated one would. For an application that uses client credentials, it also deactivates the application’s service user in the same action.
Revoking an authorization, deleting the application, and rotating its secret don’t force-expire an access token that was already issued. Treat that token as usable, with its original scopes, until its own TTL elapses. See Token Lifecycle: What invalidates a token early for the full picture of what does and doesn’t affect a live token.
What a leaked token can reach
Section titled “What a leaked token can reach”A leaked token isn’t a leak of your whole organization: it’s bounded twice over, matching the three-layer security model:
- Its OAuth scopes (Layer 1): fixed into the token when it was issued. Removing a scope from the application later doesn’t shrink a token that’s already out; that only takes effect the next time the token is refreshed or reissued.
- The service user’s content access and role (Layers 2 and 3): which nodes (organizations, divisions, sites) the token’s service user can see, and what it’s allowed to do there. Unlike scopes, this isn’t baked into the token: it’s evaluated against your current configuration every time a request is made.
The practical takeaway: the tighter you scope the application and its service user up front, the less a leaked token is worth.
After a suspected leak
Section titled “After a suspected leak”Work through this in order:
- Rotate the client secret immediately (
Settings → OAuth→ your application). This is the fastest thing you can do, and it stops any new token or refresh request from succeeding with the old secret. - Delete the OAuth application if you don’t intend to keep using it, or suspect the compromise goes beyond one secret. For a client-credentials application, this also deactivates its service user, cutting off new-token issuance entirely.
- Tighten or remove the service user’s content access. Because content and role access (Layers 2-3) is checked live rather than baked into the token, this shrinks what a still-valid leaked token can reach immediately, even before that token’s TTL runs out.
- Treat the token as live for one more TTL window (about an hour). Neither of the steps above force-expires a token that was already issued.
- Issue new credentials (a new secret, or a new application) and update your integration before resuming normal use.
What’s next?
Section titled “What’s next?”- Token Lifecycle for TTLs, caching, and refresh behavior
- Getting Started: Security model for how scopes, content access, and roles fit together