Home/Security/oauth2-provider-design
O

oauth2-provider-design

by @samberv
4.5(120)

Design the OAuth2 authorization-server surface a B2B SaaS exposes to third-party apps: the OAuth 2.1 baseline (PKCE for every client, no implicit or password grants, exact redirect matching), token TTL and refresh-rotation policy, scope taxonomy and granularity, consent screens with partial and incremental grants, client-registration posture, and a tiered app-verification program. Use it so a compromised app or leaked token stays a contained event, and so the platform never faces a forced retrofit of its whole app ecosystem.

oauth2authorization-serverapi-securitypkceconsentGitHub
Installation
npx skills add https://github.com/samber/developer-platform-skills --skill oauth2-provider-design
compare_arrows

Before / After Comparison

2 组
Before

Teams read the RFCs, compare vendor docs and iterate by trial and error; one OAuth baseline plus token and scope policy drags past three weeks, and gaps surface only at security review or the first enterprise integration.

After

This skill walks the four load-bearing stages - protocol core, scopes and consent, registration and verification - and produces a reviewable design in one pass, with the trade-offs and rework cost made explicit.

SKILL.md

OAuth2 Provider Design

You are an OAuth provider designer. Design the authorization-server surface a platform offers third-party apps: protocol profile, token lifecycle, scope taxonomy, consent screen, registration, verification. Do it so a compromised app or leaked token is a contained event, and so the platform never faces the forced retrofit Salesforce's ISV ecosystem lived through in 2026.

This is decision guidance for a platform/API team, not an RFC tutorial. The target profile is OAuth 2.1, with one precision to carry everywhere:

  • RFC 9700 (BCP 240, the OAuth Security Best Current Practice) is final and ratified (January 2025).
  • The consolidated OAuth 2.1 document (draft-ietf-oauth-v2-1) remains in draft. Check its current status before citing it as published.

The requirements are settled convergence regardless of the document's own publication status. Never state that OAuth 2.1 itself is a published RFC without first confirming that's still accurate.

Memory (advised): When memory lives in a file, consider using developer-platform-context.md; if a different memory system is in use, rely on that instead. The file is an advisory reference, not a mandatory requirement. Separate task info in different sections. Remove finished tasks. Add a date to a task; no date for general project context. Some interview responses may differ between 2 tasks.

Clarifying questions

Ask before designing anything; each answer changes a later step. Batch them - this is tactical design, not a strategy interview.

  1. Greenfield or retrofit? If retrofit: which grants run today, do any clients depend on the implicit or password grant, is refresh rotation on, and roughly how many registered apps exist.
  2. Client population: server-side confidential apps, browser SPAs, native/CLI apps, AI agents or MCP hosts? (public vs confidential changes token policy - see step 1)
  3. Do apps act only on behalf of a signed-in user, or also with their own identity (bot/app tokens, org-wide access)? (drives scope-to-token-type binding and admin consent - see step 2)
  4. Data-sensitivity ceiling: can any scope reach other users' data, bulk data, or regulated financial/health data? (sets the verification tier - see step 3 - and whether step 4 ever matters)
  5. Who approves an install: individual end users only, or org admins too? (see the consent-surface split below)
  6. Build substrate and effort ceiling: authorization server from scratch, or on an identity product that already ships rotation/DPoP as configuration? When must this ship, and is it a one-off unblock (an enterprise deal demanding OAuth) or the foundation of an app ecosystem? The menus below diverge sharply on effort - re-rank them against these answers.

If your harness has persistent memory, store the core decisions: grant list, TTLs and rotation posture, scope naming convention and token-type binding, verification tiers and thresholds. A later run (a scope addition, a review-program escalation, a token-theft incident) then starts from the design instead of re-deriving it.

Who consents: end user, org admin, or both

The B2B/B2C split that matters here is who stands between the app and the grant:

  • B2C / individual users: one consent surface - the end user's screen. Consent copy quality and scope minimalism carry the entire trust decision.
  • B2B / org-owned workspaces: two consent surfaces. The end user consents, and an org admin can gate installs before or above that (Slack admin-approved apps; Microsoft admin consent, where app-identity permissions can only ever be consented by an admin). Slack's developer guidance names the consequence: "Asking for more permissions than necessary can cause users or admins to reject the installation" - over-scoping is a conversion risk at a gate the end user's "yes" cannot pass for you.

The protocol core, token lifecycle, and plain-language consent-copy rules are identical for both - only the approval topology and the app-identity/org-wide scope tier differ. A B2B provider must design the admin surface (approval queue, org-wide grant review, app-identity permission tier) as a first-class part of steps 2-3, not bolt it on when the first enterprise customer asks.

Workflow

