Login & logout
Login is owned by AuthenticatedSessionController, wired through two handlers that share the
auth/login path. Your app fills the view seam with Oidc::loginView(...);
the package owns validation, credential checks, rate limiting, event dispatch, session
regeneration, and the hand-off to the post-login pipeline and
multi-factor challenge.
Routes
Section titled “Routes”| Route name | Verb | Path | Middleware |
|---|---|---|---|
identity.login |
GET |
auth/login |
web, guest:identity |
identity.login.store |
POST |
auth/login |
web, guest:identity, throttle:5,1 |
GET identity.login renders your bound loginView. If no view is bound, hitting the route throws
a RuntimeException.
The login flow (POST identity.login.store)
Section titled “The login flow (POST identity.login.store)”The store action is throttled to 5 requests per minute and runs the following steps:
- Validate. The username field (
config('oidc.auth.username'), defaultemail) andpasswordare bothrequired|string. - Normalize. The username is lowercased before it is used as a credential.
- Verify credentials against the
identityguard’s user provider (retrieveByCredentials+validateCredentials). On failure the request throws aValidationExceptionwith the genericauth.failedmessage. Passwords are rehashed on login whenhashing.rehash_on_loginis enabled. - Record the primary factor. The
pwdauthentication method is recorded (it becomes part of theamrclaim — see Multi-factor). - Run the post-login pipeline. A
LoginEventis dispatched throughOidc::postLogin(...)hooks. If a hook denies the login, the recorded factor is discarded and the request fails with the same genericauth.failedmessage. Queuedid_token/access_tokenclaims from the pipeline are stored on the session. - Branch on MFA (below).
flowchart TD
A["POST identity.login.store"] --> B["Validate + lowercase username"]
B --> C{"Credentials valid?"}
C -- no --> F["Generic auth.failed error"]
C -- yes --> D["Record pwd factor"]
D --> E["Post-login pipeline"]
E -- "deny()" --> F
E -- ok --> G{"Challengeable factor enrolled?"}
G -- "no, but requireMfa()" --> F
G -- no --> H["Log in + regenerate session"]
G -- yes --> I["Stash pending login,<br/>redirect to two-factor challenge"]
I --> J["Verify factor (adds its amr)"]
J --> H
H --> K["Redirect to intended / home"]
Success response
Section titled “Success response”When no challengeable factor is enrolled, the user is logged in on the identity guard (honouring
the remember field), the session is regenerated, and:
- A JSON request (
wantsJson) receives an empty200response. - A browser request is redirected via
redirect()->intended(...)toconfig('oidc.auth.home')(default/dashboard).
Deferring to the two-factor challenge
Section titled “Deferring to the two-factor challenge”After the primary factor succeeds, the package looks up the user’s confirmed, challengeable
enrollments, filtered by config('oidc.auth.two_factor.challenge_providers') (default ['totp']).
- If the post-login pipeline called
requireMfa()but the user has no challengeable factor, the login is denied. - If at least one challengeable enrollment exists, the login is not completed yet. The pending
user id,
rememberflag, and the first enrollment’s provider key and id are stashed on the session, and:- A JSON request receives
{"two_factor": true}. - A browser request is redirected to
identity.two-factor.login.
- A JSON request receives
The user finishes authenticating on the two-factor challenge, which performs
the actual guard->login() and session regeneration once a factor is verified.
Passkey login
Section titled “Passkey login”Passwordless login is delegated to laravel/passkeys through two guest routes:
| Route name | Verb | Path | Middleware |
|---|---|---|---|
identity.passkey.login-options |
GET |
auth/passkeys/login/options |
web, guest:identity, throttle:5,1 |
identity.passkey.login |
POST |
auth/passkeys/login |
web, guest:identity, throttle:5,1 |
The options endpoint returns the WebAuthn assertion challenge; the browser signs it and posts the
credential back to identity.passkey.login to establish the session. Passkey registration (and
its RequirePassword gate) lives on the multi-factor page.
Logout
Section titled “Logout”The auth engine deliberately ships no identity.logout route — the relying party keeps
Laravel’s conventional logout route name for itself. Log the user out of the interactive session
with the identity guard:
use Illuminate\Support\Facades\Auth;
Route::post('/logout', function (Request $request) { Auth::guard(config('oidc.auth.guard'))->logout(); $request->session()->invalidate(); $request->session()->regenerateToken();
return redirect('/');})->name('logout');Terminating the interactive session is separate from OIDC RP-initiated logout at
/oauth/logout, which relying parties use to end the OP session and any downstream sessions. That
endpoint and its CSRF threat model are documented under Logout.