Login & logout
Starting a login (GET login)
Section titled “Starting a login (GET login)”GET /login redirects to the provider’s authorization endpoint with response_type=code,
the configured client_id, redirect_uri, and scopes, plus three one-time values stored
in the session: a state, a nonce, and a PKCE code_verifier (sent as an S256
code_challenge). An already-authenticated user is redirected straight to
redirect_after_login instead.
The callback (GET login.callback)
Section titled “The callback (GET login.callback)”The callback pulls the stored state/nonce/code_verifier from the session — each
callback context is single-use, so a replayed or duplicated callback fails. It then:
- Rejects the request if the provider returned an
error, thestatedoesn’t match (constant-time comparison), or nocodeis present. - Exchanges the code at the token endpoint with the
code_verifier(and theclient_secret, when configured). - Validates the returned
id_token(below). - Resolves the local user and logs them into
login_guard. - Stores the token set in the session (
oidc-client.tokens:access_token,refresh_token,id_token,expires_at) and regenerates the session id. - Redirects via
redirect()->intended(...)toredirect_after_login.
Any failure redirects back to the login route with a generic oidc error message —
details never leak to the browser.
id_token validation
Section titled “id_token validation”The token is validated strictly, in this order:
- Parses as a signed JWT with a
kidheader. - Signature — RS256 against the provider’s JWKS key matching
kid. An unknownkidtriggers one fresh JWKS fetch before failing, so provider key rotation is picked up without redeploying. issequals the configured issuer (trailing slashes ignored).subis a non-empty string.audcontains thisclient_id; with multiple audiences,azpmust equal it.nonceequals the one-time nonce from the session.exp/nbf/iathold within the configuredleeway.
Resolving the local user
Section titled “Resolving the local user”By default, the token’s sub is fed to the login guard’s user provider via
retrieveById($sub) — which fits self-SSO setups where the provider and client share user
ids. Against a third-party IdP, the sub is the provider’s identifier, so bind your own
resolver in a service provider’s boot():
use Bambamboole\LaravelOidc\Client\Facades\OidcClient;
OidcClient::resolveUsersUsing(function (string $sub, array $claims): ?User { return User::firstOrCreate( ['oidc_sub' => $sub], ['name' => $claims['name'] ?? '', 'email' => $claims['email'] ?? ''], );});Return null to reject the login — the callback fails with the generic error message.
Logout (POST logout)
Section titled “Logout (POST logout)”POST /logout logs the user out of login_guard, invalidates the session, and — when the
provider advertises an end_session_endpoint — redirects there with the session’s
id_token as id_token_hint and the configured post_logout_redirect_uri. If discovery
fails or the provider has no end-session endpoint, the user is simply redirected to /.
If the provider is laravel-oidc, the hint satisfies its
logout threat model — the provider only destroys its session when the
request proves intent.