dmlab / module-customer-sso
Storefront SSO capability for Magento 2 — provider-agnostic OIDC login for customers (JIT customer creation, account linking by email/sub, IdP-claim to customer-group mapping).
Package info
github.com/dmlab-work/module-customer-sso
Type:magento2-module
pkg:composer/dmlab/module-customer-sso
Requires
- php: ~8.2.0||~8.3.0||~8.4.0||~8.5.0
- dmlab/module-sso-core: ^0.2.3
- magento/framework: ^103.0.7
- magento/module-backend: ^102.0.7
- magento/module-checkout: ^100.4.7
- magento/module-config: ^101.2.7
- magento/module-customer: ^103.0.7
- magento/module-store: ^101.1.7
Requires (Dev)
- phpunit/phpunit: ^11.5||^12.5
Suggests
- dmlab/module-customer-passkey: Passwordless passkey (WebAuthn) sign-in for customers, alongside SSO
Provides
None
Conflicts
None
Replaces
None
README
Provider-agnostic single sign-on for the Magento 2 storefront (OIDC).
Shoppers sign in with the account they already have at your identity provider (Okta via customer-sso-okta) instead of creating another password. The first sign-in creates the customer, existing customers are linked by verified email, and IdP groups can set the customer group (e.g. wholesale).
Install
Usually installed via a provider plugin, which pulls this module and sso-core:
composer require dmlab/module-customer-sso-okta bin/magento module:enable DmLab_CustomerSsoOkta DmLab_CustomerSso DmLab_SsoCore bin/magento setup:upgrade
Direct install of the core alone:
composer require dmlab/module-customer-sso bin/magento module:enable DmLab_SsoCore DmLab_CustomerSso bin/magento setup:upgrade
Register the module callback URL in your IdP: https://<store-host>/customersso/sso/callback. It must exactly match the storefront URL used at runtime.
Requirements
- Magento 2.4.7+ · PHP 8.2–8.5
- A provider plugin, e.g.
dmlab/module-customer-sso-okta dmlab/module-sso-core^0.2.3 (installed automatically)- The store's callback URL registered at the IdP
Hyvä
A Hyvä (Tailwind) template for the login-page button ships with the module, but the theme layout must be edited by hand. In the Hyvä theme's Magento_Customer/layout/customer_account_login.xml:
<referenceBlock name="dmlab.customer.sso.button" template="DmLab_CustomerSso::login/sso-button-hyva.phtml"/>
The checkout and minicart popup buttons are Luma only.
Pairs with customer-sso-okta (Okta sign-in) and customer-passkey (passkey sign-in).
Configuration
Admin → Stores → Configuration → DMLab → Customer SSO → General (config path dmlab_customer_sso/general/*). Settings are store-scoped, so SSO can be enabled and tuned per store view.
| Field | Path | Notes |
|---|---|---|
| Enable Customer SSO | enabled |
Master switch; off by default. |
| Identity Provider | active_provider |
Dropdown populated by installed provider plugins. |
| Client ID | client_id |
OIDC client id from the IdP. |
| Client Secret | client_secret |
Stored encrypted; sensitive, so app:config:dump leaves it out — set it per environment. |
| Keep Password Login | allow_password_login |
On (default): native email/password login stays available alongside SSO. Off: once SSO is live, login is SSO-only — the form is hidden and password login is refused server-side (login form, checkout popup, REST and GraphQL customer tokens). |
| Account Linking by Email | auto_link_policy |
require_verification (default) or disabled. See below. |
| Group to Customer-Group Map | group_customer_group_map |
IdP group → customer-group rules. See below. |
| Sign Out of IdP on Logout | idp_logout |
Off by default. See below. |
The "Sign in with SSO" button appears on the customer login page and in the Luma checkout login popups (authentication popup and the checkout authentication step) when the module is enabled and a provider is selected; it uses the identity provider's branding. Popup sign-in returns the shopper to checkout. Custom placements can link to customersso/sso/start?referer=<base64 URL> to return to that page; a URL off the store's own origin is ignored. Setting Keep Password Login to No hides the native form and refuses password login once SSO is live. As a break-glass, password login stays available on an unconfigured or broken install (no active provider), so a mis-set toggle can never lock shoppers out.
Account linking
An SSO identity is matched to a customer in this order:
-
By IdP identity — issuer (
iss) + subject (sub) stored indmlab_customer_sso_subjectper provider. Signs in regardless of email verification. A link created before issuers were stored is bound to the issuer of its next matching login. -
By email, governed by
auto_link_policy:require_verification(default) — adopt the existing customer only when the IdP marks the email verified (email_verified) and the account is not awaiting email confirmation. Legacyautoresolves to this.disabled— never link by email.
Otherwise the login is refused and the shopper is told to sign in with their password and link from My Account — keep Keep Password Login on if existing customers may need this path.
-
JIT create — no match → a new customer is created only when the IdP marks the email verified; an unverified email is refused and nothing is created.
Explicit linking — a signed-in customer links or unlinks an identity under My Account → Single Sign-On (customersso/account). The session already proves ownership, so the verified flag is not checked; the link is bound to the session customer. A customer holds at most one identity per provider. An identity belongs to one customer — one per website when customer accounts are shared per website (customer/account_share/scope).
Group → customer-group mapping
group_customer_group_map takes one rule per line as idp_group=customer_group_id, where customer_group_id is a Magento customer_group id. Lines are in priority order: a customer holds one group, so the first line matching any of the identity's groups wins, and a duplicate group keeps its first line. Blank lines and # comments are ignored.
# IdP group Customer group id
wholesale=2
vip=3
With no rules configured this is inert: customer groups are never touched, so a store using SSO only for login keeps its manually assigned groups. Once a map exists, groups are resolved on JIT create and refreshed on every login, so IdP group changes take effect at next sign-in. An identity whose groups match no rule reverts to the store's default customer group (GroupManagementInterface::getDefaultGroup) — customers always hold a valid group. A rule pointing at a non-existent group id is skipped, leaving the current group unchanged. When the IdP marks the groups claim as distributed (groups overage, e.g. Entra _claim_names), the token's groups are incomplete and the current group is left unchanged.
IdP logout
With Sign Out of IdP on Logout on, a shopper who signed in with SSO is also signed out of the IdP (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 customer session), client_id and post_logout_redirect_uri, then returns to the logout success page. This ends the shopper's IdP session for every app using it.
Register https://<store-host>/customer/account/logoutSuccess/ (each store base URL) as a post-logout redirect URI at the IdP. If the IdP advertises no end_session_endpoint, discovery fails, the store's active provider changed since login, or the shopper signed in with a password, logout stays local.
Architecture
This module is the storefront-login capability core: it signs shoppers in over OIDC via sso-core but never references a concrete IdP. A provider plugin (e.g. customer-sso-okta) registers a preset that binds it to an identity provider.
- Shopper clicks "Sign in with SSO" →
customersso/sso/startbuilds the OIDC auth URL (state + nonce + PKCE) via sso-core and the active preset, persists the state and aloginintent (optional own-origin return URL) in the customer session (valid 10 minutes, at most 10 pending per session), then redirects to the IdP. Linking starts the same way fromcustomersso/account/link(POST, signed-in only) with alinkintent. - IdP redirects back to
customersso/sso/callback, which validatesstateand the RFC 9207issresponse parameter, exchanges the code, and normalizes claims into anIdentityvia sso-core. Failures return with a message to the login page, or to My Account → Single Sign-On for alinkintent. login(refused when a customer is already signed in): the customer is matched (issuer +sub→ verified email under policy → verified JIT create), the group is resolved, the session is established andmage-cache-sessidis reset so private content reloads; redirect to the return URL or My Account.link: the identity is linked to the session customer; redirect to My Account → Single Sign-On.
License
OSL-3.0 © DMLab. Commercial licensing and support: https://dmlab.work.
