Skip to content

CORS & Sanctum (SPA authentication) ​

ExchangePro uses Laravel Sanctum in stateful (cookie) mode. The Nuxt app and API must be configured so cookies and CSRF tokens work across origins.

How authentication works ​

mermaid
sequenceDiagram
  participant Browser
  participant Nuxt
  participant API as Laravel API

  Browser->>Nuxt: Open /signin
  Nuxt->>API: GET /sanctum/csrf-cookie
  API-->>Browser: Set XSRF-TOKEN cookie
  Nuxt->>API: POST /api/auth/signin (with X-XSRF-TOKEN)
  API-->>Browser: Set session cookie
  Nuxt->>API: GET /api/user (authenticated)
  1. Frontend fetches CSRF cookie from {API_URL}/sanctum/csrf-cookie.
  2. Login POST includes X-XSRF-TOKEN header (must match the XSRF-TOKEN cookie).
  3. Laravel session cookie is stored for the shared cookie domain.
  4. Subsequent API calls send cookies (credentials: include).

CORS configuration ​

Origins are configured in backend/.env, not hardcoded in config/cors.php.

By default, FRONTEND_URL is the only allowed origin. For multiple frontends (e.g. www + apex, or local + LAN), set:

ini
FRONTEND_URL=http://localhost:4000

# Optional - full URLs with scheme, comma-separated
CORS_ALLOWED_ORIGINS=http://localhost:4000,http://127.0.0.1:4000,https://yourdomain.com,https://www.yourdomain.com

If CORS_ALLOWED_ORIGINS is omitted, Laravel uses FRONTEND_URL only.

Rules ​

  • Each entry must be a full origin: scheme + host + port (e.g. https://yourdomain.com).
  • Do not use * when supports_credentials is true.
  • Match the URL users type in the browser (including www if you use it).

After changing .env, clear config cache:

bash
php artisan config:clear
php artisan config:cache

Session cookies (cross-subdomain setup) ​

When the API and Nuxt site run on different subdomains (recommended production layout), you must share session and CSRF cookies across the root domain.

URLRole
https://exchangepro.xorinlab.comNuxt frontend
https://api-exchangepro.xorinlab.comLaravel API

Backend .env (production):

ini
APP_URL=https://api-exchangepro.xorinlab.com
FRONTEND_URL=https://exchangepro.xorinlab.com

SESSION_DOMAIN=.xorinlab.com
SESSION_SECURE_COOKIE=true
SESSION_SAME_SITE=lax

Replace xorinlab.com with your own root domain (leading dot is required).

Why SESSION_DOMAIN matters ​

Without it, cookies are scoped only to api.yourdomain.com. The frontend JavaScript on yourdomain.com cannot read the XSRF-TOKEN cookie, so login POSTs fail with 419 CSRF token mismatch even though CORS headers look correct.

After changing session settings:

  1. Run php artisan config:cache on the server.
  2. Clear browser cookies for your domain (or use a private window).
  3. Retry sign-in - confirm GET /sanctum/csrf-cookie runs before POST /api/auth/signin.

Local development ​

Leave session domain unset for localhost:

ini
SESSION_DOMAIN=null
SESSION_SECURE_COOKIE=
SESSION_SAME_SITE=lax

On localhost, cookies for localhost (no port in the cookie domain) are usually shared between :4000 (Nuxt) and :8000 (API).

Sanctum stateful domains ​

File: backend/config/sanctum.php

Stateful domains are built automatically from:

  • Hardcoded local dev hosts (localhost, localhost:4000, 127.0.0.1, etc.)
  • The host (and port, if any) parsed from FRONTEND_URL in .env

Example: FRONTEND_URL=https://exchangepro.xorinlab.com adds exchangepro.xorinlab.com to the stateful list.

You normally do not need a separate env variable - set FRONTEND_URL correctly and run php artisan config:cache.

Same-site deployment tips ​

SetupCookie notes
API api.domain.com + app domain.comRequired: SESSION_DOMAIN=.domain.com, SESSION_SECURE_COOKIE=true
Same origin /api reverse proxySimplest cookie behavior; SESSION_DOMAIN can stay null
Different registrable domains entirelyCookie auth is unreliable; consider same root domain or a reverse proxy

Admin API middleware ​

Admin routes use:

text
middleware: auth:sanctum, admin, lastSeen

The admin middleware checks that the logged-in user has role = admin (works with Sanctum cookie sessions).

If admin requests return 401/403, see Troubleshooting.

HTTPS requirement ​

Production must use HTTPS on both frontend and API so browsers accept Secure cookies when SESSION_SECURE_COOKIE=true.

Quick test ​

bash
# CSRF cookie - check Set-Cookie includes Domain=.yourdomain.com
curl -I -c cookies.txt \
  -H "Origin: https://exchangepro.xorinlab.com" \
  https://api-exchangepro.xorinlab.com/sanctum/csrf-cookie

# Login (replace token from XSRF-TOKEN cookie)
curl -c cookies.txt -b cookies.txt \
  -H "Origin: https://exchangepro.xorinlab.com" \
  -H "X-XSRF-TOKEN: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@example.com","password":"secret"}' \
  https://api-exchangepro.xorinlab.com/api/auth/signin

Browser-based login via the UI is the recommended test.

Need help? support@xorinlab.com · Website: xorinlab.com