Search by

dmlab / module-admin-sso

dmlab

Admin panel SSO capability for Magento 2 — provider-agnostic OIDC or SAML 2.0 login for the admin panel (JIT admin-user creation, IdP-group to ACL-role mapping, enforce-SSO + break-glass).

Package info

github.com/dmlab-work/module-admin-sso

Type:magento2-module

pkg:composer/dmlab/module-admin-sso

Statistics

Installs: 127

Dependents: 3

Suggesters: 0

Stars: 1

Open Issues: 0

0.3.0 2026-11-03 09:09 UTC

This package is auto-updated.

Last update: 2026-11-03 10:14:26 UTC


README

Provider-agnostic single sign-on for the Magento 2 admin panel (OIDC or SAML 2.0).

CI codecov Packagist Version Downloads PHP License Magento

Every admin user in Magento has its own password, staff who leave keep their access until someone remembers to remove them, and paid SSO extensions cap how many users you may have.

Admin SSO lets staff sign into the Magento admin with their company account — Okta, Microsoft Entra ID or Google Workspace via a provider plugin, any OpenID Connect provider (Keycloak, Authentik, Auth0, …) through the built-in Generic OIDC, or any SAML 2.0 IdP (AD FS, Entra ID, Okta, Keycloak) through the built-in Generic SAML 2.0. Admin accounts are created on first login and roles come from IdP groups (or a default role); password login can be switched off, with a break-glass list for emergencies. Free, no user cap.

Admin SSO settings in Magento: enable, identity provider, client credentials, enforce SSO and break-glass login

Install

Install a provider plugin (dmlab/module-admin-sso-okta, -azure or -google); it pulls this module and its dependencies:

composer require dmlab/module-admin-sso-okta
bin/magento module:enable DmLab_SsoCore DmLab_AdminSso DmLab_AdminSsoOkta
bin/magento setup:upgrade

For Entra ID or Google, require dmlab/module-admin-sso-azure / -google and enable DmLab_AdminSsoAzure / DmLab_AdminSsoGoogle instead.

For any other OIDC provider (Generic OIDC) or any SAML 2.0 IdP (Generic SAML 2.0), install this module alone — no plugin needed:

composer require dmlab/module-admin-sso
bin/magento module:enable DmLab_SsoCore DmLab_AdminSso
bin/magento setup:upgrade

OIDC: register the callback URL in your IdP: https://<admin-host>/<admin-path>/adminsso/sso/callback/ (with the trailing slash), where <admin-path> is the backend frontName (default admin). It must exactly match the admin URL used at runtime. SAML: see SAML.

Requirements

  • Magento 2.4.7+ · PHP 8.2–8.5
  • A provider plugin (Okta, Microsoft Entra ID, Google Workspace), any OIDC provider via Generic OIDC, or any SAML 2.0 IdP via Generic SAML 2.0
  • ext-openssl
  • The admin callback URL registered at the IdP (see Install)
  • SAML: the admin served over HTTPS (the request-state cookie is Secure; SameSite=None)
  • Magento_TwoFactorAuth optional — MFA done at the IdP is honoured (see 2FA)

Pairs with admin-scim for automatic admin offboarding.

Configuration

