Knowledge Base

Browse the concepts behind Truestamp. Follow the links between concepts, or search across everything.

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 rate limited per network address. Over the limit they answer 429 Too Many Requests with a Retry-After header in whole seconds. The JSON body’s error is one of the codes the OAuth specifications permit at each endpoint, slow_down at the token endpoint and invalid_request at registration and revocation, so treat the status and Retry-After as the signal and back off rather than branching on the code. 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/register with a client_name and redirect_uris before 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, byte-exact redirect-URI matching, and audience-bound tokens.
  • A pre-registered first-party client. The truestamp CLI ships a fixed, well-known client_id baked into its binary with fixed loopback redirect URIs (http://127.0.0.1:8976/callback and http://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. At /oauth/authorize the presented redirect_uri must match a registered one byte for byte, with a single exception: for a loopback IP literal (127.0.0.1 or [::1]) the port may differ, because a native client binds a port at authorization time. Scheme, host, path, and query still have to match exactly, and the name localhost does not qualify for the exception. At /oauth/token the redirect_uri must equal the one bound to the code byte for byte. A redirect_uri that does not match 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.

  1. Generate PKCE. The client creates a random verifier and its SHA-256 challenge, plus a state value. Only the S256 challenge method is accepted; the plain method is rejected.
  2. Start the flow. The client opens your system browser to /oauth/authorize with response_type=code, its client_id, the redirect_uri, code_challenge, code_challenge_method=S256, the requested scope, and state. A CLI first starts a small local HTTP listener on its loopback redirect port.
  3. 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.
  4. Receive the code. The server redirects to the client’s redirect_uri with a short-lived, single-use authorization code and the state. The client verifies state.
  5. Exchange. The client POSTs to /oauth/token with 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.
  6. 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. Revoking a refresh token at /oauth/revoke invalidates its whole rotation chain, and deleting your account removes every outstanding authorization code, refresh token, and consent record. An already-minted access token stays valid until it expires: access tokens are self-contained and 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 submission, team creation, webhook endpoint management)
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). The truestamp CLI registers for api:read api:write console:read console:write, because it both writes over the REST API and opens the console WebSocket.

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.

Which surfaces accept an OAuth token

An OAuth access token authenticates all four Truestamp API surfaces. Three of them also accept an API key, and existing API keys keep working unchanged.

  • The MCP protocol surface is OAuth-only. The wire protocol lives on POST and DELETE at /mcp, and both require an OAuth access token: /mcp is an OAuth protected resource and does not accept API keys. A missing, invalid, or expired token returns 401 with a WWW-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.
  • A browser GET on /mcp is a public page, not the protocol. The same path serves a public information page about the MCP server to anyone, signed in or not, so no credential is involved. A client probing for the optional server-to-client event stream (a GET or HEAD carrying Accept: text/event-stream, Mcp-Session-Id, or Mcp-Protocol-Version) gets 405 with Allow: POST, DELETE instead of the page, for any caller, before authentication is considered.
  • JSON:API and GraphQL each accept an Authorization: Bearer credential 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.
  • The console WebSocket accepts either credential as well, but a browser cannot set request headers on a WebSocket upgrade, so the credential normally rides on the socket URL: ?api_key=... for an API key, ?access_token=... for an OAuth access token. An OAuth connection must carry console:read just to open the socket. See the console WebSocket concept for the wire details.

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 discovery document returns 404, including the OpenID-configuration alias, so OAuth clients treat the server as absent and stop attempting the flow. Because the MCP protocol surface is OAuth-only, disabling OAuth takes it down with it: POST and DELETE on /mcp return 404. The public information page served on GET /mcp is unaffected, and the other three surfaces fall back to API-key-only authentication.

Security properties

  • PKCE, S256 only. The plain challenge 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.
  • Byte-exact redirect-URI matching. No normalization, no default-port elision, no trailing-slash equivalence, so a redirect URI the client did not register cannot be used to leak a code (no open redirect). The one exception is the port of a loopback IP literal, which native clients need.
  • 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. A stored consent only skips the screen when it already covers every requested scope, so a client cannot silently widen what it was granted on a later authorization.

Citations

  1. RFC 6749: The OAuth 2.0 Authorization Framework. Base authorization-code grant and token endpoint semantics that OAuth 2.1 refines.
  2. RFC 7636: Proof Key for Code Exchange (PKCE). The S256 code-challenge mechanism used in place of a client secret.
  3. RFC 7591: OAuth 2.0 Dynamic Client Registration Protocol. The /oauth/register self-registration used by agent connectors.
  4. RFC 8707: Resource Indicators for OAuth 2.0. Audience-bound access tokens.
  5. RFC 8414: OAuth 2.0 Authorization Server Metadata. The /.well-known/oauth-authorization-server discovery document.
  6. RFC 9728: OAuth 2.0 Protected Resource Metadata. The /.well-known/oauth-protected-resource document and the WWW-Authenticate challenge.
  7. RFC 7009: OAuth 2.0 Token Revocation. The /oauth/revoke endpoint.
  8. RFC 8252: OAuth 2.0 for Native Apps. The loopback-redirect authorization-code plus PKCE pattern used by a CLI.