Skip to main content

Auth & OAuth

Auth & OAuth

Every request must authenticate. Three paths — all converge to a single RequestContext.

Three Auth Paths → RequestContext

JWT
Authorization: Bearer <jwt>
Decode token
RS256 or HS256
Validate signature
PyJWT verify
Validate expiry
exp claim check
Extract claims
sub · tenant_id · exp
API Key
Authorization: ApiKey <key> or X-API-Key
Extract key
from header
Hash key
SHA-256 digest
Redis lookup
hashed key → record
Retrieve tenant/scopes
tenant_id · scopes
Token Passthrough
Access-Token: <raw_token>
Extract token
from Access-Token header
(no validation)
Octopus does not verify
Store for adapter
forwarded as Bearer token
User-delegated access
token used by adapter
Resolved By Middleware
RequestContext
tenant_id: str
user_email: str
bearer_token: str
Passed to every service instance

What & Why

Auth is the gateway. Every request must authenticate before the middleware stack continues. The three modes serve different use cases:

  • JWT — user-delegated flows where an identity provider issues a signed token. Octopus validates the signature, expiry, and extracts sub (user email) and tenant_id.
  • API Key — service-to-service calls. The key is hashed before Redis lookup; the stored record carries the tenant and allowed scopes.
  • Token Passthrough — the caller hands Octopus a raw OAuth access token (e.g. a user's Google token). Octopus does not validate it; it forwards the token directly to the adapter as a Bearer token for the external API.

All three paths produce the same RequestContext object, so service and adapter code never needs to know which auth mode was used.

OAuth 2.0

Authorization Code Flow

GET /v1/oauth/{service}/authorize → Provider → POST /v1/oauth/{service}/callback
1
Client→ Octopus
GET /v1/oauth/{service}/authorize?redirect_uri=…
Client initiates the flow
2
Octopus→ Client
Returns: 302 redirect URL to provider authorization endpoint
URL includes client_id, scope, state, redirect_uri
3
Browser→ Provider
User browser follows redirect → Provider OAuth page
Google / Azure AD / GitHub / etc.
4
User→ Provider
User reviews requested scopes and grants access
Provider validates user identity
5
Provider→ Octopus
POST /v1/oauth/{service}/callback?code=…&state=…
Authorization code + state param returned
6
Octopus→ Provider
Exchange authorization code for access_token + refresh_token
POST to provider token endpoint
7
Octopus→ Redis
Store tokens encrypted in Redis, keyed by tenant_id + user_email + service
access_token with TTL · refresh_token encrypted at rest
8
Octopus→ Client
200 OK — OAuth flow complete. Subsequent requests auto-use stored token.
No token management needed by client
Auto-Refresh Sub-Flow — Transparent to Callers
Incoming request
Adapter call triggered
Check TTL
access_token TTL < 5min?
POST token endpoint
grant_type=refresh_token
Update Redis + proceed
New token stored, call continues
Redis

Token Storage & TTL

Keys scoped to tenant + service + user. Refresh tokens encrypted at rest.
Access Token

oauth:{tenant_id}:{service}:{user_email}:access_token

TTL
token expiry (from provider)
Format
raw bearer string
Refresh Token

oauth:{tenant_id}:{service}:{user_email}:refresh_token

TTL
long-lived (days/weeks)
Format
AES-256 encrypted
Refresh Logic — Before Every Adapter Call
Read Redis TTL
GET oauth:…:access_token + TTL()
TTL < 300s?
yes → refresh · no → use as-is
Decrypt refresh token
AES-256 decrypt from Redis
New token stored + returned
SETEX access_token new_ttl value

Reference

Request Headers

HeaderAuth MethodFormatNotes
AuthorizationJWTBearer <jwt>RS256 or HS256 signed token
AuthorizationAPI KeyApiKey <key>Raw key; hashed before Redis lookup
X-API-KeyAPI Key<key>Alternative to Authorization ApiKey
Access-TokenToken Passthrough<raw_token>Not validated; forwarded to adapter

JWT Claims

ClaimTypeDescription
substringUser email address — maps to RequestContext.user_email
tenant_idstringTenant identifier — maps to RequestContext.tenant_id
expnumberUnix timestamp expiry — validated on every request
iatnumberIssued-at timestamp — informational
issstringIssuer — validated against configured allowed issuers

OAuth Endpoints

EndpointMethodDescription
/v1/oauth/{service}/authorizeGETInitiate OAuth flow. Returns redirect URL to provider. Query params: redirect_uri (required), scopes (optional).
/v1/oauth/{service}/callbackPOSTHandle provider callback. Query params: code, state. Exchanges code for tokens and stores in Redis.
/v1/oauth/{service}/refreshPOSTManually trigger a token refresh for a service. Useful for pre-warming tokens before a long job.

OIDC (Enterprise SSO)

For enterprise deployments, Octopus supports OpenID Connect for identity federation. The ID token from Google Workspace, Azure AD, or Okta is validated against the provider's JWKS endpoint. The extracted identity (sub, email) populates RequestContext identically to a JWT flow — downstream services see no difference.

Supported OAuth Services

ServiceAuth FlowScopes
Google (Gmail, Calendar, Drive)Authorization CodePer-product, e.g. https://www.googleapis.com/auth/gmail.readonly
Microsoft 365 (Outlook, Teams, SharePoint)Authorization CodeDelegated, e.g. Mail.Read, Calendars.ReadWrite
GitHubAuthorization Coderepo, read:org, read:user
NotionAuthorization Coderead_content, update_content
SlackAuthorization Codechannels:read, chat:write