PhilippKr's avatar

Laravel Sanctum with Angular - CSRF token mismatch

I have got an Angular Frontend and an Laravel Backend with Sanctum.

My Login Component looks like this. When I run it without the Login-Request, everything works and both cookies are done. When I insert the login request, I always got an 419 (unknown status) with the message CSRF token mismatch.

I tried a bunch of ideas out of other threads (http://localhost, localhost, ip-number, with ports, without ports), but unfortunately nothing work.

In advance, thanks for your help :)

Angular Component:

import { Component, Input } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterLink } from '@angular/router';
import axios from 'axios';

axios.defaults.withCredentials = true
axios.defaults.baseURL = 'http://localhost:8000'

@Component({
  selector: 'app-login',
  standalone: true,
  imports: [CommonModule, RouterLink],
  templateUrl: './login.component.html',
  styleUrl: './login.component.scss'
})
export class LoginComponent {

  login(){
    console.log('test');
     
    axios.get('/sanctum/csrf-cookie').then(response => {
      axios.post('/login', {
        email: '[email protected]',
        password: 'password'
      }) 
      .then(response => {
        console.log(response);
      })
      .catch(error => {
        console.log(error.response)
      })
  });
  }

}

Laravel .env

SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=localhost,localhost:4200,localhost:3000,localhost:8000,127.0.0.1,127.0.0.1:8000,::1

Laravel cors.php

'paths' => ['api/*', 'sanctum/csrf-cookie', 'login', 'logout'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

Laravel sanctum.php

'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s',
        'localhost,localhost:4200,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
        Sanctum::currentApplicationUrlWithPort()
    ))),

Laravel session.php

'domain' => env('SESSION_DOMAIN'),

Laravel kernel.php

protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            \Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];
0 likes
3 replies
LaryAI's avatar
Level 58

The CSRF token mismatch error typically occurs when the Laravel backend expects a valid CSRF token for a POST request, but the token is either missing or incorrect. Since you're using Laravel Sanctum, it's important to ensure that the SPA (Single Page Application) is properly configured to handle CSRF protection.

Here's a checklist to help you troubleshoot and resolve the CSRF token mismatch issue:

  1. Ensure CSRF Cookie Request is Made: Before making any POST requests, you should request the CSRF cookie from the /sanctum/csrf-cookie endpoint. It looks like you're already doing this in your Angular component, which is good.

  2. Check SESSION_DOMAIN Configuration: The SESSION_DOMAIN should match the domain from which your Angular application is making requests. If your Angular app is served from a different domain or subdomain, you need to specify it here. For local development, setting it to .localhost might help to include all subdomains.

  3. Verify SANCTUM_STATEFUL_DOMAINS: Make sure that the SANCTUM_STATEFUL_DOMAINS includes the domain and port from which your Angular application is served. If your Angular app is running on localhost:4200, then localhost:4200 should be included in the list.

  4. CORS Configuration: Your CORS configuration seems to be permissive, which is fine for local development. However, ensure that the supports_credentials is set to true to allow cookies to be sent with cross-origin requests.

  5. Middleware Configuration: The EnsureFrontendRequestsAreStateful middleware should be included in your api middleware group, which it is according to your kernel.php. This middleware will attempt to authenticate using session state, which is necessary for CSRF protection to work.

  6. Frontend Axios Configuration: Ensure that withCredentials is set to true for all requests, not just the default configuration. This is necessary for cookies to be included in the request.

  7. Clear Cookies and Cache: Sometimes, old cookies can cause issues. Clear your cookies and cache in the browser and try again.

  8. Check Network Requests: Use your browser's developer tools to inspect the network requests. Make sure that the CSRF token cookie (XSRF-TOKEN) is being sent with the login request.

  9. Check Laravel Logs: If the issue persists, check the Laravel logs for any additional information that might help you debug the problem.

If after going through this checklist the issue still persists, you might want to try the following code snippet in your Angular component to ensure that the CSRF token is being set correctly:

axios.get('/sanctum/csrf-cookie').then(response => {
  // After this request, the XSRF-TOKEN cookie should be set
  axios.post('/login', {
    email: '[email protected]',
    password: 'password'
  }, {
    headers: {
      'X-XSRF-TOKEN': getCookie('XSRF-TOKEN') // Get the CSRF token from the cookie
    }
  }) 
  .then(response => {
    console.log(response);
  })
  .catch(error => {
    console.log(error.response)
  });
});

function getCookie(name) {
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) return parts.pop().split(';').shift();
}

This function getCookie is used to retrieve the CSRF token from the cookies and include it in the headers of the POST request. However, this should not be necessary if withCredentials is set to true and the backend is configured correctly, as Axios should handle this automatically.

PhilippKr's avatar

@LaryAI @all

I tried the suggested steps, but the did not fix the problem. I found the cookies in the header of the login request: Accept: application/json, text/plain, / Accept-Encoding: gzip, deflate, br Accept-Language: de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7 Connection: keep-alive Content-Length: 46 Content-Type: application/json Cookie: XSRF-TOKEN=eyJpdiI6ImYzOEFqUG9RSmVzNU9hYURhT2IxMXc9PSIsInZhbHVlIjoiZlRaSGp5djZWcFFYb0lub2xNWHo5SGNOUkxJWW9nTDRHNEovVDJrK3Q1U0dsTGIwL1ZIVmYyL0ozMHJzSzlPTStqRk1VYk1TLzNqV01PK1l0aThZaythSXZUTkFCRjJzelhMUm05Y2VVVGFzeStzK0Jya01vQUVxamE4ZmhNMzQiLCJtYWMiOiIzMGE5NzBjODhhNGUwMGE4NjdiZGMxODFhNzJlNWVlNTZiNThjZDcxYjQ1YmJjZGMwNjgyYjQyZmY2OTcwNDcxIiwidGFnIjoiIn0%3D; laravel_session=eyJpdiI6IlJ2UmZuajBqSjJ5OEdYbStxSDNnY2c9PSIsInZhbHVlIjoicTRNOTlabENXcXFTeHY5aVozRmdDUUJpZHZ3ZEx5OWlzYS9BVHh2SzJ0aE1DRW9VWnlKQ1FETkJYcFgwQUZwMUZvcnVZVC9oeUxqdUkzUVBWNkl2VEtRbDVtMkZrSmQvenNocERCeDVhWENTRW45Z0Q1SkNSR0lYYkFpblJkWjciLCJtYWMiOiJkOTlhMTBiNTY5YzViZjg3NjY1Y2VhMzFkOWQ3YjYwN2E5OGI1YjI5Mjc1NDJmZjBhNDUxN2Y5OWE4NjMyYWU1IiwidGFnIjoiIn0%3D Host: localhost:8000 Origin: http://localhost:4200 Referer: http://localhost:4200/ Sec-Ch-Ua: "Google Chrome";v="119", "Chromium";v="119", "Not?A_Brand";v="24" Sec-Ch-Ua-Mobile: ?0 Sec-Ch-Ua-Platform: "macOS" Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-site User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36

PhilippKr's avatar
PhilippKr
OP
Best Answer
Level 5

I found the error. I forgot this line in my component

axios.defaults.withXSRFToken = true;
5 likes

Please or to participate in this conversation.