Generic OpenID Connect SSO, multi-domain hosting, and social-login hardening - #1005
Open
SoFMeRight wants to merge 2 commits into
Open
SoFMeRight wants to merge 2 commits into
SoFMeRight wants to merge 2 commits into
Conversation
Adds a generic OpenID Connect provider so an instance can sign in against any OIDC issuer, and lets one instance serve several domains by deriving the OAuth callback from the request host. OpenID Connect socialiteproviders/openidconnect, driver "openidconnect", configured by discovery from OIDC_ISSUER. EventServiceProvider gains the SocialiteWasCalled mapping the package registers its driver through -- without it Socialite reports "Driver [openidconnect] not supported" however complete the configuration is. config/app.php stops listing Laravel's SocialiteServiceProvider so the Manager's provider (a superset adding the extendSocialite hook) binds instead; the built-in Google, Facebook, Twitter and GitHub providers are unaffected. Claim mapping Providers disagree over which claim carries which field, so the username, display name, email and picture are each read from a configurable, ordered list of claims, defaulting to the standard OIDC claims. This is not cosmetic: the driver maps neither preferred_username nor picture, so without it an avatar is never stored at all and the profile slug quietly degrades to the email local part on Zitadel, Keycloak and Authentik. Multi-domain SocialLoginController derives the callback from the current request when no redirect is configured, so each visitor returns to the domain they arrived on. ALLOWED_HOSTS is the Host allow-list that keeps that derivation safe; left unset the previous behaviour is unchanged. Social login hardening Identity now matches on the provider's immutable subject id rather than the email alone, an email is required and must not be asserted unverified before it bridges to an existing account, and provisioned users get the role, block flag, hashed password and unique littlelink_name a locally-registered account has. Provisioning and account linking are each separately refusable. Also ALLOWED_FRAME_ORIGINS writes a CSP frame-ancestors directive (empty sends none, as before) and SESSION_SAME_SITE becomes settable, which is what embedding across sites needs. FORCE_HTTPS and FORCE_ROUTE_HTTPS move to config/app.php so `config:cache` no longer silently disables them. OIDC_AUTO_LAUNCH sends the login page straight to the provider, with ?local=1 always still reaching the form. RP-initiated logout ends the provider session on sign-out, and can be switched off. Every new setting defaults to prior behaviour; an instance that sets none of them is unchanged.
Lets the identity provider decide who may sign in and who administers the instance. Neither was reachable before: every single sign-on user was provisioned with the plain user role, and no group claim was ever read. OIDC_ALLOWED_GROUPS and OIDC_ALLOWED_DOMAINS are allow-lists. Empty, which is the default, places no restriction at all. Both are re-evaluated on every sign-in rather than only at provisioning, so removing someone from a group upstream ends their access here on their next attempt. OIDC_ADMIN_GROUP mirrors the admin role onto a group. Unset, the role stays entirely the instance's own business. Membership is re-read on each sign-in so a grant can be withdrawn as well as given, with the first account exempt so an instance can never be locked out of its own panel. Group memberships are read from OIDC_GROUPS_CLAIM, accepting both a list and a comma-separated string, since providers send both shapes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #918.
Adds a generic OpenID Connect provider so an instance can sign in against any
OIDC issuer — Keycloak included, by pointing
OIDC_ISSUERat the realm URL(
https://<host>/realms/<realm>) and letting discovery supply the endpoints.As #918 notes, Socialite has no generic OIDC driver of its own; this uses the
community
socialiteproviders/openidconnectpackage, which is discovery-basedrather than per-vendor, so Keycloak, Zitadel, Authentik, Entra, Auth0, Okta and
Google are all the same configuration with a different issuer.
It also lets one instance serve several domains, and fixes several issues in the
existing social login along the way.
Every new setting defaults to current behaviour — an instance that sets none of
them behaves exactly as it does today.
Social login fixes (relevant even without OIDC)
These affect existing Google / Facebook / Twitter / GitHub users, so they may be
worth taking regardless of the rest:
SocialLoginControllerlooked the user upby email, so an email reassigned at the provider could take over an existing
account. It now matches on the provider's immutable subject id, and only falls
back to email — which must not be asserted unverified — to bridge or provision.
role, noblockflag, and a
littlelink_nametaken straight from the provider without theuniqueness or charset rules registration enforces. They now match the shape of
a locally-registered account.
FORCE_HTTPS/FORCE_ROUTE_HTTPSwere silently dead underconfig:cache.They were read with
env()outsideconfig/, which returns null once theconfig is cached. Moved into
config/app.php; values and defaults unchanged.OpenID Connect
Uses
socialiteproviders/openidconnect, configured by discovery fromOIDC_ISSUER. Two things were needed to make a community Socialite driver workhere: the
SocialiteWasCalledlistener the package registers through (without itevery request fails with
Driver [openidconnect] not supported), and droppingLaravel\Socialite\SocialiteServiceProviderfromconfig/app.phpso theManager's provider — a superset that adds the
extendSocialitehook — bindsinstead. Built-in providers are unaffected either way.
Claim mapping is configurable, because providers disagree about which claim
carries which field and the driver maps neither
preferred_usernamenorpictureat all. Without it an avatar is never stored, and the profile slugsilently degrades to the email local part on Zitadel, Keycloak and Authentik.
Multi-domain
With no redirect configured, the OAuth callback is derived from the request
host, so one instance can serve several apexes and return each visitor to the
domain they arrived on.
ALLOWED_HOSTSis the Host allow-list that keeps thatderivation safe; left unset, behaviour is unchanged. Setting
OIDC_REDIRECT_URIpins a single redirect as before.
Commits
OIDC_ALLOWED_GROUPS,OIDC_ALLOWED_DOMAINS,OIDC_ADMIN_GROUP. Purely additive (100 insertions, 0 deletions), so it canbe dropped without touching the first commit if you'd rather not take role
mapping from an IdP.
Notes for review
orandSign in with :providerare added toall 14 locales. The
orvalues match the first word of each locale's existing"or sign in with other accounts?" string, so those are corroborated; the
Sign in with :providerstrings are mine and only checked for consistency witheach file's own phrasing. Worth a native speaker's eye.
composer.lockadds onlysocialiteproviders/managerandsocialiteproviders/openidconnect— no version bumps, no removals.config:cachefixes, the host/frame settings,and the social-login hardening are all separable from OIDC if you'd prefer them
as their own PRs.
"other accounts" heading now only renders when the provider icon row is
non-empty — previously it could introduce an empty row.