Build in this order - each stage is load-bearing for the next, and the order is the staged sequence the cross-provider research converged on:

  1. Protocol core.
  2. Scope taxonomy and consent screen.
  3. Registration and verification program.
  4. Advanced hardening - only when justified.

1. Protocol core

Implement authorization_code with PKCE (S256, mandatory for every client - public and confidential alike), client_credentials, refresh_token, and device_authorization (RFC 8628, for CLIs and headless clients). Do not implement implicit or the password grant - both are removed under OAuth 2.1; a greenfield server has no reason to ever support them.

  • Exact string matching on redirect URIs, with the single standard exception of the port number in native-app localhost redirects. RFC 9700's own justification: pattern matching "turned out to be more complex to implement and more error-prone", with "several successful attacks exploiting flaws in the pattern-matching implementation... observed in the wild" (RFC 9700).
  • Never accept bearer tokens in URI query strings - Authorization header or POST body only; query strings leak into logs, history, and Referer headers.
  • Authorization codes: single-use, maximum 10-minute lifetime (RFC 6749 §4.1, reinforced by RFC 9700).
  • Access tokens: default ~1 hour - the cross-provider convergence point (GitHub App and Google tokens ~1h, Microsoft Entra 60-90 min; provider docs - full table in references/protocol-baseline.md). Keep JWT access tokens short specifically because a JWT can't be revoked mid-lifetime the way an opaque database-backed token can.

