4

I have a Laravel backend and react frontend where I use sanctum for SPA authentication and Axios to make my requests. I am using localhost with my backend on http://localhost:8000 and frontend on http://localhost:3000.

Here is my frontend code:

const apiClient = axios.create({
    baseURL: `http://localhost:8000`,
    withCredentials: true,
});    

let config = {
    headers: {
        'Content-Type': 'application/json'
    },
}

export async function login(credentials: LoginCredentials) {

    return apiClient.get('sanctum/csrf-cookie', config).then(response => {
        apiClient.post('/api/v1/login', JSON.stringify(credentials));
    });
}

my api middleware class looks like this

'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            'throttle:api',
        ],

These are my sanctum configurations;

'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s',
        'localhost,localhost:3000,localhost:3000/api/v1/login,127.0.0.1,127.0.0.1:8000,::1',
        env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
    ))),

SESSION_DOMAIN=http://localhost:3000

And finally my cors middleware

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

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

I am pretty sure i am missing something minor. Any help would me appreciated

isherwood
  • 58,414
  • 16
  • 114
  • 157
Patrick Obafemi
  • 908
  • 2
  • 20
  • 42
  • could you add `Accept` with `application/json` in headers. – Faesal Dec 06 '21 at 15:53
  • You shouldn't need to set the `SESSION_DOMAIN` for `localhost:3000` as it's already included as the default. Also, you should only need to add the domain i.e. don't include the `http://` bit. What response/status (network tab) are you getting back from your `login` request? – Rwd Dec 06 '21 at 16:08
  • @Faesal using accept with application json does not change anything. – Patrick Obafemi Dec 06 '21 at 17:27
  • @Rwd the response status I get is a token mismatch exception – Patrick Obafemi Dec 06 '21 at 17:28
  • remove `\App\Http\Middleware\VerifyCsrfToken::class` from my api middlewares – Faesal Dec 06 '21 at 18:09

3 Answers3

5

Set this in you backend env

SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=localhost
Oliver Tembo
  • 305
  • 3
  • 6
  • I have the same issue and this solves it seamlessly – Arelancelot Oct 18 '22 at 02:33
  • please add some description about why this works. Thank you. – StealthTrails Dec 22 '22 at 06:12
  • 'SESSION_DOMAIN' is an environment variable that establishes which domains the cookie is available to. This value is used in the config/session.php file. 'SANCTUM_STATEFUL_DOMAINS' should also be in the .env file, to specify the domains that will receive stateful API authentication cookies (backend and frontend). – jgarcias Aug 15 '23 at 01:37
-2

because you are using post method .you have two way to fix this problem:

1- use get method

2- go to this path

app/http/middleware/VerifyCsrfToken.php

and set your path inside

protected $except = []

for exception csrf token.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Amir Khaledian
  • 109
  • 1
  • 4
-2

The reason you got this problem is that you need to hit the csrf-cookie Before you can login or register (You will have the same problem when you are sending a post but not logged in.)

await this.$axios({ url: "/v1/sanctum/csrf-cookie" });

This is also mentioned in the docs https://laravel.com/docs/8.x/sanctum#csrf-protection

You can find the csrf-cookie route using php artisan route:list

You can also set your own csrf-cookie route in config/santrum 'prefix' => 'api/v1/sanctum'

John
  • 29
  • 1
  • 7