Getting 401 unauthorized for Laravel sanctum
Asked Answered
W

11

29

I am using Laravel Sanctum with Vuejs SPA. Both reside on same top level domain

Laravel backend : app.demo.localhost
Vue SPA : app-spa.demo.localhost

Login and logout (endpoints) are working correctly when called from VueJS SPA using axios and XSRF-TOKEN is succesfully set, but when I call other api end points it gives me 401 unauthorized.

In axios this is being set

axios.defaults.withCredentials = true;

I have the below configurations

In Laravel .env

SESSION_DRIVER=cookie
SESSION_DOMAIN=.demo.localhost
SANCTUM_STATEFUL_DOMAINS=app-spa.demo.localhost

In Routes/Api.php

Route::middleware('auth:sanctum')->get('api/user', function (Request $request) {
   return $request->user();
});

In 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,

Could someone help me out please?

Warnke answered 25/4, 2020 at 5:53 Comment(6)
You don't have it shown above, have you enabled the sanctum middleware in app/Http/Kernel.php? laravel.com/docs/7.x/sanctum#spa-authenticationCrapulous
Did you manage to solve this issue?Aerification
I have the same problem on my production server, using react and axios. O development server it works fineCowan
anyone solve this? I am getting a success login with a custom guard. But subsequent requests fail even through xsrf token is set etc...Dowson
Facing the same problem. Please provide answer if someone as found it.Sthenic
I'm struggling with a very similar issue...POST requests seem to work (login, logout), however the GET /api/user is returning 401. I'm on Laravel 10, using Postman client. I have an older Laravel 8 app that I booted up and tried the same Postman requests and the GET /api/user returns 200...pretty stumped atm. Did an eyeball diff of the 2 server-side config files (8 vs 10) but no major differences as far as I can tell. Referer header is set. Also not sure if this matters but I'm running Laravel via sail (may affect port number?)Electrotonus
S
39

If you are using php artisan serve add the port number to SANCTUM_STATEFUL_DOMAINS. So if your port number is 8000:

SESSION_DRIVER=cookie
SESSION_DOMAIN=.demo.localhost
SANCTUM_STATEFUL_DOMAINS=app-spa.demo.localhost:8000

Your SANCTUM_STATEFUL_DOMAINS must match the url in your browser. The port number should not be on the SESSION_DOMAIN.

Skewness answered 1/7, 2020 at 17:18 Comment(2)
I added port number to the SESSION_DOMAIN setting and that made it return 401 - i removed the port number and it worked right away - thanks!Albertson
by default APP_URL is stateful. fixing APP_URL in my .env solved my problem. thanks!Sentiment
N
21

Following are the 8 steps that I follow while setting up Laravel sanctum check if you missed anything

Step1 composer require laravel/sanctum

Step2 php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider

