Skip to content

Laravel authentication and API security

Status: Complete. Last reviewed 2026-08-27.

Authentication establishes which principal a credential represents for this request. It does not establish that the principal may perform an operation, owns a resource, or belongs to the active tenant. Laravel separates authentication into guards, which define how a request maintains or extracts identity, and providers, which retrieve the authenticatable user. A session guard and a token guard can use the same Eloquent provider while trusting different credentials.

The architectural choice is therefore not “cookies or APIs.” It is which party issues the credential, which clients may present it, whether browsers attach it automatically, how long it remains replayable, and where revocation is enforced. Session authentication, Sanctum personal access tokens, Passport OAuth2 access tokens, and OpenID Connect identity assertions solve different trust problems.

Authorization, policies, denial semantics, and tenant isolation are owned by Validation, authorization, policies, and tenant boundaries. Generic XSS, CORS, storage, and threat-model guidance remains in Application security.

Guards, providers, and the session lifecycle

Section titled “Guards, providers, and the session lifecycle”

On a conventional browser login, Auth::attempt() asks the configured provider for a user matching non-password credentials and validates the submitted password against the stored adaptive hash. The application should regenerate the session after success to prevent session fixation. Later requests present the session cookie; the session guard recovers the stored user identifier and the provider retrieves the current user. The password itself is not stored in the session.

A guard name is part of the contract. auth uses the default guard; auth:admin or another explicit guard selects different configuration. Code that calls Auth::user() without considering the active guard can return the wrong type or no principal in multi-guard applications. Guard names are not roles, and adding an “admin guard” does not authorize administrative abilities.

Use Hash::make() and Hash::check() rather than encryption for passwords. Hash::needsRehash() supports upgrading cost or algorithm on a later successful login. Laravel can reject hashes created with a different configured algorithm unless verification settings deliberately permit migration; make algorithm changes a tested rollout rather than a silent configuration edit.

Logout should call the guard’s logout operation, invalidate the session, and regenerate the CSRF token. Invalidating only the browser cookie may leave server-side session state usable if the identifier was stolen. “Remember me” creates a longer-lived recaller credential backed by the model’s remember token, so its theft window and logout/revocation behavior deserve explicit tests. Sensitive actions can require recent password confirmation even within an authenticated session.

Browsers automatically attach matching cookies, which is convenient and creates CSRF exposure. Laravel’s CSRF middleware compares a request token with session state for state-changing web requests. Secure, HttpOnly, and an appropriate SameSite policy reduce transport, script-access, and cross-site attachment risk; none substitutes for correct CSRF validation. XSS within the origin can often perform authenticated actions even when an HttpOnly cookie cannot be read.

A bearer token in the Authorization header is not normally attached by the browser automatically, so it changes the CSRF threat. It remains a bearer credential: anyone who obtains it can replay it until expiry or revocation. Storing it in JavaScript-readable browser storage exposes it to XSS theft. Putting it in a cookie makes browser attachment—and therefore CSRF reasoning—relevant again.

CORS controls which browser origins may read responses and send credentialed cross-origin requests. It is not server authorization and does not stop curl, malware, or another backend from calling the endpoint. Require TLS, redact credentials from logs and error reports, bound request sizes, rate-limit credential verification and token issuance, and avoid placing tokens in URLs where histories and referrers expose them.

For a first-party SPA sharing the application’s top-level domain, Sanctum uses Laravel’s session cookie rather than an API token. The application enables Sanctum’s stateful API middleware, configures stateful hosts and cookie domains, allows credentialed CORS where needed, and initializes CSRF protection through /sanctum/csrf-cookie before login. Requests to routes protected by auth:sanctum can then authenticate through the session guard. This preserves HttpOnly cookie storage, session expiry, and CSRF protection.

Sanctum also issues personal access tokens for mobile clients, scripts, and simple first-party integrations. Laravel stores a hash of the token and returns the plain token only when it is created. Abilities are attached to the token and checked with middleware or tokenCan(). By design, tokenCan() returns true for first-party SPA requests, so a policy must still authorize the user and resource. Token abilities are a capability ceiling, not ownership proof.

Personal tokens do not expire by default unless Sanctum configuration or a per-token expiry supplies a deadline. Store a device/purpose name, issue the minimum abilities, show last use, and support revocation of one token or all tokens. Prune expired records and decide whether password reset, role change, tenant removal, or suspected compromise revokes existing tokens. The focused Sanctum boundary example combines ability checks with a policy instead of treating either as sufficient alone.

