1

Problem

I am struggling with annoying 419 error trying to logout. The login and register are working fine. My api and spa are on the same top-level domain, but different ports

api - localhost:8000

spa (vue) - localhost:5173

After hours of debugging and changing the different settings I still get the 419

Code

backend:

env

APP_URL=http://localhost:8000
FRONTEND_URL=http://localhost:5173
SANCTUM_STATEFUL_DOMAINS=localhost:5173
SESSION_DOMAIN=localhost

bootstrap/app.php

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
        apiPrefix: 'api/v1',
    )
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->alias([
            'role' => RoleMiddleware::class,
        ]);

        $middleware->api(prepend: [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
        ]);

        $middleware->statefulApi();
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();

cors

'paths' => ['*'],

    'allowed_methods' => ['*'],

    'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:3000')],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

sanctum

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

authentication routes in routes/web.php

Route::post('login', LoginController::class);
Route::post('register', RegisterController::class);
Route::post('logout', LogoutController::class);

LoginController.php

    public function __invoke(LoginRequest $request)
    {
        if (Auth::attempt($request->validated())){
            $request->session()->regenerate();

            return (new UserResource(Auth::user()));
        }else{
            return response()->json([
                'message' => 'The provided credentials are incorrect'
            ], 422);
        }
    }

LogoutController.php

public function __invoke(Request $request)
    {
        Auth::logout();

        $request->session()->invalidate();

        $request->session()->regenerateToken();

        return response()->json([
           'message' => 'Logged out'
        ]);
    }

frontend:

axios.js

import axios from 'axios'
import router from '@/router/index.js'

const axiosClient = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  withCredentials: true,
  withXSRFToken: true,
})

axiosClient.interceptors.response.use( (response) =>{
  return response;
}, error => {
  if (error.response && error.response.status === 401){
    router.push({name: 'Login'})
  }

  throw error;
})

export default axiosClient

env

VITE_API_BASE_URL=http://localhost:8000

logout function

function logout() {
  axiosClient.post('/logout')
    .then((response) => {
      router.push({name: 'Login'})
    })
}

login function

const data = ref({
  email: '',
  password: '',
})

const errorMessage = ref('')

function submit() {
  axiosClient.get('/sanctum/csrf-cookie').then(response => {
    axiosClient.post('/login', data.value)
      .then(response => {
        router.push({name: 'Home'})
      })
      .catch(error => {
        console.log(error.response)
        errorMessage.value = error.response.data.message;
      })
  });
}

How the process to get 419 (unknown status) looks like:

  • Log in (success preflight request & actual login request)
  • success logout preflight request
  • see the 419 (unknown status) error in actual logout request
  • check cookies are set in the logout request(Cookie & X-XSRF-TOKEN headers are set)

Would be grateful for your help

Best regards

2 Answers 2

2

You would still need to call the /sanctum/csrf-cookie as your /logout endpoint is a POST request.

All of laravel's POST,PUT, PATCH, DELETE requests require you to have the csrf cookie as mentioned here in the docs.

Sign up to request clarification or add additional context in comments.

1 Comment

After some research I noticed I don't need to call /sanctum/csrf-cookie in logout
0

I finally noticed where I had a mistake

My session table didn't have an user_id because I was using UUID for my users, and session table wasn't properly configured for such UUID

Fixed session migration (notice that session table included in the create_users_table migration in laravel 12)

Schema::create('sessions', function (Blueprint $table) {
    $table->string('id')->primary();
    $table->foreignUuid('user_id')->nullable()->index(); // fixed line to use UUID
    $table->string('ip_address', 45)->nullable();
    $table->text('user_agent')->nullable();
    $table->longText('payload');
    $table->integer('last_activity')->index();
});

EDIT
I also wanna to leave the important configuration places you need to check if you're getting errors


Config settings to check:

backend

1. Check your SPA (e.g. VueJS app) and API (e.g. Laravel) are on the same top level domain
For example:
api - localhost:8000
spa - localhost:5173
(notice: localhost = 127.0.0.1:8000 is a top-level domain)

2. Ensure to properly configure .env

APP_URL=http://localhost:8000
FRONTEND_URL=http://localhost:5173
SANCTUM_STATEFUL_DOMAINS=localhost:5173
SESSION_DOMAIN=localhost

3. In your bootstrap/app.php in the withMiddleware

$middleware->statefulApi(); 

4. Ensure config/cors.php is properly configured

Notice: If you don't have this file run: php artisan config:publish cors

    'paths' => ['*'], // optionally config this line (see usual config below)

    'allowed_methods' => ['*'], // EXAMINE THIS LINE 

    'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:3000')], // v

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],
    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true, // AND EXAMINE THIS LINE (throws CORS error if false)

usually you can see this kind of config for paths in config/cors.php

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

5. Session and SESSION_DRIVER
In your .env file check SESSION_DRIVER=database (it's a default value and you don't need to touch it)

Check user_id is set after registration/login in your sessions table

Reminder:

- You don't need to call the /sanctum/csrf-cookie in logout, because after yout logged in or registered the frontend should already have the token
- Authentication routes (login, register, logout) should be in routes/api.php not routes/web.php


frontend

1. Check your axiosClient (if you're using one, or chec your axios are using proper settings)

Example of my src/axios.js

import axios from 'axios'
import router from '@/router/index.js'

const axiosClient = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL, // check how to configure this line below
  withCredentials: true, // THIS LINE IS IMPORTANT
  withXSRFToken: true, // THIS LINE IS ALSO IMPORTANT
})

axiosClient.interceptors.response.use( (response) =>{
  return response;
}, error => {
  if (error.response && error.response.status === 401){
    router.push({name: 'Login'})
  }

  throw error;
})

export default axiosClient

in your .env (notice: its a SPA's .env file, not an API's) check you have your API base URL, for example

VITE_API_BASE_URL=http://localhost:8000

2. Check your routes are sending the GET /sanctum/csrf-cookie request before **POST, PUT, PATCH, DELETE

example: login request

const form = reactive({
  email: '',
  password: '',
})

const errorMessage = ref('')

function submit() {
  // THIS LINE IS IMPORTANT
  axiosClient.get('/sanctum/csrf-cookie').then(response => {
    axiosClient.post('/login', form)
      .then(response => {
        router.push({name: 'Home'})
      })
      .catch(error => {
        errorMessage.value = error.response.data.message;
      })
  });
}

Reminder:

- You shouldn't call GET /sanctum/csrf-cookie before EVERY POST, PUT, PATCH, DELETE requests

Remember this: You only need to call the endpoint once at the start of the session.

Hope it'll provide clear understanding where you can find your error

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.