JoaoHamerski's avatar

Laravel Sanctum CORS issue

Honestly i'm loosing my mind with that, i already tried eveything on the Internet, i keep getting CORS error when trying to get CSRF-TOKEN on localhost

Laravel App: localhost:8000

Vue App: localhost:8080

I also changed sanctum prefix URLs with 'prefix' => 'api', on sanctum.php config so instead of /sanctum/csrf-cookie the url is /api/csrf-cookie

(i already confirmed via php artisan route:list and its working fine)

What i already tried

Add \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, to cors.php as said on documentation:

My "cors.php' file

    protected $middleware = [
        // \App\Http\Middleware\TrustHosts::class,
        \Fruitcake\Cors\HandleCors::class,
        \App\Http\Middleware\TrustProxies::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \App\Http\Cors::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];

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

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

The \App\Http\Cors::class is a custom class i created to try to handle CORS:

    public function handle(Request $request, Closure $next)
    {
        return $next($request)->header('Access-Control-Allow-Origin', '*')
            ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE,
     OPTIONS')
            ->header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-
     Type, Accept, Authorization');
    }

Tried add some .env variables:

SESSION_DOMAIN=localhost
[and]
SESSION_DOMAIN=.localhost

SANCTUM_STATEFUL_DOMAINS=localhost
[and]
SANCTUM_STATEFUL_DOMAINS=.localhost
[and]
SANCTUM_STATEFUL_DOMAINS=localhost:8080
[and]
SANCTUM_STATEFUL_DOMAINS=http://localhost,http://localhost:8080,localhost:8080,localhost

My cors.php file

<?php

return [
    'paths' => [
        '*',
        'api/*',
        'login',
        'logout',
    ],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

];

FRONTEND

In frontend i just created a axios instance:

export const authClient = axios.create({
  baseURL: process.env.VUE_APP_API_URL // localhost:8000,
  withCredentials: true
})

authClient.interceptors.response.use(
  response => response,
  error => {
    if (
      error.response
      && [401, 419].includes(error.response.status)
      && store.getters('auth/authUser')
      && !store.getters('auth/guest')
    ) {
      store.dispatch('auth/logout')
    }

    return Promise.reject(error)
  })

And try to get the token:

 async login (payload) {
    await authClient.get('/api/csrf-cookie')
    // return authClient.post('/login', payload)
  },

This is enough to get the error:

Access to XMLHttpRequest at 'localhost:8000/api/csrf-cookie' from origin 'http://localhost:8080' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, brave, chrome-untrusted, https.

0 likes
17 replies
jlrdw's avatar

Have you tried same domain? Just suggestion.

JoaoHamerski's avatar

@jlrdw I need to test on localhost, what do you mean, a local domain on /etc/hosts?

jlrdw's avatar

@JoaoHamerski see this note in sanctum chapter:

In order to authenticate, your SPA and API must share the same top-level domain. However, they may be placed on different subdomains. Additionally, you should ensure that you send the Accept: application/json header with your request.

I also suggest A google search like

site:laracasts.com Laravel Sanctum CORS issue

or

site:laracasts.com Laravel Sanctum CORS API issue

That gets laracasts search results, you could also try stackoverflow. I have seen so many cors issues come up lately on Sanctum.

Just suggestions.

JoaoHamerski's avatar

@jlrdw But since i configured my cors.php file to accept any URL, why Laravel still give me this errors?

cors.php Comes by default with 'allowed_origins' => ['*'], plus i add on paths:

    'paths' => [
        '*',
        'api/*',
        'login',
        'logout',
    ],

Does it make sense?

JoaoHamerski's avatar

@jlrdw @jlrdw I just added to my headers application/json same error again.

export const authClient = axios.create({
  baseURL: process.env.VUE_APP_API_URL,
  withCredentials: true,
  headers: { Accept: 'application/json' }
})
` ` 
jlrdw's avatar

You probably need more like:

laravel.test:8000
front.test:8000

Or something with same 8000.

JoaoHamerski's avatar

@jlrdw I tried now but it doesnt seems to work I add to my /etc/hosts two entries:

127.0.0.1       backend.test
127.0.0.1       frontend.test

It works fine for both, but when i try to run Laravel serve on the same port, even its different subdomain it says

Failed to listen on backend.test:8001 (reason: Address already in use)

jlrdw's avatar

@JoaoHamerski Why does your back and front need to be on different domains? Normally you could have:

somesite.com
somesite.com/front

Then use like somesite.com and front.somesite.com

But see the APache docs if using apache or nginx docs for nginx.

JoaoHamerski's avatar

@apex1 Its VUE_APP_API_URL=localhost:8000 I already console.log it and it returns correctly, but i tried to hardcoded too, same issue persist.

JoaoHamerski's avatar

@apex1 You are right, i just realized now it need to be "http://" before the domain, that's why i was getting CORS errors, i didnt found it in any place:

The correct config should be:

VUE_APP_API_URL=http://localhost:8000

or

export const authClient = axios.create({
  //baseURL: 'http://localhost:8000',
  baseURL: process.env.VUE_APP_API_URL,
  withCredentials: true,
  headers: { Accept: 'application/json' }
})
JoaoHamerski's avatar

@azimidev If you tried everything i mentioned, make sure your API URL has http:// before the URL

Instead of:

localhost:8000

It has to be:

http://localhost:8000
1 like
kachi_dk's avatar

For anyone visiting this recently.

Vendor packages also use the .env (SESSION_DOMAIN and SANCTUM_STATEFUL_DOMAINS), so sometimes there is weird behavior.

Remove these from the .env if present

# SESSION_DOMAIN=
# SANCTUM_STATEFUL_DOMAINS=

Add these to the .env. Make sure the URL is in full (scheme, domain and port (when in development))

APP_URL=http://localhost:8000

FRONTEND_URLS=http://localhost:5173,http://localhost:5174,http://localhost:5175,http://localhost:5176

Add these to config/cors.php

return [
    'paths' => ['*'],

    'allowed_methods' => ['*'],

    'allowed_origins' => explode(',', env('FRONTEND_URLS')),

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,
]

Add these to config/sanctum.php

return [
...
'stateful' => explode(
        ',',
        env(
            'SANCTUM_STATEFUL_DOMAINS',
            sprintf(
                '%s%s%s',
                'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
                env('APP_URL') ? ',' . parse_url(env('APP_URL'), PHP_URL_HOST) : '',
                env('FRONTEND_URLS')
                    ? implode(
                        ',',
                        array_map(function ($url) {
                            return parse_url($url, PHP_URL_HOST);
                        }, explode(',', env('FRONTEND_URLS')))
                    )
                    : ''
            )
        )
    ),
...
]

Make sure this line is present in kernel.php

 protected $middlewareGroups = [
        'web' => [
            ...
        ],

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, // <---
          ...
        ],
    ];

Then don't forget to clear the cache both in development and server.

php artisan config:clear
php artisan route:clear
php artisan cache:clear
1 like

Please or to participate in this conversation.