Login & logout
Login is owned by AuthenticatedSessionController, wired through two handlers that share the
auth/login path. Your app fills the LoginView seam by binding the
contract; 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 through the bound LoginView contract. If none is bound, hitting the
route throws MissingAuthViewException.
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 (honoring
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.