Admin → Stores → Configuration → DMLab → Admin SSO → General (config path dmlab_admin_sso/general/*).

Field Path Notes
Enable Admin SSO enabled Master switch; off by default.
Identity Provider active_provider Generic OIDC, Generic SAML 2.0 and the presets of installed provider plugins.
Client ID client_id OIDC client id from the IdP (not used by SAML).
Client Secret client_secret Stored encrypted; sensitive, so app:config:dump skips it (set per environment with config:sensitive:set).
Enforce SSO enforce_sso Blocks native password login. See below.
Allow Break-Glass Login break_glass Lets allowlisted users keep password login where SSO blocks it. Off by default.
Break-Glass Usernames break_glass_usernames Admin usernames, one per line or comma-separated; case-insensitive.
Group to Role Map group_role_map IdP group → ACL role rules. See below.
Default Role default_role Fallback role when no group matches; empty = refuse login.
Trust IdP MFA trust_idp_mfa On by default; skips Magento 2FA only with MFA evidence. See below.
Trusted ACR Values trusted_acr_values Optional acr values that count as MFA evidence.
Sign Out of IdP on Logout idp_logout Off by default. See below.

A "Sign in with SSO" button appears on the admin login page when the module is enabled and a provider is selected, using the active preset's branding (the generic OIDC and SAML presets have no icon).

Group → role mapping

group_role_map takes one rule per line as idp_group=role_id, where role_id is a Magento admin role id (authorization_role with role_type G). Blank lines and # comments are ignored. A Magento admin holds one role, so the first line matching any of the user's groups wins (rule order, not IdP group order); a repeated group keeps its first line.

# IdP group          Magento role id
admins=1
support-team=4

Roles are (re)assigned on every login, so IdP group changes take effect at next sign-in. An identity whose groups match no rule gets default_role. If that is empty (or the role id does not exist) the SSO login is refused before any admin is created or linked; the stored role of an existing admin is never demoted — deprovisioning is SCIM's job. If the IdP sends the groups as a distributed claim (_claim_names, e.g. Entra groups overage), the groups are incomplete and the login is refused too.

Any OIDC provider

Select Identity Provider = Generic OIDC, set Client ID / Secret under General, then fill DMLab → Admin SSO → Generic OIDC (config path dmlab_admin_sso/generic/*):

Field Path Notes
Issuer URL issuer_url Discovery is read from <issuer>/.well-known/openid-configuration; a full discovery URL also works.
Display Name display_name Button label "Sign in with …"; default SSO.
Scopes scopes Space-separated; default openid profile email; openid is always sent.
Groups Claim groups_claim ID token claim with the user's groups; default groups; empty = Default Role only.
Extra Authorization Parameters extra_auth_params One key=value per line (e.g. prompt=select_account); blank lines and # comments ignored.

Validation on save: absolute https issuer (http only for localhost); valid scope tokens; extra parameters as unique key=value lines, protocol-reserved names (e.g. state, redirect_uri) refused.

Redirect URI: https://<admin-host>/<admin-path>/adminsso/sso/callback/. Post-logout redirect URI (only with Sign Out of IdP on Logout): https://<admin-host>/<admin-path>/admin/auth/login/. email_verified is needed only to adopt an existing admin by email; new admins are created without it.

Keycloak

  1. Manage realms → Create realm → Realm name magento → Create.
  2. Clients → Create client → Client type OpenID Connect, Client ID magento-admin → Next → Client authentication On, Standard flow on → Next → Valid redirect URIs = the redirect URI, Valid post logout redirect URIs = the post-logout URI → Save. Copy the secret from the Credentials tab.
  3. Clients → magento-admin → Client scopes → magento-admin-dedicated → Configure a new mapper (or Add mapper → By configuration) → Group Membership: Name groups, Token Claim Name groups, Full group path Off, Add to ID token On → Save. With Full group path On, groups arrive as /magento-admins and the map lines must use that form.
  4. Groups → Create group magento-admins. Users → Create user (Email; Email verified On to adopt an existing admin) → Credentials → Set password, Temporary Off → Groups → Join Group → magento-admins → Join.
  5. Magento: Identity Provider = Generic OIDC; Client ID / Secret; Issuer URL https://<keycloak-host>/realms/magento; Display Name Keycloak; Groups Claim groups; Group to Role Map magento-admins=1 (role id).

Authentik

  1. Applications → Applications → New Application → with New Provider…: Name Magento Admin, Slug magento-admin → provider type OAuth2/OpenID Connect → Client type Confidential; Redirect URIs: Strict, type Authorization, the redirect URI (add the post-logout URI as a Strict entry of type Logout if IdP logout is used); Signing Key: pick any — required, sso-core accepts only asymmetric signatures and without a key authentik signs with HS256. Note the Client ID and Client Secret. Bindings are optional (none = every user may sign in).
  2. The default profile scope mapping already sends groups (group names) in the ID token. Directory → Groups → create magento-admins and add users.
  3. The default email mapping sends email_verified: false. Only to adopt existing admins by email: Customization → Property Mappings → New Property Mapping → Scope Mapping, Scope name email, expression per authentik's "Verify email address" docs; on the provider, Advanced protocol settings → Scopes, replace the default email mapping with it (never both).
  4. Magento: Issuer URL https://<authentik-host>/application/o/magento-admin/; Groups Claim groups; Group to Role Map magento-admins=1.

SAML (ADFS, Entra, Okta, Keycloak)

Select Identity Provider = Generic SAML 2.0, then fill DMLab → Admin SSO → SAML 2.0 (config path dmlab_admin_sso/saml/*). Group to Role Map, Default Role, Enforce SSO and break-glass under General apply as for OIDC.

SP Endpoints at the top of the group shows the three values to enter at the IdP:

  • ACS URL (Reply URL): https://<admin-host>/<admin-path>/adminsso/saml/acs/ (HTTP-POST)
  • SP Entity ID (Audience): SP Entity ID below, or the SP metadata URL when empty
  • SP Metadata URL: https://<admin-host>/<admin-path>/adminsso/saml/metadata/
Field Path Notes
Display Name display_name Button label "Sign in with …"; default SSO.
IdP Metadata Source idp_metadata_source url (default) or xml.
IdP Metadata URL idp_metadata_url Absolute https URL; keep any query string (Entra ?appid=).
IdP Metadata XML idp_metadata_xml The IdP EntityDescriptor, pasted as is; validated on save.
IdP Entity ID idp_entity_id Only when the metadata describes several IdPs.
SP Entity ID sp_entity_id Must equal the Entity ID / Audience at the IdP; empty = SP metadata URL.
NameID Format name_id_format Default unspecified; persistent recommended.
SP Certificate sp_certificate PEM; needed for signed AuthnRequests or encrypted assertions; published in the SP metadata.
SP Private Key sp_private_key Unencrypted PEM matching the certificate; stored encrypted, sensitive.
Sign AuthnRequests sign_authn_requests Off by default; needs certificate + key.
Require Encrypted Assertions require_encrypted_assertions Off by default; needs certificate + key. Encrypted assertions are decrypted whenever a key is set.
Email / Name / Groups Attribute email_attribute, name_attribute, groups_attribute Empty = defaults: email, mail; displayName, name (else givenName/firstName + surname/sn/lastName); groups, memberOf; plus the WS-Fed claim URIs and X.500 OIDs.
Subject Attribute subject_attribute Single-valued attribute identifying the user; empty = NameID.
Trust IdP Email trust_email Off by default. On = the asserted email counts as verified, so an existing admin with that email is adopted on first login. Enable only if the IdP controls the email.
Allow IdP-Initiated Login allow_idp_initiated Off by default. See below.

Assertions must be signed (RSA-SHA256+); a Response-only signature is rejected. The admin is linked by issuer = IdP entity ID and subject = NameID (or the subject attribute). Run bin/magento setup:upgrade once for sso-core's assertion replay table.

Keycloak

  1. Clients → Create client → Client type SAML, Client ID = SP entity ID → Valid redirect URIs = ACS URL → Save.
  2. Settings: Name ID format persistent, Force name ID format On, Sign documents On, Sign assertions On. Advanced: Assertion Consumer Service POST Binding URL = ACS URL.
  3. Keys: Client signature required Off unless Sign AuthnRequests is on (then import the SP certificate); Encrypt assertions needs the SP certificate.
  4. Client scopes → <client>-dedicated → Add mapper → By configuration: User Property email → SAML Attribute Name email, firstName → givenName, lastName → surname; Group list → Group attribute name groups, Single Group Attribute On, Full group path Off.
  5. Magento: IdP Metadata URL https://<keycloak-host>/realms/<realm>/protocol/saml/descriptor. IdP-initiated: set IDP-Initiated SSO URL name.

Microsoft Entra ID

  1. Entra admin center → Entra ID → Enterprise apps → New application → Create your own application (non-gallery) → Single sign-on → SAML.
  2. Basic SAML Configuration: Identifier (Entity ID) = SP entity ID, Reply URL (Assertion Consumer Service URL) = ACS URL (or Upload metadata file).
  3. Attributes & Claims: Unique User Identifier → user.objectid, format Persistent; the default emailaddress/givenname/surname claims match the defaults. Add a group claim → Groups assigned to the application (avoids the 150-group overage, which sends groups.link and refuses the login), Source attribute Group ID → map the object IDs in Group to Role Map.
  4. SAML Certificates → App Federation Metadata Url (keep its ?appid=) → Magento IdP Metadata URL. Signing: Sign SAML assertion, SHA-256 (defaults).
  5. Users and groups → assign.

Okta

  1. Admin Console → Applications → Applications → Create App Integration → SAML 2.0.
  2. Single sign-on URL = ACS URL (Use this for Recipient URL and Destination URL checked), Audience URI (SP Entity ID) = SP entity ID, Name ID format Persistent.
  3. Attribute Statements email → user.email, givenName → user.firstName, surname → user.lastName; Group Attribute Statements groups, filter Matches regex .* (or Starts with a prefix).
  4. Sign On tab → Metadata URL → Magento IdP Metadata URL. Assertion Encryption (advanced settings) needs the SP certificate.
  5. Assignments → groups.

AD FS

  1. AD FS Management → Relying Party Trusts → Add Relying Party Trust → Claims aware → Import data about the relying party published online (SP metadata URL; "from a file" when the admin is IP-restricted) → access control policy → Finish.
  2. Claim issuance policy: Send LDAP Attributes as Claims (E-Mail-Addresses → E-Mail Address, Given-Name → Given Name, Surname → Surname, Token-Groups - Unqualified Names → Role); Transform an Incoming Claim (UPN → Name ID, Persistent Identifier). Without a Name ID rule the login is rejected.
  3. Magento: IdP Metadata URL https://<adfs-host>/FederationMetadata/2007-06/FederationMetadata.xml. AD FS encrypts assertions automatically when the SP metadata carries a certificate.

IdP-initiated login, 2FA, logout

  • IdP-initiated (portal tile) needs Allow IdP-Initiated Login. Risk: login CSRF — an attacker can post their own valid unsolicited assertion into a victim's browser and sign the victim into the attacker's admin account. Keep it off unless the tile is required. Such logins land on the admin startup page whatever the RelayState.
  • 2FA: Magento 2FA always applies to SAML logins (Trust IdP MFA is OIDC only).
  • Logout stays local; SAML single logout is not supported.

Enforce SSO + break-glass

With Enforce SSO on, every native password login is rejected — the admin form and POST /rest/V1/integration/admin/token alike (enforced on Magento\User\Model\User::authenticate()). All sign-in goes through the IdP.

Even with Enforce SSO off, an admin linked to an IdP identity (created or adopted by SSO) cannot use a password while SSO is enabled, so a local password (e.g. from "Forgot password") does not outlive deactivation at the IdP. Local-only admins keep password login.

For recovery if the IdP is misconfigured, turn on Allow Break-Glass Login and list the local admins in Break-Glass Usernames. Only those usernames keep password login; an empty list means no bypass. There is no URL parameter.

With break-glass off, a lockout is recoverable only by disabling enforce_sso via CLI/DB:

bin/magento config:set dmlab_admin_sso/general/enforce_sso 0

2FA

With Magento_TwoFactorAuth enabled, an SSO login skips Magento 2FA only when Trust IdP MFA is on and the validated ID token carries MFA evidence:

  • amr (RFC 8176) contains mfa or mca, or a possession/biometric factor (otp, hwk, swk, sms, tel, sc, fpt, face, …) plus another method (e.g. ["pwd","otp"]); Okta and Entra send mfa after an MFA challenge;
  • or acr equals one of Trusted ACR Values.

Otherwise (no amr/acr, e.g. Google, or password-only) Magento 2FA still applies. Trust IdP MFA = No always keeps Magento 2FA.

IdP logout

With Sign Out of IdP on Logout on, logging out of an SSO-established admin session also ends the IdP session (OIDC RP-Initiated Logout 1.0): after the local logout the browser goes to the IdP's end_session_endpoint with id_token_hint (the login's ID token, kept in the admin session), client_id and post_logout_redirect_uri. This signs the user out of every app using that IdP session.

Register the admin login URL as a post-logout redirect URI at the IdP: https://<admin-host>/<admin-path>/admin/auth/login/. If the IdP advertises no end_session_endpoint, discovery fails, the active provider changed since login, or the session was a password login, logout stays local.

Architecture

admin-sso is provider-agnostic: it handles admin login, JIT accounts and roles, and delegates the OIDC and SAML protocols to the shared engine sso-core. Provider plugins (admin-sso-okta, -azure, -google) register a preset (discovery URL, groups claim, branding) that fills the Identity Provider dropdown. SAML plugins implement Api\SamlPresetInterface (branding only; protocol settings come from dmlab_admin_sso/saml/*) and di-merge it into Model\Saml\SamlPresetRegistry. The built-in Generic OIDC (from sso-core) and Generic SAML 2.0 (saml) presets cover standard IdPs from config alone; plugins stay the branded path.

How it works

  1. User clicks "Sign in with SSO" → adminsso/sso/start builds the OIDC auth URL (state + nonce + PKCE; pending state valid 10 minutes, at most 10 per session) via sso-core and the active preset, then redirects to the IdP.
  2. The IdP redirects back to adminsso/sso/callback, which validates state and the response issuer (RFC 9207 iss), exchanges the code, and normalizes claims into an Identity via sso-core.
  3. The ACL role is resolved from IdP groups; if none resolves, the login is refused here.
  4. JIT: the admin user is matched by IdP issuer + sub (stored on admin_user.dmlab_sso_issuer / dmlab_sso_subject_id, unique together, added at setup:upgrade), falling back to email — adopted only when the IdP marks it verified (email_verified), otherwise refused — and created if absent (with a suffixed username on any local collision). A matched admin must be active and not locked (lock_expires). The role is assigned and the admin backend session is established like a native login: backend_auth_user_login_success fires and Magento_Security records the session (Admin Account Sharing applies).
  5. If the IdP proved MFA, the session satisfies Magento core 2FA (Magento_TwoFactorAuth); otherwise the user completes Magento 2FA.

SAML: adminsso/sso/start sends a (optionally signed) AuthnRequest via HTTP-Redirect and binds it to the browser in an encrypted dmlab_admin_sso_saml cookie (10 minutes, 5 newest kept). The IdP POSTs to adminsso/saml/acs, which checks the response against that request, validates signature, audience, destination and replay via sso-core, and maps attributes into an Identity. Steps 3–4 then run unchanged (issuer = IdP entity ID, subject = NameID); Magento 2FA always applies.

Identity links and switching IdP

An admin is linked to one IdP identity (issuer + sub). Links created before the issuer was stored are bound to the issuer of their next SSO login. A different issuer (new IdP, tenant or authorization server) never matches an existing link: its users go through the normal email adoption / JIT rules, and email adoption only applies to unlinked admins. Switching a provider between OIDC and SAML changes the subject (NameID vs sub), so clear the links first there too. To move linked admins to a new IdP, clear their link first:

UPDATE admin_user SET dmlab_sso_subject_id = NULL, dmlab_sso_issuer = NULL WHERE user_id IN (...);

License

OSL-3.0 © DMLab. Commercial licensing and support: https://dmlab.work.