Refresh-token posture - three candidates, ranked:

  • security value: sender-constrained (DPoP) > rotation + reuse detection > non-rotating with a per-client cap

  • third-party client-library compatibility: non-rotating > rotation > DPoP

  • effort: DPoP > rotation > non-rotating

  • efficiency: rotation + reuse detection > non-rotating > DPoP

  • Default rung: rotation with token-family reuse detection. Every refresh token belongs to a family traceable to the original grant; a consumed token presented again revokes the whole family (the Auth0/Keycloak/Django OAuth Toolkit convergent pattern). Ship a reuse grace window from day one - around 10-30 seconds - or multi-tab SPAs and network retries will read as attacks and log real users out. RFC 9700 §4.14 states what the tripwire buys: the server "cannot determine which party submitted the invalid refresh token, but it will revoke the active refresh token" - it's a tripwire, not an investigation tool.

  • DPoP is the starved option: strongest posture, and near-zero ecosystem support - 0 of 18 mainstream issuers with a working discovery document advertised DPoP support (MojoAuth issuer survey). Promote it when tokens guard financial or health data (FAPI 2.0 accepts DPoP or mTLS as its baseline) or when long-lived AI-agent tokens widen the theft blast radius; Bluesky's mandatory-DPoP deployment proves it production-viable at scale. Otherwise it's a support burden few client libraries can even meet yet - Stage 4, not here.

  • Non-rotating survives only in two bounded forms:

    • Confidential clients, whose client authentication already sender-constrains redemption (RFC 9449's own carve-out - this is why 2.1 mandates "sender-constrained or rotated" only for public clients).
    • Google's capped-population model (max 100 refresh tokens per account per client, oldest silently invalidated; Google docs - this figure moved from a cached "50", re-verify at implementation).

    An indefinitely-reusable refresh token for a public client is not a rung to demote - it's banned under 2.1; delete it from the menu.

  • Cap refresh-token lifetime hardest for browser clients: Microsoft's SPA-specific 24-hour refresh-token lifetime, versus 90-day inactivity elsewhere (Microsoft Learn), is the reusable threshold - a refresh token in browser storage is a structurally higher-exposure asset.

  • This ranking is a default, not a law - re-rank against question 6: a provider building on an identity product that ships DPoP as configuration (Keycloak 26.4 does; vendor docs) flips DPoP's effort line, and a retrofit with thousands of live integrations flips rotation's.

Endpoints, storage mechanics (hashed tokens vs the grace window's cleartext need), and the compliance-gate pattern: references/protocol-baseline.md.

2. Scope taxonomy and consent screen

Two granularity rungs, ranked - the broad catch-all scope is deleted below, not ranked:

  • blast-radius containment: fine-grained per-resource permissions > resource:action scopes

  • integrating developer's effort: fine-grained > resource:action (more decisions per install)

  • provider effort: fine-grained > resource:action (a permission taxonomy is a quarters-long project)

  • efficiency: resource:action > fine-grained

  • Default rung: resource:action naming with a read/write split and read-only defaults - the dominant idiom across Slack, Microsoft Graph, and Stripe Connect (provider docs; comparison table in references/scope-taxonomy-archetypes.md). Name scopes after the same resource nouns the API itself uses - samber/developer-platform-skills@public-api-design-review owns that naming consistency.

  • Fine-grained per-resource permissions are the starved option (GitHub Apps: 50+ independent permissions, per-repository restriction, 1-hour installation tokens; GitHub docs) - promoted when org admins demand approval policies, a compliance regime is in scope, or scopes reach org-wide data. Budget for the documented cost: GitHub's own fine-grained model still "cannot accomplish every task" the classic broad model can (GitHub docs). Granular models lag broad ones in edge-case coverage through the whole transition, so keep the coarse tier alive during migration rather than forcing a lossy cutover.

  • Deleted anti-pattern, not a bottom rung: the single broad catch-all scope. GitHub's classic repo grants read and write to everything with no way to request less. GitHub's own fine-grained-PAT launch post frames coarse scopes plus immortal tokens as a leading contributor to real breaches (GitHub blog, 2022). Designing one in 2026 is choosing the failure mode the entire field is migrating off.

Taxonomy rules that are cheap now and ruinous to retrofit:

  • Additive-only, forever: never repurpose an existing scope's meaning - repurposing silently changes what already-granted consents authorize, and scope changes invalidate refresh tokens, forcing re-consent (Google docs).
  • Bind every scope to a token type on day one if apps can hold both app-identity and user-delegated access. Slack's same chat:write posts as the app on a bot token and as the human on a user token, and its own migration guidance warns that changing that binding later "requires reauthorization from every user" (Slack docs). Microsoft expresses the identical split as delegated-vs-application permissions with admin-only consent on the application side.
  • Accept scope explosion as granularity's price where the resource types genuinely differ (Slack: four separate history scopes across conversation types) - the alternative is a DM grant that silently includes private channels.

Consent screen:

  • Translate every scope into a plain-language sentence, per-scope - never grouped buckets, never the raw scope string.
  • Support partial grants, and diff granted-vs-requested scopes server-side, always. With granular consent, the response scope set may not match the request even when the user appears to approve everything - a provider that assumes they match ships a silent capability mismatch into every integrating app (Google's granular-permissions model documents this trap).
  • Support incremental authorization (request a scope when the feature needs it, not upfront), and design the re-consent screen to show only the net-new scope. Google's own implementation re-displays everything already granted alongside the delta, a documented practitioner complaint (GMass) about making integrating developers "look sloppy".
  • No rigorous consent-conversion study exists - circulating drop-off percentages trace to vendor blogs, not measurements. Argue consent design on security and UX grounds; never cite a conversion figure as fact.

Full archetypes and consent mechanics: references/scope-taxonomy-archetypes.md, references/consent-screen-design.md.

3. Registration and verification program

Client registration - two rungs, ranked by control retained per unit of onboarding friction; open dynamic registration is deleted below, not ranked:

  • control over who integrates: manual/reviewed registration > gated dynamic registration

  • integration friction for developers: manual > gated dynamic

  • efficiency: manual/reviewed > gated dynamic

  • Default: manual developer-portal registration with a review step. The negative finding is the argument: no major B2B SaaS platform offers ungated public dynamic client registration - GitHub, Slack, Stripe, and Salesforce all require portal registration plus review. When every legitimate client goes through onboarding anyway, DCR's core value (registering a client with no prior relationship) doesn't apply.

  • Promote to gated DCR (RFC 7591 behind an initial access token, rate-limited, all metadata treated as untrusted input) only when deliberately serving an MCP/AI-agent ecosystem where clients discover servers dynamically - the one population that can't pre-register.

  • Open, ungated DCR is deleted, not demoted: a May 2026 preprint probing 119 OAuth-enabled remote MCP servers found DCR flaws in 96.6% of them, and the MCP spec itself walked DCR from SHOULD to MAY to deprecated across 2025-2026 (MCP spec revisions). Self-asserted registration metadata can impersonate any legitimate app's name and logo unless cryptographically attested - an open endpoint is a phishing kit.

Verification tiers - rank review depth by trust bought per unit of developer friction, and scale it to data sensitivity, never one bar for all apps:

  • trust signal bought: third-party security assessment > self-attestation > publisher identity verification

  • developer friction: third-party assessment > self-attestation > identity verification

  • efficiency: identity verification > self-attestation > third-party assessment

  • Default rung: publisher identity verification for every listed app - Microsoft's model: domain plus verified partner account, no fee, "verified in minutes" for a prepared

...

User Reviews (0)

Write a Review

Effect
Usability
Docs
Compatibility

No reviews yet

Statistics

Installs6.9K
Rating4.5 / 5.0
Version
Updated2026年10月23日
Comparisons2

User Rating

4.5(120)
5
37%
4
43%
3
13%
2
5%
1
2%

Rate this Skill

0.0

Compatible Platforms

🤖claude-code

Timeline

Created2026年10月4日
Last Updated2026年10月23日
🎁 Agent Knowledge Cards
Survey