Skip to main content
Drupal is a registered trademark of Dries Buytaert
Release: Configuration Language Lock 1.0.2 Minor update available for module config_language_lock (1.0.2). Release: Canvas Override 1.0.1 Minor update available for module canvas_override (1.0.1). Release: Media Remote Image 8.x-1.2 Minor update available for module media_entity_remote_image (8.x-1.2). Release: IDNA Convert Service (punycode) 2.0.4 Minor update available for module idna (2.0.4). Release: Bootstrap Cloud 7.1.3 Minor update available for theme bootstrap_cloud (7.1.3). Release: Token Browser 1.0.2 Minor update available for module token_browser (1.0.2). Release: Varbase Project 11.0.8 Minor update available for module varbase_project (11.0.8). Release: Media Remote Image 2.0.0-beta1 First beta version released for module media_entity_remote_image (2.0.0-beta1). Usage Milestone: Term CSV Export Import Module term_csv_export_import crossed 1,000 active installs. Security Coverage: Module Scout Module module_scout now has official Drupal security advisory coverage.

This module enables decoupled Drupal sites to authenticate users against external identity providers like AWS Cognito or Okta. It handles token validation, multi-factor authentication, and account linking, allowing Drupal to act as an API backend while the identity provider manages user credentials and lifecycle. This provides a secure, headless authentication solution without requiring custom glue code.

Headless IdP authenticates a decoupled Drupal site against an external identity provider, so you don't have to write the JWT-validation, MFA and account-linking glue yourself. The provider handles passwords, MFA and the user lifecycle; Drupal validates the token and runs as your API backend.

Features

  • Four providers ready to use. AWS Cognito, Okta and two Microsoft Entra providers - workforce Entra ID and Entra External ID (customer identity / CIAM) - ship with the module, and a plugin API lets you add more. Keycloak and Supabase are on the 1.x roadmap, so you're not tied to one vendor.
  • The whole auth lifecycle, not just login. Password sign-in, MFA challenge and response, user enrolment, session revocation, email and password sync, and refresh-token rotation.
  • Headless-first. REST endpoints, JSON in and out, Bearer JWTs (an id_token or access token, depending on the provider). No redirect flows, no session cookies and no OAuth login screens to theme. Your frontend (Nuxt, Next, or anything that speaks HTTP) handles all of that.
  • Security you don't have to build. JWKS signature verification, algorithm-confusion rejection, strict issuer and audience binding, per-user, per-IP and per-session brute-force rate limiting, a configurable password-complexity policy, and length caps. Defence-in-depth from the start, with a dedicated security-kernel test suite.
  • Extensible by interface. Each provider declares which capabilities it supports and the module adapts. Adding a provider or a capability never breaks the ones already shipped.
  • Operator tooling. Drush commands to inspect providers, manage the Drupal-to-IdP account links, migrate from openid_connect, and manage MFA preferences. Backed by over 1,000 unit and kernel tests.

Why use it

It's built for teams running a Nuxt, Next or other SPA frontend on a Drupal backend who want their external IdP to handle credentials, MFA and the lifecycle, with Drupal as the policy and API layer. The provider does the security-sensitive work; you get a clean, headless, provider-agnostic integration instead of bespoke glue code.

Why I built it

Over five years of decoupled Drupal builds, I kept solving the same authentication problem. On all of them Drupal itself was the authenticator - the frontend signed users in against a Drupal endpoint through simple_oauth, and every site had its own local user pool. When the company moved towards ISO 27001 compliance, those separate pools had to merge into one shared AWS Cognito pool, with attributes controlling which platforms each user could reach. The identity provider now owned credentials, MFA and the audit trail, not Drupal.

You can't bulk-migrate hashed passwords - they're one-way, so that move had to run lazily, at each user's next login. That, and everything around it - JWT validation, issuer and audience pinning, algorithm-confusion rejection, the MFA round-trip, linking each external identity to a Drupal user and keeping them in sync - is fiddly, security-sensitive work where subtly wrong means a hole.

This module is that work done once, properly - the distillation of what those years taught me. The provider quirks and the production edge cases are baked into the capability interfaces and the security-kernel test suite, so the hardening comes as standard rather than something you retrofit under audit pressure. Making that kind of migration turnkey - moving an existing Drupal user pool onto the IdP lazily at login - is on the roadmap.

How it works

Your frontend signs the user in against the IdP and gets a JWT back, then sends that token to Drupal as a Bearer credential. Drupal verifies the signature against the provider's published keys (JWKS), links the token to a Drupal user through the externalauth module, and treats the request as authenticated.

Try it before you install it

There is a standalone evaluation rig you can run locally: github.com/koopatroopa/headless-idp-demo. It is a DDEV site with this module installed and a small Nuxt frontend that drives the real endpoints, so you can watch login, an MFA challenge and the lazy on-login migration happen against an identity provider rather than reading about them.

You bring your own throwaway AWS Cognito pool - the README walks you through creating one in the console or from the CLI, and through deleting it again afterwards. Nothing in the rig talks to AWS on your behalf and no credentials ship with it. There is an Okta lane too, against a free Integrator org, which shows the same flows running through a different provider plugin.

Three seeded accounts cover the interesting cases: a plain login, a user whose existing Drupal password already meets the pool policy and so migrates without noticing, and a user whose password does not and gets asked for a replacement.

It is a demo, not a deployment reference - seeded admin accounts and permissive CORS are fine for a throwaway and wrong for production. The module README and docs/frontend-integration-nuxt.md cover the production shape.

Requirements

  • Drupal 10.2 or 11, PHP 8.1 or later
  • simple_oauth, externalauth, and firebase/php-jwt
  • The AWS SDK (aws/aws-sdk-php) when you enable the Cognito sub-module. The Okta and Entra sub-modules use Guzzle, which already ships with Drupal.

Getting started

Enable the base module and at least one provider sub-module (headless_idp_cognito, headless_idp_okta, headless_idp_entra or headless_idp_entra_external), then run drush cache:rebuild. Configure it at Configuration > People > Headless IdP. Each provider's own setup is documented in its sub-module README, and a reference Nuxt 3 frontend composable ships in the module's docs.

Documentation

How it compares

  • OpenID Connect is redirect-flow OIDC for server-rendered Drupal. Headless IdP is Bearer-token and headless-first instead, and provider-agnostic through capability interfaces.
  • JWT mints and validates Drupal-issued JWTs. Headless IdP validates JWTs minted by an external IdP.

Depends on

Dependencies of the latest stable release

No dependencies recorded for this project.

Required by

Tracked projects that depend on this one

No tracked projects depend on this one yet.

Activity

Tracked releases
4
Tracked since
Jun 2026
Latest release
1 month ago
Releases (12 mo)
4 ▲ from 0
Maintenance
Active

Release Timeline

Releases

Version Type Core Release date
1.0.0-beta4 Pre-release 10–11 Jul 31, 2026
1.0.0-beta3 Pre-release 10–11 Jul 9, 2026
1.0.0-beta2 Pre-release 10–11 Jul 8, 2026
1.0.0-beta1 Pre-release 10–11 Jun 7, 2026