Skip to content

Latest commit

 

History

History
141 lines (111 loc) · 6.74 KB

File metadata and controls

141 lines (111 loc) · 6.74 KB

Environment Authentication Profile

For maintainers. Using T3 Code? See docs/user.

The environment server and the relay use separate credentials, issuers, and trust boundaries. They intentionally use a similar OAuth-shaped model so that permission checks and token exchange behavior can be audited against established concepts.

Authorization Model

Environment authorization is capability-based. A session carries zero or more OAuth-style scope strings:

Scope Permission
orchestration:read Read snapshots, status, events, configuration, and filesystem/VCS state.
orchestration:operate Dispatch user operations and mutate environment-side workspace state.
terminal:operate Create, attach, input, resize, clear, restart, and terminate terminals.
review:write Read review diff previews used to compose review feedback.
access:read Inspect pairing links and client sessions.
access:write Create or revoke pairing links and client sessions.
relay:read Inspect managed relay connectivity.
relay:write Link, configure, or unlink managed relay connectivity.

Ordinary pairing links grant the four client-operation scopes and read access to managed relay connectivity: orchestration:read orchestration:operate terminal:operate review:write relay:read. The desktop bootstrap credential and command-line administrative bootstrap credentials additionally grant access:read access:write relay:write.

Authentication Flows

Browser Session

POST /api/auth/browser-session consumes a one-time bootstrap credential and creates a browser session cookie. The cookie is an HTTP transport adapter for the same scoped session model; the response never exposes the session secret to browser JavaScript.

Bearer Access Token

Non-browser clients use POST /oauth/token with an application/x-www-form-urlencoded body:

grant_type=urn:ietf:params:oauth:grant-type:token-exchange
subject_token=<bootstrap credential>
subject_token_type=urn:t3:params:oauth:token-type:environment-bootstrap
requested_token_type=urn:ietf:params:oauth:token-type:access_token
scope=orchestration:read orchestration:operate terminal:operate review:write relay:read

Clients may additionally submit client_label, client_device_type, and client_os extension parameters so the authorized-clients UI can identify the device that established the session. These are presentation hints only; the environment derives transport metadata such as IP address and user agent from the request and does not use these fields for authorization.

The response has the token-exchange shape:

{
  "access_token": "<opaque session token>",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "token_type": "Bearer",
  "expires_in": 2592000,
  "scope": "orchestration:read orchestration:operate terminal:operate review:write relay:read"
}

Sessions issued from a plain bearer exchange use the store's DEFAULT_SESSION_TTL of 30 days. The shorter one-hour expires_in: 3600 applies only to DPoP-bound exchanges, where the token is additionally constrained by a proof key. See SessionStore.ts and EnvironmentAuth.ts.

Requested scopes must be a subset of the one-time bootstrap credential grant. An ordinary paired client therefore cannot exchange its grant for access:read, access:write, or relay:write.

DPoP-Bound Access Token

The same /oauth/token exchange supports proof-of-possession tokens. A client that sends a DPoP header has its proof verified by verifyRequestDpopProof; the resulting JWK thumbprint is stored on the session, which is then issued with method dpop-access-token and a one-hour TTL instead of the bearer default. An invalid proof gets a DPoP challenge header and a credential error rather than a bearer token.

dpop-access-token is advertised alongside browser-session-cookie and bearer-access-token in the descriptor's sessionMethods (EnvironmentAuthPolicy.ts), so clients can discover support rather than assume it. Relay-brokered clients use this mode so that a leaked token cannot be replayed without the corresponding key.

WebSocket Ticket

POST /api/auth/websocket-ticket accepts any authenticated session and returns a short-lived, single-purpose WebSocket ticket, issued through EnvironmentAuth.issueWebSocketTicket with a five-minute default TTL. The client presents its bearer or DPoP credential in headers to get the ticket, then appends only that ticket to the socket URL as wsTicket. This keeps long-lived tokens and browser cookies out of WebSocket URLs while letting the handshake authenticate.

The ticket carries its session's scopes; each RPC method then enforces orchestration:read, orchestration:operate, terminal:operate, review:write, relay:write, or access:read as appropriate, through RPC_REQUIRED_SCOPES in apps/server/src/auth/RpcAuthorization.ts. Review feedback submission currently dispatches an orchestration operation, so clients performing it also need orchestration:operate. Creating a ticket is not authorization to call every RPC method.

Standards Alignment

  • Bearer access tokens are used through the Authorization: Bearer scheme from RFC 6750.
  • The token endpoint profiles the request and response vocabulary from OAuth 2.0 Token Exchange (RFC 8693), including subject_token, requested_token_type, access_token, issued_token_type, and token_type.
  • Scope values follow the OAuth 2.0 scope model from RFC 6749: space-delimited, unordered capabilities with subset checking during exchange.

This is intentionally not a general-purpose OAuth authorization server. The environment bootstrap token type is private, the bootstrap cookie and WebSocket connection-token routes are product-specific adapters, and the API returns its typed HttpApi errors rather than implementing every OAuth error response surface.

Upgrade Behavior

Migration 031_AuthAuthorizationScopes is a hard cutover from role-bearing auth records to scoped records. It deletes existing pairing links and sessions while leaving non-authentication environment state unchanged. Upgraded clients must pair again; old owner or client credentials are never silently mapped to new capabilities.

Relay Boundary

Relay-managed tunnels use their own tokens and keys. The relay can reuse scope parsing and token-exchange conventions, but an environment access token is not a relay token and cannot be presented to the relay.