angularlaravelcookiestokencsrf-token

Laravel 12 - Angular 20 - Sanctum SPA - CSRF Token mismatch


Im trying to build a login and create user with CSRF Tokens and it wont run i tried for 3 days now and it wont work because of CSRF token mismatch. i think im missing something small or idk if its a huge mistake..

I tried to edit following files:
cors.php sanctum.php session.php app.php app.config.ts .env

It still wont work..

Here is my code: Register function in frontend

import { CommonModule } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';

@Component({
  selector: 'app-user-management',
  imports: [CommonModule, ReactiveFormsModule, FontAwesomeModule],
  templateUrl: './user-management.html',
  styleUrl: './user-management.scss'
})
export class UserManagement {
  showPassword = false;
  eyeAnimate = false;
  pulseEye = false;
  private timer: any;
  private pulseTimer: any;

  username = new FormControl('');
  email = new FormControl('');
  password = new FormControl('');
  remember = new FormControl(false);

  constructor(
    private http: HttpClient
  ) {}

    register(): void {
    this.http.get('http://localhost:8000/sanctum/csrf-cookie', { withCredentials: true })
      .subscribe(() => {
        this.http.post('http://localhost:8000/register', {
          username: this.username.value ?? '',
          email: this.username.value ?? '',
          password: this.password.value ?? '',
          password_confirmation: this.password.value ?? ''
        }, { 
          withCredentials: true,
          headers: { 'Accept': 'application/json' }
        })
        .subscribe({
          next: (res) => {
            console.log('Registrierung erfolgreich', res);
          },
          error: (err) => {
            console.error('Registrierung fehlgeschlagen: ', err);
          }
        });
      });
  }

In backend ( AuthController.php )


public function register(Request $request) {
        $request->validate([
            'username' => 'required|string|unique:users',
            'email' => 'required|email|unique:users',
            'password' => 'required|string|min:8|confirmed',
        ]);
        $user = User::create([
            'username' => $request->username,
            'email' => $request->email,
            'password' => bcrypt($request->password),
        ]);
        Auth::login($user);
        return response()->json($user);
    }

app.config.ts

import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
import { provideHttpClient, withXsrfConfiguration, withFetch,  } from '@angular/common/http';


export const appConfig: ApplicationConfig = {
  providers: [
    provideBrowserGlobalErrorListeners(),
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideRouter(routes), provideClientHydration(withEventReplay()),
    provideHttpClient(
      withFetch(),
      withXsrfConfiguration({
        cookieName: 'XSRF-TOKEN',
        headerName: 'X-XSRF-TOKEN'
      })
    )
  ]
};

sanctum.php

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

app.php

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
        api: __DIR__.'/../routes/api.php',
    )
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->statefulApi();
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();

cors.php

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

    'allowed_methods' => ['*'],

    'allowed_origins' => ['http://localhost:4200', 'http://localhost:8000'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

web.php ( routes )

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;

Route::post('/login', [AuthController::class, 'login']);
Route::post('/logout', [AuthController::class, 'logout']);
Route::post('/register', [AuthController::class, 'register']);

.env

SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=true
SESSION_PATH=/
SESSION_DOMAIN=
# SANCTUM_STATEFUL_DOMAINS=localhost:4200,localhost:8000,localhost:3000

SESSION_SAME_SITE=lax
SESSION_SECURE_COOKIE=false

Solution

  • Fixed it, Problem was that the XSRF token Header was NOT set. i had to do it manually.

    EDIT:

    Fixed it with adding:

    this.http.post(environment.apiURL + '/login', userData, { 
              withCredentials: true,
              headers: { 'X-XSRF-TOKEN': this.XsrfHelper.getXsrfTokenFromCookie() ?? '' }
    })
    
    public getXsrfTokenFromCookie(): string | null {
        if (typeof document === 'undefined') return null;  // This prevents errors during server-side rendering or testing environments where 'document' is not available.
    
        const match = document.cookie.match(new RegExp('(^| )XSRF-TOKEN=([^;]+)')); // Try to find the "XSRF-TOKEN" cookie in document.cookie
        return match ? decodeURIComponent(match[2]) : null;
      }