Documentation Index
Fetch the complete documentation index at: https://docs.busha.io/llms.txt
Use this file to discover all available pages before exploring further.
Token types at a glance
| Token | Format | Lifetime | Notes |
|---|---|---|---|
access_token | JWT (RS256) | ~1 hour | Validated locally. Cannot be revoked mid-flight — it self-expires at exp. |
refresh_token | Opaque string | 30 days, sliding | Single-use. Rotates on every refresh. Replay revokes the entire family. |
id_token | JWT (RS256) | ~1 hour | Identity proof only. Do not use as a bearer token on API calls. |
Validating access tokens
Access tokens are JWTs signed with RS256. Validate them locally on every request — no per-request introspection round-trip to Busha.Fetch and cache the JWKS
Fetch public keys from Read the canonical
<issuer>/.well-known/jwks.json. Cache for ~5 minutes. Refetch when you encounter a kid (key ID) that is not in your cache.issuer string from /.well-known/openid-configuration at startup and pin against it — do not hardcode.Match the key and verify the signature
Match the JWT header’s
kid to a key in the JWKS. Verify the RS256 signature with that key.Verify the standard claims
| Claim | Check |
|---|---|
iss | Must match the canonical issuer from the discovery document. |
exp | Must be in the future. Allow ~30 seconds of clock skew. |
aud | Must include your client_id (if present). |
client_id | Must match your registered client_id. |
Refreshing tokens
Every successful refresh issues a new(access_token, refresh_token) pair and immediately invalidates the old refresh token.
- Persist the new pair atomically before discarding the old one. If persistence fails, you lose the session.
- If a previously-rotated refresh token is replayed, Busha revokes the entire token family immediately.
- Any
invalid_grantresponse on a refresh is a hard disconnect — prompt the user to re-authorize. - The 30-day lifetime is sliding: each successful refresh resets the clock by another 30 days.
Revoking tokens
When a user disconnects your integration, revoke the refresh token:200 with an empty body whether or not the token was valid — this is intentional and prevents leaking token validity.
Revoking a refresh token prevents future refreshes immediately, but does not invalidate a currently-issued access token. The access token continues to validate until its
exp (~1 hour). For instant revocation on critical paths, consider maintaining a server-side allowlist that your backend checks.Storage requirements
| Token | Storage requirement |
|---|---|
access_token | In-memory or short-lived server-side cache. Never in browser localStorage or cookies. |
refresh_token | Encrypted at rest, with row-level encryption keyed per user. Never in browser cookies or localStorage. |
client_secret | Secret manager (HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager). Never in source code or environment variables committed to version control. |
What never to log
Never write these values to application logs, request logs, or error trackers:client_secretaccess_tokenrefresh_tokencode_verifier- Authorization
code id_token
client_id, jti, sub, scope list, request IDs, trace IDs.