Drupal is a registered trademark of Dries Buytaert
Search API Solr 4.4.0 Minor update available for module search_api_solr (4.4.0). Page Manager 8.x-4.0 Major update available for module page_manager (8.x-4.0). Trash 3.1.0-beta2 New beta version released for module trash (3.1.0-beta2). Commerce AutoSKU 3.0.1 Minor update available for module commerce_autosku (3.0.1). Custom Field 4.0.10 Minor update available for module custom_field (4.0.10). Alternative login ID & display names 2.0.12 Minor update available for module alt_login (2.0.12). EntityReference UUID 3.0.1 Minor update available for module entity_reference_uuid (3.0.1). LocalGov Publications Importer 1.1.1 Minor update available for module localgov_publications_importer (1.1.1). Configuration Override Warn 8.x-1.6 Module config_override_warn updated after 10 months of inactivity (8.x-1.6). Table Alternate Rows Module table_altrow crossed 1,000 active installs.

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.

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.

Activity

Total releases
3
First release
Jun 2026
Latest release
2 weeks ago
Releases (12 mo)
3 ▲ from 0
Maintenance
Active

Release Timeline

Releases

Version Type Release date
1.0.0-beta3 Pre-release Jul 9, 2026
1.0.0-beta2 Pre-release Jul 8, 2026
1.0.0-beta1 Pre-release Jun 7, 2026