OAuth 2.1 Authorization Server
Truestamp's OAuth 2.1 authorization server with authorize, token, register, and revoke endpoints, RFC 8414/9728 discovery documents, PKCE S256, audience-bound tokens, rotating refresh tokens, and per-surface mcp/api/console scopes for authenticating a CLI or MCP agent.
Overview
Truestamp runs a standards-compliant OAuth 2.1
authorization server so a client
such as the truestamp CLI or an LLM agent connector (Claude Code, Claude.ai)
can obtain delegated, user-consented, scoped access to your account without you
handing over a password or a long-lived API key. A client runs the
authorization-code flow with PKCE: it sends you to a branded consent screen,
receives a short-lived authorization code at a redirect URI, and exchanges that
code for an access token plus a rotating refresh token. Access tokens are bound
to a single audience (the Truestamp origin) and carry per-surface scopes that
say what the token may do. The whole server is enabled by default and can be
turned off globally by an administrator with a single kill-switch. When it is
off, every OAuth route returns 404.
Endpoints and discovery
The authorization server exposes four protocol endpoints plus a set of
well-known discovery documents. All paths are relative to
https://www.truestamp.com.
| Method | Path | Purpose |
|---|---|---|
| GET/POST | /oauth/authorize |
user consent and authorization-code issuance (browser) |
| POST | /oauth/register |
dynamic client registration |
| POST | /oauth/token |
exchange an authorization code or a refresh token for tokens |
| POST | /oauth/revoke |
revoke a refresh token |
| GET | /.well-known/oauth-authorization-server |
authorization-server metadata (RFC 8414) |
| GET | /.well-known/oauth-protected-resource |
protected-resource metadata (RFC 9728) |
| GET | /.well-known/openid-configuration |
OIDC-tooling alias for the AS metadata |
A client never needs these paths hard-coded. It fetches the well-known
authorization-server document and reads the authorize, token, register,
and revoke URLs from it. The protocol endpoints under /oauth/* are
unauthenticated by design (the token endpoint’s proof is PKCE and the code, not
a client secret) and are per-IP rate limited. The consent step at
/oauth/authorize runs in the browser and requires you to be signed in. If you
have a second authentication factor (TOTP) configured, you must complete it in
your session before you can approve a grant.
Registering a client
Clients are public and PKCE-only: there is no client secret, so the per-flow
PKCE verifier is the only secret. There are two ways a client gets a
client_id.
- Dynamic Client Registration. An agent connector self-registers by
POSTing to/oauth/registerwith aclient_nameandredirect_urisbefore any user-facing flow begins. Registration is open (no initial access token), because MCP-style clients must register before they can send you to consent. Your protection lives downstream in the consent screen, PKCE, exact redirect-URI matching, and audience-bound tokens. - A pre-registered first-party client. The
truestampCLI ships a fixed, well-knownclient_idbaked into its binary with fixed loopback redirect URIs (http://127.0.0.1:8976/callbackandhttp://127.0.0.1:8765/callback), so it does not need to self-register.
A registered client records the redirect_uris, grant_types,
response_types, and requested scope it will use. The redirect URI is matched
exactly at both /oauth/authorize and /oauth/token; an unregistered
redirect_uri produces an error page rather than a redirect.
Authorization-code flow with PKCE
The flow a CLI or agent runs is authorization-code plus PKCE with a loopback redirect, which is the correct pattern for a public native client.
- Generate PKCE. The client creates a random verifier and its SHA-256
challenge, plus a
statevalue. Only the S256 challenge method is accepted; theplainmethod is rejected. - Start the flow. The client opens your system browser to
/oauth/authorizewithresponse_type=code, itsclient_id, theredirect_uri,code_challenge,code_challenge_method=S256, the requestedscope, andstate. A CLI first starts a small local HTTP listener on its loopback redirect port. - Consent. You sign in (and complete your second factor if needed) and approve the client name, scopes, and redirect target on the Truestamp consent screen. If you have already consented to this client at a scope that covers the request, the screen is skipped.
- Receive the code. The server redirects to the client’s
redirect_uriwith a short-lived, single-use authorization code and thestate. The client verifiesstate. - Exchange. The client
POSTs to/oauth/tokenwith the code and the PKCE verifier. The server checks the verifier against the stored challenge and, on success, returns an access token and a refresh token. - Use and refresh. The client sends the access token as
Authorization: Bearer <token>and uses the refresh token to mint a new access token when the old one expires.
Authorization codes are single-use: a code that is exchanged a second time is rejected, so a stolen-then-replayed code fails.
Tokens, lifetimes, and refresh
Both grant types are handled at /oauth/token: authorization_code for the
initial exchange and refresh_token for renewal.
- Access token: 24 hours. This is deliberately longer than the more common one hour. Some agent clients abandon a still-valid refresh token after a long idle and force a full re-authorization instead of refreshing, so a day-long access token lets an overnight agent session keep working on the access token alone.
- Refresh token: 30 days, rotating. Each refresh returns a fresh access token and a new refresh token; the old refresh token is retired. Refresh tokens are stored only as SHA-256 hashes. Reusing an already-rotated refresh token trips reuse detection and revokes the entire rotation chain, surfacing a compromise.
- Authorization code: 10 minutes. Short-lived and single-use.
The OAuth-correct renewal path is the refresh_token grant: when an access
token expires, the client silently exchanges its refresh token for a new access
token with no user interaction. Revocation of a refresh token (via
/oauth/revoke, or by deleting your consent) invalidates that chain, though an
already-minted access token remains valid until it expires because access tokens
are not individually revocable before expiry.
Scopes per surface
Capability is differentiated by scope, not by audience: every token shares one
origin-wide audience, and the scopes on it decide what surfaces it may reach.
The server advertises six scopes, a read and a write per surface. A client that
requests any scope outside this set is rejected at /oauth/authorize before you
see a consent screen.
| Scope | Grants |
|---|---|
mcp:read |
read access on the MCP server |
mcp:write |
writes composed in an MCP script (item and team creation) |
api:read |
JSON:API and GraphQL reads |
api:write |
JSON:API and GraphQL mutations |
console:read |
open the console WebSocket and read or stream |
console:write |
console write commands |
Request the narrowest scope your client needs. For a read-only integration over
the REST API, api:read is enough (the read-only
beacon API is a natural first authenticated call for
smoke-testing a new grant); a CLI that also writes requests
api:read api:write. Scope enforcement applies only to a delegated OAuth grant.
An API key is not scope-limited, so scope checks are a no-op for API-key callers.
The same origin-wide audience means an api:* token issued by this server works
against the REST API today.
Which surfaces accept an OAuth token
An OAuth access token authenticates all four Truestamp API surfaces. Three of them accept an OAuth bearer token or an API key interchangeably, and existing API keys keep working unchanged.
- MCP (
/mcp) is OAuth-only. It is an OAuth protected resource and does not accept API keys. A missing, invalid, or expired token returns401with aWWW-Authenticate: Bearer resource_metadata="..."header pointing at the protected-resource metadata document. That challenge is the discovery linchpin: an agent harness reads it, self-registers, and runs the PKCE flow automatically. See the MCP server for LLM agents concept for the tool catalog and the confirm-before-write flow. - JSON:API, GraphQL, and the console WebSocket each accept an
Authorization: Bearercredential that is either an OAuth access token or an API key. These surfaces already required authentication; OAuth is an additional accepted credential, not a new public path.
Activation and kill switch
The authorization server is enabled by default, and an administrator can disable
the entire surface globally on demand. While it is disabled, every /oauth/* route and every
/.well-known/oauth-* discovery document returns 404, so OAuth clients treat
the server as absent and stop attempting the flow. Because MCP depends on the
OAuth surface, disabling OAuth also makes /mcp unavailable; the other three
surfaces fall back to API-key-only authentication.
Security properties
- PKCE, S256 only. The
plainchallenge method is never accepted, and the verifier is checked at the token endpoint. - Audience binding. Every access token is bound to the Truestamp origin as its audience, so a token minted by another deployment (for example staging) is rejected here.
- Exact redirect-URI matching. No normalization, so an unregistered redirect URI cannot be used to leak a code (no open redirect).
- Refresh rotation with reuse detection. Single-use refresh tokens; reusing a rotated token revokes the whole chain.
- Second-factor gate on consent. If you have a second authentication factor (TOTP) configured, you cannot approve a grant until you have verified it in your session.
- Scoped, per-client consent. You see and approve the client name, the redirect target, and the exact scopes before any token is issued.
Citations
- RFC 6749: The OAuth 2.0 Authorization Framework. Base authorization-code grant and token endpoint semantics that OAuth 2.1 refines.
- RFC 7636: Proof Key for Code Exchange (PKCE). The S256 code-challenge mechanism used in place of a client secret.
- RFC 7591: OAuth 2.0 Dynamic Client Registration Protocol. The
/oauth/registerself-registration used by agent connectors. - RFC 8707: Resource Indicators for OAuth 2.0. Audience-bound access tokens.
- RFC 8414: OAuth 2.0 Authorization Server Metadata. The
/.well-known/oauth-authorization-serverdiscovery document. - RFC 9728: OAuth 2.0 Protected Resource Metadata. The
/.well-known/oauth-protected-resourcedocument and theWWW-Authenticatechallenge. - RFC 7009: OAuth 2.0 Token Revocation. The
/oauth/revokeendpoint. - RFC 8252: OAuth 2.0 for Native Apps. The loopback-redirect authorization-code plus PKCE pattern used by a CLI.