Single sign-on for the Craft CMS control panel

Craft CMS has no built-in SAML or OpenID Connect sign-in for the control panel. Keyway SSO adds it: it signs people in with your own identity provider, maps the attributes and groups it receives onto Craft users and user groups, and records every attempt on a diagnostics screen inside the control panel.

What the plugin does

These pages are written for the person who has the Craft site on one screen and the identity provider's admin console on the other. Everything here is taken from the plugin's own code and from its settings screen, so the field names quoted are the ones you will actually see.

Requirements

WhatVersion
PHP8.2 or newer
Craft CMS5.0 or newer
PHP extensionsdom, mbstring, openssl, zlib

zlib is not optional and is declared as a requirement, so Composer refuses to install the plugin without it: the SAML authentication request is DEFLATE-encoded before it is sent, and the SAML logout messages are encoded and read back the same way.

Installation

Not published yet. Keyway SSO is in final testing and is not yet in the Craft Plugin Store or on Packagist, so the commands below do not resolve today. They are here so you can see what installation will look like; this page will be updated when the plugin is listed.

From the project root of your Craft site:

The plugin handle is keyway-sso. Installing it creates two tables: the one behind the diagnostics screen, and the record of which accounts single sign-on created — without which a just-in-time account is refused on its second login. Installing through Settings → Plugins in the control panel runs the same migration.

After any later update, run php craft up. It is not optional housekeeping: a site that already had the plugin installed gets new tables only that way, and the one added in schema version 1.1.0 is the one that keeps people signing in.

Then open Settings → Plugins → Keyway SSO and follow the guide for your provider. Until you choose a protocol there, the login screen looks exactly as it did before the plugin was installed — Protocol starts at Disabled (password login only).

The five addresses

The plugin answers on five addresses. Each one exists twice: as an action URL and as a control-panel alias (sso/start, sso/acs, sso/callback, sso/metadata, sso/slo).

Give the identity provider the action URL and nothing else. The control-panel alias contains this site's cpTrigger (the admin part of the URL), which the site owner can rename at any time, and a renamed address is a configuration that quietly stops working.

AddressWhat it doesWho needs it
<BASE>/actions/keyway-sso/sso/startBegins a login: issues the single-use login state and the browser-binding cookie, then redirects to your provider.Nobody. The SSO button on the login screen links to the control-panel alias.
<BASE>/actions/keyway-sso/sso/acsSAML assertion consumer service: receives the provider's HTTP-POST assertion.Your provider (SAML), as the ACS / Single sign-on URL.
<BASE>/actions/keyway-sso/sso/callbackOIDC redirect URI: receives the authorization code.Your provider (OIDC), as the redirect URI.
<BASE>/actions/keyway-sso/sso/metadataServes this site's SAML service-provider metadata document. Public and unauthenticated, because several providers fetch it themselves.Your provider (SAML), optionally — it is also the file behind Download metadata.
<BASE>/actions/keyway-sso/sso/sloReceives the provider's SAML LogoutRequest on the HTTP-Redirect binding and ends the matching Craft session. Refuses every message until both the provider's logout URL and an SP private key are set, and until then the metadata document advertises no SingleLogoutService.Your provider (SAML), only if you opt into Single Logout — which is SAML only and one way (signing out of Craft ends nothing at the provider), and which Okta will not trigger after an ordinary Okta sign-out.

Do not type these by hand. The settings screen shows the exact addresses this installation answers on, as copy fields (ACS URL this site answers on, Redirect URI this site answers on, Metadata URL, SP single logout URL). For the two you have to repeat in a field of your own — ACS URL and Redirect URI — the screen also compares what you pasted with the address this site really answers on and comments underneath the field. Both protocols compare those values character for character, and a one-character difference fails the login with a message that reads like a certificate problem.

Two things worth knowing before they surprise you. On an installation with Craft's default omitScriptNameInUrls = false the addresses come out as <BASE>/index.php?p=actions/keyway-sso/sso/acs; that routes correctly and is fine for SAML, but some providers refuse a redirect URI carrying a query string, so such a site needs omitScriptNameInUrls turned on before OpenID Connect can be registered. And the metadata address answers 404 whenever Protocol is not SAML 2.0, or SP entity ID or ACS URL is empty or malformed — deliberately, because a metadata document naming a blank entity ID would configure your provider against a login that can never validate.

The sixth address is not one you give away: the diagnostics screen is a control-panel page (<cp>/sso/diagnostics), reachable from the Open sign-in diagnostics button at the bottom of the settings screen. The plugin adds no navigation item, so that button is the way in.

Providers, and what has actually been verified

Any SAML 2.0 or OpenID Connect provider works the same way — the guides differ only in where each value is found in the provider's console. What differs is how much of it we have run ourselves, so here it is without varnish:

ProviderProtocols covered by the guideVerification status
KeycloakSAML 2.0 and OpenID ConnectSAML verified end to end on 17 September 2026 against a live Keycloak 26.0 realm and a live Craft install: sign-in, attribute and group mapping, account creation, and IdP-initiated Single Logout — which is SAML only and one-way (IdP → Craft; signing out of Craft ends nothing at the provider), opt-in (it needs the provider's logout URL and an SP private key before it does anything), and will not work with Okta, which does not send such a request after an ordinary Okta sign-out. OpenID Connect has not been run end to end; for it a smoke check reads a real realm's discovery document and JWKS and verifies a real id token's key selection and signature — but not the HTTPS transport and not the full id-token reader.
OktaSAML 2.0SAML verified end to end on 17 September 2026 against a live Okta tenant (Integrator Free Plan) and a fresh Craft 5.11 install on default settings: first sign-in created the account just in time with the mapped group, and the second sign-in updated it and reached the control panel. Single Logout will not work with Okta — by Okta's own documentation (read 17 September 2026) Okta will not send a logout request to the application after an ordinary Okta sign-out; that half was not exercised on the live tenant. OIDC with Okta has not been checked.
Microsoft Entra IDOpenID ConnectGuide provided, not yet verified against a live tenant.

Guides

Secrets belong in environment variables

Exactly two settings accept an environment-variable reference — SP private key (SAML) and Client secret (OpenID Connect). Write them as $KEYWAY_SP_KEY / $KEYWAY_OIDC_SECRET and put the value in the server's environment; Craft resolves the reference on read, so the secret stays out of project config and out of version control. Every other field is stored as typed — writing $SOMETHING into SP entity ID or ACS URL puts that literal string into the metadata document and the comparison, and the login fails.

If one of the two supported fields points at a variable that is not set, the plugin fails closed: single sign-on stays off, the settings screen names the missing variable at the top of the page, and the field itself carries an error.

Keyway SSO for Craft CMS 5

SAML 2.0 and OpenID Connect sign-in for the Craft control panel: attribute and group mapping, just-in-time accounts, an admin password fallback with a guard that switches admin password login back on, with a warning, if your settings would otherwise leave nobody able to sign in, and a diagnostics screen that tells you why a login was refused. The plugin is in final testing and is on its way to the Craft Plugin Store.

Read the deployment documentation