Step3 php artisan migrate (you can ignore this if you're using spa)

Step4 uncomment this line from app/http/kernel.php \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,

Step5 In config/cors.php update 'supports_credentials' => true,

Step6 In .env file update SESSION_DRIVER=cookie & add new line of SESSION_DOMAIN=localhost (even if your using any port like 8080 just mention localhost in session_domain)

Step7 In config/sanctum.php add your client domain along with port(if local) in stateful as follows, in my case for vue CLI it's usually localhost:8080 & for nuxt its localhost:3000 , code is as follows

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

Mostly if your stateful (step7) is not setup properly you will get 401 unauthorized or it will try to redirect you to the home page along with cors policy error

Step8 Do not forget to await until sanctum/csrf-cookie promise is resolved

    async login() {
      await axios.get("http://localhost:8000/sanctum/csrf-cookie");

      await axios.post("http://localhost:8000/login", {
        email: "[email protected]",
        password: "password",
      });

      let response = await axios.get("http://localhost:8000/api/user");
      console.log(response.data);
    },

Nash answered 5/11, 2021 at 19:4 Comment(4)
Thanks @Kanul Rajput for this comprehensive answer. My login happens in a guard before I can make this call, which I think may be causing me problems. Do you know if there is there any way I can 'manually' trigger whatever process happens when the login is called with the XSRF token?Thaumaturge
Thank you so much , it's was EnsureFrontendRequestsAreStateful in my caseAluminum
Step 7 was the blocker for me. I set SANCTUM_STATEFUL_DOMAINS=http://localhost:3000, I just had to remove the http:// All you just need is SANCTUM_STATEFUL_DOMAINS=localhost:3000Toupee
(for multiple guards) Step 8 could be adding additional guards to 'guard' => ['web', 'admin'] in config/sanctum.phpKarelian
G
9

For anyone dealing with localhost:

SESSION_DRIVER=cookie    
SESSION_DOMAIN=localhost    
SANCTUM_STATEFUL_DOMAINS=localhost:8080(port number you use)
Greenshank answered 14/1, 2021 at 11:39 Comment(0)
R
4

For me i just had to place the host with port number:

SANCTUM_STATEFUL_DOMAINS=127.0.0.1:5173

and it started working. Maybe this helps someone.

Rese answered 6/9, 2022 at 14:24 Comment(0)
S
1

I just encountered the same problem. I configured all the options according to the official documentation, but I couldn't get the authorization.

Then I use routes/web.php instead of routes/api.php, so I can use sanctum middleware very well.

Now the problem seems obvious,Axios withCredentials maybe need to place in the correct way.

const http = axios.create({
    baseURL: API_URL,
    withCredentials: true
})

maybe not work. So I add {withCredentials: true} like

http.get('/api/whoami', {withCredentials: true})
  .then(res => {
                console.log(res.data)
            })

Then it works.

But the very strange thing is that it is normal now, no matter whether I clear the browser cache, cookies or Laravel's various caches, there is no previous situation

Shoebill answered 10/6, 2020 at 1:34 Comment(1)
man... That was the only solution worked for me! Thanks.Physicist
A
1

My issue was I setup the domain in the wrong place. I thought was an array of domains, in config/sanctum.php, but not, needs to be placed within the string:

OK:

    'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s',
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1,myownlocaldomain.test,myownlocaldomain.test:8080', <-------- OK
        env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
    ))),

BAD:

    'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%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) : '',
        'myownlocaldomain.test', <----- BAD
        'myownlocaldomain.test:8080', <---- BAD
    ))),

I hope I save days of work to someone else...

Aetna answered 22/2, 2022 at 14:32 Comment(0)
W
1

You SPA which is intended to be a first party app is not a first party app if it's on different port. So it's just acting like a mobile app in your development environment.

For example

Laravel api

localhost:8000

Vuejs app

locahost:5781

So you need to authenticate it be sending an authorization header when developing in your localhost host. Note: Do this only in your local development enviroment. So put the code below in an if.

axios.interceptors.request.use(
  config => {
    // Do something before request is sent
    
    if(process.env.dev) {
       // accessToken was set after a successful login
       const accessToken = localStorage.getItem('accessToken')
    
       if (accessToken) config.headers.Authorization = `Bearer ${accessToken}`
    }
    

    return config
  },
error => Promise.reject(error),
)
Whippletree answered 7/11, 2023 at 13:42 Comment(0)
I
0

My problema was.... (no read with attention)

If your SPA needs to authenticate with private / presence broadcast channels, you should place the Broadcast::routes method call within your routes/api.php file:

Impervious answered 14/1, 2021 at 22:36 Comment(0)
F
0

Hi i found a solution.

My SPA is Vue v3 working on 3000 port. Also my backend is working on 80 port. (laravel 8.1)

Make Stateful Domains in config/sanctum.php like that

 'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
    '%s%s',
    'localhost:3000',
    env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
))),

Adding only one and correct domain on their, worked for me magically. I wrote before there whole possible variant of ports, it made me crazy and cost a couple days and nights.

Francklyn answered 5/2, 2022 at 23:49 Comment(0)
H
0

Laravel 10 with VueJs 3 and Sanctum package. I have solved this problem by setting APP_URL=[my domain name] in the .env file which the value matches the domain name in the web browser.

Give this a try. Hope it helps.

Harbin answered 3/3, 2023 at 10:39 Comment(0)
H
0

If anyone is using Valet as the webserver, you might want to double check the APP_URL - it needs to match with your Valet linked domain (for example domain.test or domain.dev).

In my case, logging in into the app via Inertia was no problem, but following requests to my /api routes got 401. Applying the above fixed this.

Hannus answered 4/4, 2024 at 7:31 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.