Skip to main content
DRIFTSTACK

OAuth 2.0 integration

Driftstack uses OAuth 2.0 with PKCE-S256 for third-party app access. This page documents the authorization code flow your app uses to act on a Driftstack customer's behalf.

Getting a client_id

OAuth client registration is administered by Driftstack. Email [email protected] with:

We turn around new client registrations within one business day. You get back a client_id (prefixed oac_) and a client_secret (prefixed oas_). The secret is shown to you ONCE — we only store the hash on our side, so we cannot recover it for you. Treat it like a Stripe secret key.

Available scopes

ScopeWhat it grants
read:sessionsRead session metadata + history.
write:sessionsStart + stop sessions on the account.
read:profilesRead browser-profile metadata.
write:profilesCreate + update browser profiles.
admin:profilesDelete + manage shared profiles.
read:webhooksList webhook endpoints + delivery history.
write:webhooksCreate webhook endpoints + send test events.
admin:webhooksRotate webhook secrets + delete endpoints.
read:api-keysList the account’s API keys (metadata only).
admin:api-keysMint + revoke API keys.
read:billingRead invoices, usage, and crypto-order history.
admin:billingInitiate checkout sessions + manage subscriptions.
read:auditRead the account audit log.

Scopes follow the verb:resource pattern. Always request the narrowest scope set your app actually needs — broad scopes get pushed back during the human review step.

Authorization flow

1. Mint a PKCE verifier

Generate a high-entropy random string (RFC 7636 §4.1, 43–128 characters from [A-Za-z0-9-._~]). Compute the code_challenge as the URL-safe base64 of SHA-256(verifier). Keep the verifier in your app until step 3 — never send it to anyone except Driftstack at the token endpoint.

2. Redirect the customer to Driftstack

GET https://app.driftstack.dev/oauth/authorize/
  ?client_id=oac_…
  &redirect_uri=https://yourapp.com/oauth/callback
  &state=<csrf-token>
  &code_challenge=<sha256(verifier) base64url>
  &code_challenge_method=S256
  &scope=read:sessions write:sessions

The customer signs into Driftstack if they aren't already, then sees the app label, requested scopes, and callback host before choosing Approve or Cancel. Approval returns them to your redirect_uri with ?code=…&state=…; cancellation returns ?error=access_denied&state=…. Verify state matches what you sent (CSRF guard) in either case.

The Dashboard validates and stages this request through GET /v1/oauth/authorize, then performs approved consent through POST /v1/oauth/authorize/complete. Those are provider-internal steps: your integration never receives or handles the intermediate authorization_id.

3. Exchange the code for an access token

POST /v1/oauth/token
Content-Type: application/json

{
  "grant_type": "authorization_code",
  "code": "...",
  "code_verifier": "<your pkce verifier>",
  "client_id": "oac_…",
  "client_secret": "oas_…",
  "redirect_uri": "https://yourapp.com/oauth/callback"
}

The response carries the token:

{
  "access_token": "oat_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": ["read:sessions", "write:sessions"]
}

Tokens live for one hour. There are no refresh tokens — when the token expires, run the full authorize → exchange dance again. This is intentional: it makes consent re-confirmation a regular event rather than a forever-grant.

4. Use the token

Bearer-token authentication against the Driftstack API:

Authorization: Bearer oat_…

The token can only call API surfaces whose required scope is a subset of the token's scope. A token with read:sessions can call GET /v1/sessions but not POST /v1/sessions.

Introspection (RFC 7662)

Check whether a token is still active + what scopes it carries:

POST /v1/oauth/introspect
Content-Type: application/json

{
  "token": "oat_…",
  "client_id": "oac_…",
  "client_secret": "oas_…"
}

→ { "active": true, "client_id": "oac_…", "account_id": "acc_…",
    "scope": ["read:sessions"], "exp": 1736600000 }

After your client credentials are authenticated, a revoked, expired, unknown, or foreign-client token returns {"active": false}. Invalid or revoked client credentials return 401 before token lookup. Use introspection sparingly and only from server-side code — the request contains your client_secret.

Revocation (RFC 7009)

Invalidate a token before its 1-hour expiry — useful when the customer signs out of your app:

POST /v1/oauth/revoke
Content-Type: application/json

{
  "token": "oat_…",
  "client_id": "oac_…",
  "client_secret": "oas_…",
  "token_type_hint": "access_token"
}

→ 200 OK

After client authentication succeeds, the endpoint returns 200 for an owned, unknown, or foreign-client token, but only a token issued to your client_id is revoked. This preserves RFC 7009 anti-enumeration behavior without allowing cross-client revocation. Invalid or revoked client credentials return 401. token_type_hint is optional and informational.

Security expectations

Rate limits

Token-issued requests count against the customer's account rate limits, not your app's. If you're going to mint many tokens for many customers, be aware that high per-customer QPS will hit the tier limits.

Sandbox

We don't yet operate a separate sandbox environment. Test against your own dev account on the production API; use the read:*-only scopes if you don't want your dev account's session usage to count against billing.

Support

Questions, bug reports, or scope requests: [email protected]. We aim to respond within one business day.