How Simple OAuth authenticates decoupled Drupal clients
Simple OAuth is the Drupal module that lets a front end or app obtain a token and call Drupal's JSON:API with access limited to what that client is allowed to do. Teams need it when a Next.js site, mobile app or other external client must read protected content, submit data or act for a logged-in user, and the anonymous access that works for public content is no longer enough. Without a token layer, the only options are to open protected resources to everyone or to share a single Drupal account across all clients, and neither holds up once more than one consumer exists. The model has three roles. The *resource owner* is the Drupal user whose data is at stake, the *client* is the front end or app asking for access, and the *authorization server* is Drupal itself, which issues the access token after the user or the client proves its identity. The client then presents that token on each JSON:API request, and Drupal checks it before returning data. OpenID Connect adds a standard way to establish who the user is, through an identity token alongside the access token. Where a separate identity provider already holds your users, the contributed *OpenID Connect* module can handle sign-in, but that is a different job from issuing API tokens, so confirm early which system is the source of identity.
The decisions that matter sit in the setup. Choose the grant type for each client: a server-side Next.js route can use a confidential client with a secret, while a single-page app or mobile app is a public client and cannot keep a secret safely, so it needs a flow designed for that case. Define scopes narrowly, mapped to the resources and operations each client needs, and decide how long access tokens and refresh tokens live, since shorter access token lifetimes limit damage from a leaked token but add refresh traffic. Confirm the exact grant types, token settings and key handling for the Simple OAuth release you plan to use in your own environment, because those details change between releases and are not covered by the facts available here. Plan the upgrade path before go-live as well, since the 5.x to 6.x transition of Simple OAuth requires a deliberate review of its configuration and dependencies.
Which grant type fits each client type
A server-side web application runs on infrastructure you control, so it can keep a client secret out of browser code and hold it in server configuration or a secrets store. That makes it a confidential client. The usual pattern is the authorization code grant, where the application redirects a user to Drupal, receives a code, and exchanges it on the server for tokens. The secret is sent only from server to server, and the access token never has to reach the browser. The main risk is secret sprawl: the secret gets copied into version control, container images or CI logs, and nobody notices until it leaks. Points to plan for:
- Secret storage: keep the client secret in a secrets manager or environment-injected configuration, and rotate it on a documented schedule.
- Redirect URIs: register exact callback URLs for each environment, with no wildcards.
- Session handling: store tokens server-side and issue the application its own session cookie with secure attributes.
- Scope choice: request only the scopes the application needs for each user role.
- Token refresh: confirm how refresh tokens are issued and revoked for confidential clients in your installed version.
Confirm the exact grant names, secret handling options and redirect validation rules in the Simple OAuth documentation for the version you run before writing the integration.
Code that runs in a browser cannot hold a secret, because anyone can read the bundled JavaScript and network traffic. Public clients therefore rely on the authorization code grant with Proof Key for Code Exchange (PKCE), where the browser creates a one-time verifier and sends its hash with the authorization request. This lets the token endpoint confirm that the code was issued to the same client that asked for it. Next.js applications often split the work: pages that need private data can call a server route that performs the token exchange and keeps tokens out of client-side storage. The main risks are tokens persisted in local storage, where any script injected through a cross-site scripting flaw can read them, and overly broad scopes that make a stolen token more valuable. Points to plan for:
- PKCE: confirm the installed version enforces PKCE for public clients and that your client registration is set to match.
- Token storage: prefer server-side sessions or short-lived in-memory tokens over local storage.
- Backend-for-frontend: route privileged calls through a server component that holds the session.
- Content security policy: restrict script sources to reduce the chance of token theft.
- Logout: define how tokens are revoked when a user signs out.
Confirm PKCE support, public client settings and token lifetimes in the Simple OAuth documentation for your release.
Native mobile apps are public clients because the app package can be inspected and modified on a user's device. They should use the authorization code grant with PKCE and open the system browser or a platform authentication session for the login step, rather than embedding a login form inside the app. The app receives a redirect to a custom scheme or app link, exchanges the code for tokens, and stores the refresh token in the operating system's secure storage. Mobile clients also face device loss and long-lived sessions, so token lifetime and revocation matter more than they do for a web app. Points to plan for:
- System browser login: avoid embedded web views for credential entry.
- Redirect handling: register the app's custom scheme or verified app link exactly as the app uses it.
- Secure storage: keep refresh tokens in the platform keychain or keystore, never in plain preferences files.
- Revocation: provide a way for users to sign out on all devices and for support staff to revoke sessions.
- Offline use: decide how long cached content stays valid after the token expires.
Confirm the supported redirect patterns, refresh token behavior and default lifetimes in the Simple OAuth documentation for your installed version.
Services that call Drupal without a human user, such as a scheduled sync job, an ingestion pipeline or a backend that publishes content, use the client credentials grant. The service authenticates with its own client ID and secret and receives an access token tied to a Drupal user account that you define for that purpose. Because no person signs in, the important design work is in the account: what it can read and write, and who is accountable for its actions. This grant is confidential by nature, so the secret must stay on the service side and should be protected like any database credential. Points to plan for:
- Dedicated user: create a Drupal account with the minimum roles and permissions the job needs, and do not reuse an editor's account.
- Secret lifecycle: store the secret in a secrets manager, rotate it, and plan for revocation when a job is retired.
- Audit trail: make sure write operations can be traced to the service account in logs.
- Token caching: cache tokens until shortly before expiry and request new ones instead of storing long-lived credentials.
- Network scope: restrict which networks can reach the token endpoint where your architecture allows it.
Confirm which grants the installed version enables by default and how service accounts are linked to clients in the Simple OAuth documentation.
Configuration options and when each one fits
This table compares the configuration decisions that shape a Simple OAuth setup for decoupled Drupal, with the trade-off each one carries and the situations where it fits. Read each row as a decision to settle with your front-end and security teams before the build starts, since most of them are hard to change once consumers are live.
| Decision | What it controls | Trade-off | When it fits |
|---|---|---|---|
| Grant types enabled | How each client obtains a token: authorization code with PKCE for browser and mobile apps, client credentials for server-to-server calls | Every enabled grant is an extra path to review; leaving unused grants on widens the attack surface | Enable only the grants a registered client type needs, and confirm the exact grant support against the Simple OAuth documentation for your version |
| Scope definitions | Which JSON:API resources and operations a token may reach, mapped to Drupal roles and entity access | Fine scopes limit damage but add maintenance as content types change; coarse scopes are simpler but grant more than needed | Use narrow scopes for public or partner clients and map each scope to a named permission set that editors can read |
| Access token lifetime | How long a stolen or leaked access token stays valid | Shorter lifetimes reduce exposure but increase refresh traffic and token requests from clients | Shorter lifetimes fit sensitive content or user data; confirm the default and maximum values in your installed version before setting them |
| Refresh token use | Whether clients can obtain new access tokens without a new sign-in | Refresh tokens keep users signed in smoothly but must be stored and revoked carefully on logout | Use refresh tokens for logged-in mobile or single-page sessions; avoid them for anonymous or machine clients that can re-authenticate |
| Key pair management | The private and public keys used to sign tokens and where they are stored | Rotating keys is safer but requires a planned cutover; keys in the web root or shared repositories are a common failure | Store keys outside the document root with restricted file permissions, and plan rotation with your secrets process |
| Client registration by developers | Which client IDs and secrets exist, set through configuration managed in code review | Slower to change, but every client is versioned, reviewed and traceable | Suits production front ends and partner integrations where an audit trail of each client matters |
| Client registration by editors | Clients created through the Drupal admin interface by site staff | Faster for internal tools, but changes bypass code review and can drift between environments | Suits low-risk internal prototypes only, with an access review of who can create clients and secrets |
Setup sequence and checks for Simple OAuth
This sequence takes a Drupal site from a bare Simple OAuth install to a state where each decoupled client holds only the tokens and scopes it needs. Each step closes with a check, so a fault is caught at the step that introduced it rather than during launch testing.
Install Simple OAuth and its dependencies
The Drupal developer installs the Simple OAuth module with Composer, enables it along with the JSON:API module it depends on, and records the exact versions in the project lock file. Confirm that the module's compatibility matches your Drupal core version, since the 5.x to 6.x upgrade caution applies to later version changes. The step is done when the module appears as enabled on the extensions page and the OAuth token endpoint responds on the site instead of returning a not-found error.
Generate signing keys outside the web root
The platform engineer generates the private and public key pair that signs access tokens and stores them in a directory the web server cannot serve, with the private key readable only by the PHP runtime user. Keep the key path in environment configuration, not in version control. The check is that a direct request to the key file path returns a denial or not-found response, and that the module reports the keys as valid on its settings page.
Define scopes for each resource group
The architect lists the content and user resources the front end needs, then groups them into scopes such as read-only article access and authenticated user profile access. Write each scope name and its purpose in the project's security design record before configuring anything in Drupal. The check is a reviewed table where every JSON:API resource the front end calls maps to at least one scope, and no resource is mapped to a broad scope by default.
Register each client with the right grant type
For each consumer, the developer creates a consumer entry in Drupal and selects a grant type that matches the client: client credentials for server-side services, and authorization code with PKCE for browser or mobile front ends that act for a signed-in user. Assign only the scopes from the previous step. The check is that each consumer appears with its intended grant type and scope list, and that a test token request using a grant the client should not have is refused.
Set token lifetimes per client type
The security lead sets access token and refresh token lifetimes in the module settings, with shorter access lifetimes for browser clients and a documented revocation path for lost devices. Ask the module maintainers and your security team to confirm the defaults against your policy, because lifetimes are a configuration decision, not a fixed value. The check is a token issued with the configured lifetime, confirmed by reading the expiry in the response, and a token that is still accepted only within that window.
Restrict JSON:API resources by scope
The developer applies access control so that each JSON:API resource checks for the matching scope on the incoming token, and anonymous or unscoped requests receive the same denial as an unknown resource. Keep the public content endpoints separate from the private ones so that an accidental change does not expose user data. The check is a request to a protected resource without the scope returning an access-denied response, and the same request with the scope returning the expected data.
Test denied and allowed requests end to end
The QA engineer runs a scripted test set for every client, covering allowed reads, out-of-scope reads, expired tokens, revoked tokens and tokens issued to another client. Confirm that a token for a read-only client is rejected when it calls a write endpoint, and that a revoked refresh token cannot obtain a new access token. The step is done when every case produces the expected result and the test set is stored with the project so it runs again after each module or core update.
Pairing Simple OAuth with OpenID Connect sign-in
Simple OAuth and the OpenID Connect module solve different problems, and many decoupled projects need both. Simple OAuth issues access tokens to the API clients that read and write content, such as a Next.js front end or a mobile app. The OpenID Connect module, by contrast, lets an external identity provider authenticate people who sign in to Drupal, and it creates a Drupal account on first sign-in. Its built-in clients cover generic OAuth 2.0, GitHub, Google, LinkedIn, Facebook and Okta. The decision to pair them comes down to whether editors or site visitors need to sign in through your corporate identity provider, and whether that identity should then drive what they can do in Drupal's API.
Teams should map each user population to one path before the build starts. Editors authenticating into the Drupal administration interface and API consumers acting on behalf of a signed-in user usually need the OpenID Connect module, with roles assigned from claims your provider supplies. Machine clients such as a server-side rendering process should use Simple OAuth alone. Confirm that your identity provider can issue the claims you need for role mapping, and confirm which of the built-in clients fits it. If your provider is not on the list, the generic OAuth 2.0 client or a separate integration will need its own estimate.
SAML is outside this pairing. Where your identity provider only speaks SAML, the samlauth module (8.x-3.14, which supports Drupal 10.3 and later and Drupal 11) lets Drupal act as a SAML service provider, and it does not replace Simple OAuth for API tokens. Treat sign-in federation, token issuance and role mapping as a separate piece of work with its own test plan, and confirm the module version against your Drupal core version before you commit to a plan.
Security considerations for token-based API access
Token-based access to a decoupled Drupal site concentrates risk in a few places: the client secret, the token itself, the scopes it carries and the endpoints that issue it. Each card below names one risk, the control that addresses it and the team that should own that control.
Client secrets in browser code
A confidential client secret placed in JavaScript bundles is visible to anyone who opens the browser developer tools, so it is effectively public. For Simple OAuth, use the public client pattern with PKCE for single-page and mobile apps, and keep client secrets only in server-side code such as a Next.js route handler. Owner: front-end lead. Check that no secret appears in built assets or source maps.
Token leakage through logs and referrers
Access tokens sent in query strings can end up in web server access logs, proxy logs, analytics tools and the Referer header on outbound links. Require tokens in the Authorization header, configure log formats to exclude it, and set a referrer policy on pages that receive redirects from the token flow. Owner: platform engineering. Check a sample of production logs for token-shaped strings.
Over-broad scopes
Simple OAuth issues tokens against scopes you define, and a client that receives more scopes than it needs can read or write content it should never touch. Map each client to the smallest set of scopes its screens require, and review the scope list whenever a new front end is added. Owner: Drupal site architect. Check each client's granted scopes against its feature list.
Long-lived refresh tokens
A refresh token lets a client mint new access tokens without a user signing in again, so a stolen refresh token gives long access. Set access token lifetimes short, decide per client whether refresh tokens are issued at all, and make revocation part of logout. Owner: security team with the Drupal administrator. Confirm the lifetime values in your own configuration and test revocation before launch.
Signing key rotation
Tokens are signed with a private key that Drupal uses to issue them, and the public key is shared with resource consumers that verify them. Plan a rotation schedule, a way to publish the old and new keys during the transition, and a rehearsal in a staging environment. Owner: infrastructure team with the security lead. Confirm how your Simple OAuth version handles key files and file permissions.
CORS and HTTPS on token endpoints
The token and authorization endpoints must be served only over HTTPS, and cross-origin requests should be allowed only from the exact origins of your front ends. A wildcard origin on an endpoint that handles credentials undermines the browser's protection. Owner: web platform team. Check response headers from the token endpoint and confirm the allowed origin list matches your deployment domains.
The 5.x to 6.x upgrade caution for Simple OAuth
Treat a major Simple OAuth upgrade, such as moving from 5.x to 6.x, as a planned change to the authentication layer rather than a routine module update. Every decoupled front end, mobile app and server-to-server integration depends on the tokens that Simple OAuth issues, the keys it signs them with and the scope names it maps to Drupal permissions. A major version can change any of these, so the test plan should cover token issuance for each grant your clients use, existing clients registered in the consumer configuration, the scopes each client requests, and the public and private key files used to sign tokens. Confirm the exact changes in the 6.x changelog and upgrade notes before you estimate the work, since this section does not describe 6.x release contents.
Plan the test against the core version you intend to run next, not only the one in production today. Drupal 10 reaches end of life on December 9, 2026, according to the Drupal core release schedule, and Drupal 12 requires PHP 8.5 or later, according to the Drupal.org PHP requirements page. A Simple OAuth upgrade that works on your current core may still need adjustment once core moves forward, so run the client-facing auth flows against the target core and PHP versions in a staging environment that mirrors production. Check that the Drupal core release cadence, with bugfix releases on the first Wednesday of each month and security releases on the third Wednesday, fits your change calendar so that a core update and an auth module upgrade do not land in the same release window without a test.
Ask your implementation partner to write the test cases before the upgrade begins. A useful set covers a login and token refresh from each front end, a rejected request with an expired or revoked token, a request with a scope the client should not have, and a key rotation rehearsal that confirms the new key files are read by the running site. Record which steps are automated and which need a person to sign in, because that split determines how much effort a repeat test takes at each future release. Also confirm the rollback path: keep the previous key files and module versions available until the client test suite passes in production.
Limits to confirm in your environment
Simple OAuth sits between your Drupal core, your front-end framework and your gateway, so a limit in any one of them changes the token design. The cards below list the checks to complete against the versions you plan to run, before the design is signed off.
Supported grants in your release
Confirm which OAuth 2.0 grant types the installed Simple OAuth version supports, and whether each one is enabled for the client types you plan to register. Server-side clients, single-page apps and mobile apps need different grants, and a grant missing from your release forces a redesign rather than a configuration change. Ask the maintainers for the grant list in writing and test each flow against a staging site.
Core and module compatibility
Check that the Simple OAuth release you select declares support for your target Drupal core version and its PHP requirement, since both move on the Drupal release schedule. Confirm this for the version you plan to run at launch and for the next major version, so the token design survives a planned upgrade. Record the result in the module compatibility matrix for the project.
Bearer token handling in the front end
Test how your chosen framework stores, refreshes and sends bearer tokens, especially during server-side rendering and preview requests. Browser storage, cookie-based sessions and server-side proxies each expose tokens differently to cross-site scripting and leakage in logs. Confirm the refresh flow works when a token expires in the middle of an editor preview session.
Rate limiting at the gateway
Determine where token issuance and introspection requests are limited, and whether your API gateway or reverse proxy can throttle login attempts without blocking legitimate refresh traffic. Token endpoints are a common target for credential stuffing, so the limits need to be tuned per endpoint. Confirm the gateway passes the authorization header through unchanged, because some proxies strip it.
Vendor documentation coverage
Compare the flows your design needs against the Simple OAuth documentation and the contributed module pages. Where the documentation is silent on a flow, such as token revocation on logout or custom claims for downstream services, plan a test that proves the behavior rather than assuming it. Gaps found here usually turn into custom code that your team must maintain through upgrades.
Security team sign-off on the token design
Ask your security team to approve the token lifetimes, scope list, storage model and key management before the build starts. Lifetimes, refresh token rotation and the signing key rotation schedule should be written down with an owner for each. An approved design is easier to defend in an audit than one that was configured during a sprint.
Questions teams ask before building Simple OAuth access
What drives the cost of building Simple OAuth access for decoupled Drupal?
The biggest cost drivers are the number of client types, the number of scopes and protected resources, and how identity is sourced. A single-page app, a server-side app, a native mobile app and a machine-to-machine integration each need a different grant setup, testing and documentation. Costs fall into two groups. One-time costs cover design, configuration, custom code, test coverage and handover. Recurring costs cover module updates, security advisory response, key and secret rotation, and support for new clients. When you ask for a quote, request the price split by client type, the scope list, the test plan and the post-launch support terms. If a partner is involved, the quote should state who pays for environment setup and who owns the configuration afterward.
How long does a Simple OAuth implementation usually take?
Duration depends mostly on how many clients must authenticate, how complex the scope model is, and whether the Drupal site is already on a supported version. A single consumer app on a current Drupal site moves much faster than several clients with different trust levels. Time is also added by identity provider setup, staging environments and security review. Ask for a schedule in the written scope with milestones for design, build, integration testing and launch. Plan around the Drupal calendar as well: Drupal 10 reaches end of life on December 9, 2026, according to the core release schedule, so a site still on Drupal 10 should have its upgrade timing decided before the authentication work begins.
What must our team provide before OAuth work can start?
Your team should provide a complete client inventory before the build starts. For each front end or app, list the platform, the users who sign in, the data it reads and writes, and whether it runs in a browser, on a server or on a device. Also provide the Drupal and PHP versions in use, a staging environment that matches production, and access to the identity provider if one is involved. Name the people who approve scopes and token lifetimes, and the people who receive security advisories. Finally, provide test accounts for each role. Missing inventory is the most common reason for rework, because scope decisions depend on knowing every consumer before the first token is issued.
Can our in-house team build Simple OAuth access without a partner?
An in-house team can do this work if it already has Drupal development skills and working knowledge of OAuth 2.0 and OpenID Connect. The hard parts are not the module configuration. They are choosing grant types for each client, keeping scopes narrow, handling token expiry in the front end, and rotating keys without downtime. Teams without recent OAuth experience often underestimate testing negative cases, such as revoked tokens and expired refresh behavior. Check your team's coverage of these areas and whether anyone can own the setup after launch. A partner can be useful for a fixed-scope build with written deliverables, and the choice should be based on skills and ownership rather than on speed alone.
Does a Drupal 7 or older site change the Simple OAuth plan?
Yes, it changes the plan significantly. Drupal 6 and 7 sites cannot be updated in place to Drupal 10 or later. They must be migrated into a new site using the core Migrate, Migrate Drupal and Migrate Drupal UI modules or Drush, and themes must be rebuilt because they cannot be migrated. Authentication is then designed for the new site, not carried over from the old one. Drupal 7 security support ended on January 5, 2025, according to Drupal.org. The Drupal Association's Extended Security Support Provider Program lists HeroDevs and Tag1 Consulting as certified vendors for sites that cannot migrate yet. Confirm with the provider how they handle API endpoints, because a temporary security arrangement does not replace a supported authentication design.
Should we use the OpenID Connect module alongside Simple OAuth?
They solve different problems, so the decision is about direction of trust. The OpenID Connect module lets an external identity provider authenticate Drupal users, and it creates accounts on first sign-in. It includes built-in clients for generic OAuth 2.0, GitHub, Google, LinkedIn, Facebook and Okta, according to its Drupal.org project page. Simple OAuth issues tokens so your own front ends and apps can call Drupal's APIs. Many decoupled builds use both: staff sign in through the corporate provider, and the front end then receives tokens for API calls. Decide which system owns user accounts, how roles map from the provider, and what happens when an account is disabled upstream. Confirm the exact combination against each module's current documentation before you commit to it.
What usually breaks in decoupled OAuth setups after launch?
Most post-launch problems come from a small set of causes. Front ends mishandle token expiry and refresh, so users see sudden sign-out errors. Redirect URIs drift between environments, so sign-in fails after a deployment. Cross-origin settings change during hosting updates, and key or secret rotation is done without a rollout plan. Drupal's own releases also matter: bugfix releases come on the first Wednesday of each month and security releases on the third Wednesday, according to the core release schedule. Each security release can touch authentication code, so the team needs a tested routine for applying it to staging first. Ask any partner for a written plan covering rotation, advisory response and regression tests for sign-in flows.
How DeployOpen scopes and delivers Drupal API authentication
DeployOpen plans, implements, documents and supports Simple OAuth authentication for decoupled Drupal inside your own infrastructure. We start from the clients you need to serve, agree the grant types, scopes and token lifetimes in writing, and then build and hand over the configuration with the runbooks your team needs to operate it.
How we scope
- Inventory every front end, app and integration that will call Drupal and the user context each one needs
- Agree in writing which grant type, scopes and token lifetimes apply to each client type
- Record the Drupal version path, including the Simple OAuth 5.x to 6.x upgrade caution, before build work starts
How we build
- Configure Simple OAuth consumers, keys and scopes in a non-production Drupal environment first
- Connect identity providers with OpenID Connect only where the agreed design needs external sign-in
- Test token issuance, expiry, refresh and revocation against each client, with results recorded for your review
How we hand over
- Document each client configuration, scope map and key rotation step so your team can repeat them
- Provide runbooks for revoking a compromised client and for responding to a security advisory affecting the stack
- Walk your engineers through the upgrade notes that apply to your Drupal and Simple OAuth versions
How we support
- Engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts
- Ongoing support, upgrades and continuous security testing are available as a retainer
- Ask any partner to confirm how they will handle key rotation and upgrade testing in your own environment