Passport turns the application into an OAuth2 authorization server. Use it when independently operated clients need delegated access, authorization-code flows, refresh tokens, client credentials, scopes, consent, and standards-compatible token endpoints. It adds cryptographic keys, client registration, grant policy, token persistence, revocation, operational monitoring, and a larger attack surface. It is unnecessary for a first-party SPA that can use a session or a script that needs one simple personal token.

For public clients that cannot protect a client secret, use authorization code with PKCE. Avoid creating new password or implicit-grant designs; current OAuth security guidance favors redirects, short-lived access tokens, PKCE, refresh-token protection, exact redirect matching, and sender-constrained mechanisms where warranted. Client credentials represent the client application, not a human user, so policies must not invent a user identity for that token.

OAuth2 delegates authorization to access protected resources. It does not by itself define an authentication statement for “log in with provider.” OpenID Connect adds an identity layer, including an ID token and discovery/user-information conventions. A resource API validates access tokens for audience, issuer, expiry, signature or introspection, and scope; it should not accept an ID token merely because both are JWT-shaped. If the requirement is workforce SSO or social login, consuming an established OIDC provider is often safer than operating a new authorization server.

Email verification proves control of an address at the time the signed verification link is used; it does not prove a legal identity or current mailbox security. Protect routes with both authentication and verification when the feature requires it, and rate-limit resending. Password reset uses Laravel’s broker to create a time-limited, single-purpose token and locate the user without exposing whether an address exists through response wording or materially different timing.

A successful reset should hash the new password, rotate remember state, and apply an explicit session/token revocation policy. Laravel cannot infer whether the product should preserve trusted devices. High-risk systems commonly revoke other sessions and personal/refresh tokens, notify the account owner, and record security telemetry. Recovery is an alternative authentication path and must not be weaker than login.

Laravel Fortify and current starter-kit features can provide TOTP-based two-factor authentication and recovery codes. Encrypt or otherwise strongly protect MFA secrets, hash or safely store one-time recovery material, show recovery codes once, and regenerate them after use or disclosure. Require recent authentication for enrolling, disabling, or replacing factors. Support staff should follow an auditable recovery procedure rather than bypassing MFA with an unlogged database edit.

auth or auth:sanctum middleware establishes a principal or returns 401. Token ability middleware can reject credentials lacking the advertised capability. A policy then decides whether that principal may act on the concrete resource, and tenant-scoped lookup prevents cross-tenant resolution. These checks are cumulative:

  1. the credential is valid and maps to the intended principal;
  2. the token is allowed to request this class of operation;
  3. the actor is authorized for this resource and action;
  4. the resource belongs to the trusted tenant context;
  5. domain invariants still permit the transition.

Returning 403 for a token with insufficient ability does not prove object authorization happened. Conversely, a broad token ability cannot expand what the user may do. Test a matrix of session and token credentials, expired and revoked tokens, missing abilities, foreign resources, tenant removal, and role changes. Assert no writes or side effects on denial.

Failure timeline: the “read-only” token writes across tenants

Section titled “Failure timeline: the “read-only” token writes across tenants”
  1. A user creates a Sanctum token with projects:read.
  2. An API route uses only auth:sanctum; it never applies ability middleware.
  3. The controller accepts a project ID and updates it without a policy or tenant-scoped lookup.
  4. The valid token modifies another tenant’s project even though its label says read-only.
  5. Revoking the token stops future use but cannot undo the write.
  6. The repair enforces the ability, authorizes the resolved tenant-owned model, derives tenant context from trusted membership, logs the denial safely, and adds tests for both a missing ability and a foreign identifier.

Current (Laravel 13): session guards and user providers remain the authentication foundation; Sanctum supports stateful first-party SPA authentication and hashed personal access tokens with optional expiry; Passport supplies a full OAuth2 server; password confirmation, email verification, password brokers, and starter-kit/Fortify MFA integrate with the same authenticatable user model.

Common (Laravel 11–12): these mechanisms are substantially familiar, though middleware registration, starter-kit organization, and package configuration differ. Many maintained applications use Sanctum for both SPA sessions and personal tokens without introducing Passport.

Legacy: older APIs may store unhashed custom tokens, use perpetual broad tokens, adopt OAuth password grants, skip session regeneration, or conflate an admin guard with authorization. Inventory credential formats and clients first; add telemetry and compatibility tests, then migrate with overlapping verification, explicit expiries, scoped abilities, revocation, and client communication.