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
Self-service OAuth client registration is not yet open (follow-up). Until it is, email [email protected] with:
- Your app name and a one-paragraph description
- Your redirect URI(s) — HTTPS only in production,
http://localhost:<port>is accepted for development - Which scopes you need (see the table below); we'll push back if the request is broader than your use case justifies
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
| Scope | What it grants |
|---|---|
read:sessions | Read session metadata + history. |
write:sessions | Start + stop sessions on the account. |
read:profiles | Read browser-profile metadata. |
write:profiles | Create + update browser profiles. |
admin:profiles | Delete + manage shared profiles. |
read:webhooks | List webhook endpoints + delivery history. |
write:webhooks | Create webhook endpoints + send test events. |
admin:webhooks | Rotate webhook secrets + delete endpoints. |
read:api-keys | List the account’s API keys (metadata only). |
admin:api-keys | Mint + revoke API keys. |
read:billing | Read invoices, usage, and crypto-order history. |
admin:billing | Initiate checkout sessions + manage subscriptions. |
read:audit | Read 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 /v1/oauth/authorize
GET /v1/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 a consent screen for your app + the requested
scopes. On approval they're redirected back to your
redirect_uri with ?code=…&state=….
Verify state matches what you sent (CSRF guard).
3. Exchange the code for an access token
POST /v1/oauth/token
Content-Type: application/json
{
"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_…" }
→ { "active": true, "client_id": "oac_…", "account_id": "acc_…",
"scope": ["read:sessions"], "exp": 1736600000 }
A revoked or expired token returns {"active": false}.
Use introspection sparingly — the access-token request itself
already carries the scope information.
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_…", "token_type_hint": "access_token" }
→ 200 OK
The endpoint always returns 200 — even for tokens that don't
exist — per RFC 7009 to prevent token-enumeration probes.
token_type_hint is optional and informational.
Security expectations
- Always validate
stateon the redirect back. Without it, your callback is wide open to CSRF. - Generate a fresh PKCE verifier per authorization flow. Re-using a verifier defeats its purpose.
- HTTPS-only redirect_uri in production. Driftstack rejects HTTP redirect URIs that aren't localhost.
- Store client_secret server-side. Browser apps cannot keep it secret; if your app is browser-based, talk to support about a public-client variant (no secret, PKCE-only).
- Tokens are bearer tokens. Anyone who reads the token can act as the customer. Treat them like passwords.
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.