How does choosing Livewire or Inertia versus a separate SPA or a mobile client change the way a Laravel app authenticates users?
answer
- same origin means session cookies
- web middleware group and CSRF
- Sanctum statefulApi for a first-party SPA
- same top-level domain requirement
- mobile means bearer tokens
basics
~20 sBlade, Livewire and Inertia pages are served by Laravel itself, so they use ordinary session cookies and CSRF protection from the web middleware group. A separate SPA can still use sessions through Sanctum if it shares the top-level domain; a mobile app needs API tokens.
solid answer
~40 sLivewire and Inertia run inside pages Laravel serves, so every request carries Laravel's session cookie through the `web` middleware group: the `auth` middleware, CSRF protection and `Auth::user()` work as in any Blade app, and there is no token to store. A **separate first-party SPA** changes that: it calls an API, so you add Sanctum (`php artisan install:api`) and let it authenticate with session cookies by calling `$middleware->statefulApi()` in `bootstrap/app.php`; the SPA fetches `/sanctum/csrf-cookie` before logging in and must share the API's top-level domain. A **mobile app** or third-party client cannot rely on browser cookies, so it gets Sanctum API tokens sent as `Authorization: Bearer`. Adding a mobile client later therefore adds a token-authenticated API regardless of the web UI you chose.
code
php · 15 lines<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
)
->withMiddleware(function (Middleware $middleware): void {
// first-party SPA on the same top-level domain: session cookies on API routes
$middleware->statefulApi();
})
->create();go deeper
Know that Blade, Livewire and Inertia use normal session login, while mobile apps use API tokens.
Explain why same-origin pages get sessions for free, and what Sanctum's statefulApi and csrf-cookie steps do for a separate SPA.
Show the architectural consequence: a mobile client adds a token API regardless, so it does not force the web UI into a separate SPA.
Weigh one auth model for all clients against the simplest model per client, including domain layout decisions made early.
## Why the UI choice reaches authentication Authentication in a Laravel web app normally rides on the **session**: after login, the browser holds an encrypted session cookie, and every request through the `web` middleware group starts the session, checks CSRF tokens and makes `Auth::user()` available. That works whenever **Laravel serves the page and the browser talks to the same origin**. The frontend approach decides whether that stays true. ## Server-driven and Inertia apps: plain sessions | Approach | How requests reach Laravel | Auth mechanism | |---|---|---| | Blade-only | full page loads and form posts | session cookie, CSRF token in forms | | Livewire | page loads plus Livewire's update requests from the same page | session cookie; Livewire sends the CSRF token with each update | | Inertia | first load as HTML, then XHR visits from the same origin | session cookie; CSRF through the `XSRF-TOKEN` cookie mechanism | In all three, the login flow is the ordinary web one — in the official starter kits it is **Fortify** posting to `/login` — and routes are protected with `auth` middleware. There is no API, no token storage in JavaScript and no CORS configuration, because nothing leaves the origin. ## A separate first-party SPA: sessions through Sanctum A standalone SPA (its own repository, calling Laravel over JSON) no longer gets the `web` group on its API calls by default. Laravel's answer for first-party SPAs is **Sanctum's cookie-based SPA authentication**: 1. Install the API scaffolding and Sanctum with `php artisan install:api`. 2. In `bootstrap/app.php`, call `$middleware->statefulApi()` so requests from your SPA's domains get session cookies and CSRF protection on API routes, while other clients can still use tokens. 3. Before logging in, the SPA requests `/sanctum/csrf-cookie`; afterwards its HTTP client echoes the `XSRF-TOKEN` cookie in an `X-XSRF-TOKEN` header. 4. The SPA and the API **must share the same top-level domain** (different subdomains are fine), and requests should send `Accept: application/json` plus `Origin` or `Referer`. The benefit is that the SPA still never stores a long-lived token in JavaScript. The cost is configuration across two deployments: stateful domains, CORS with credentials, cookie domain settings. ## Mobile and third-party clients: tokens A native mobile app cannot rely on the browser's cookie jar, and a third party should never share your session. Sanctum **API tokens** fill this role: a login endpoint issues a plain-text token once, the client stores it (on the device), and sends it as `Authorization: Bearer <token>`; tokens can carry **abilities** that limit what they may do. ## The consequence for architecture - Choosing **Livewire or Inertia** keeps authentication simple *for the web*. - Needing a **mobile app** adds a token-authenticated API **whatever** the web UI is — so "we will have a mobile app one day" is not, by itself, a reason to build the web front end as a separate SPA. - Choosing a **separate SPA** for the web means taking on Sanctum's SPA setup and its same-top-level-domain constraint, or accepting tokens in the browser. ## Where the shared logic lives Whatever the transport, the **authorization** logic should be shared: policies, gates and form requests behave the same whether a controller returns `Inertia::render()`, a Blade view or an API resource. What differs per client is only **how the user is identified** — a session for same-origin pages and first-party SPAs, a token for mobile and third-party clients. Designing controllers or actions so both a web route and an API route can call the same code keeps a later mobile client from duplicating business rules. | Client | Identified by | Protected by | |---|---|---| | Blade / Livewire / Inertia page | session cookie | `auth` middleware on web routes, CSRF | | First-party SPA via Sanctum | session cookie on API routes | `auth:sanctum`, CSRF via `XSRF-TOKEN` | | Mobile or third-party client | Bearer token | `auth:sanctum`, token abilities | ## Pitfalls - Serving the SPA from an unrelated domain and wondering why session auth fails — the top-level domain must match. - Storing API tokens in `localStorage` for a first-party SPA when cookie-based SPA auth was available. - Assuming Inertia requests are "API calls" that need tokens — they are same-origin requests with the session.
- Your Inertia web app now needs a mobile companion app. What do you add, and what stays the same?You add JSON endpoints in `routes/api.php` authenticated with Sanctum API tokens, which the mobile app sends as a Bearer header. The Inertia web app keeps its session-based `web` routes unchanged; both call the same models, policies and actions, so the business logic is shared while the transport and auth differ.
- Why must a Sanctum-authenticated SPA share the API's top-level domain?Cookie-based SPA authentication relies on the browser sending Laravel's session and XSRF cookies with each API request. Browsers only attach those cookies within the domain they were set for, so an SPA on an unrelated domain cannot carry the session; subdomains of the same top-level domain can.
saying these in an interview costs you the question
- Inertia pages need API tokens because they fetch data with XHR
- A separate SPA can use Sanctum session auth from any domain
- Planning a mobile app means the web UI must also be a separate SPA
- Livewire requests skip CSRF protection because they are AJAX
- Mobile apps should authenticate with Laravel's session cookie