Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

JeffH's avatar
Level 8

Laravel Sanctum Help - 401 Error

So I am running into a 401 error when attempting to get the user from a sanctum protected route. Was hoping to get some help as what I am doing is a little confusing.

I am using vue cli, vue router, Laravel 7. Note, we did recently updated from laravel 5.4 to laravel 7. My first thought was maybe a new setting or config didn't get copied over, but I haven't found anything yet.

Also worth noting I am using https://github.com/nWidart/laravel-modules

Vue and laravel are hosted on the same server, same domain.

First, on my local machine I have a vhost setup.

<VirtualHost *> 
    DocumentRoot "/Users/jeff/Development/www/server/public" 
    ServerName dev.server.com
</VirtualHost>

So my url is http://dev.server.com

My Config is

SESSION_DRIVER=database
SESSION_DOMAIN=.server.com
SANCTUM_STATEFUL_DOMAINS=dev.server.com,localhost,127.0.0.1

Instead of the users table, we are using another table called Attendants. So I setup a guard and provider.

'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
		],
		
		'web_attendant' => [
            'driver' => 'session',
            'provider' => 'attendants',
		],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],

        'sanctum' => [
            'driver' => 'sanctum',
            'provider' => 'users',
		],

		'sanctum_attendant' => [
            'driver' => 'sanctum',
            'provider' => 'attendants',
        ],
    ],
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => Modules\User\Entities\User::class,
            'table' => 'users'
        ],

        'attendants' => [
			'driver' => 'eloquent',
            'model' => Modules\Webinar\Entities\Attendant::class,
            'table' => 'attendants'
        ],
    ],

my cors config

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

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

So because we are using a different table, I created a new login controller. But basically I do this in the end

Auth::guard('web_attendant')->login($attendant);

I have actually dd(Auth::guard('web_attendant')->user(); and the Attendant model shows up just fine here.

On a success login return response, I use vue-router to change components. The returned user shows up just fine on the new component. But if I refresh the page, I get a 401: unauthorized error. And when I attempt to get the webinar, i get a 401 error as well.

Route::group(['middleware' => [ 'auth:sanctum_attendant', 'bindings' ], 'prefix' => 'webinar', 'namespace' => 'Api' ], function()
{
    ///
}

My axios has the following defaults

Vue.prototype.$axios = require('axios');
Vue.prototype.$axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
Vue.prototype.$axios.defaults.baseURL = defaultPath;
Vue.prototype.$axios.defaults.withCredentials = true;

my axios call is

this.$axios.get(`/webinar/${this.id}`).then(({data}) => {
   //
});

In my cookies I so see laravel_session and XSRF-TOKEN

I can't help feeling like I am missing something. Any help would be appreciated.

0 likes
11 replies
JeffH's avatar
Level 8

Update: So I think one of my issues was I was trying to login with an api route

axios.get('/sanctum/csrf-cookie').then(response => {
	/* we authenticated, login */
	axios.post(`webinars/${this.webinar}/login`, {
		email: this.email,
		password: this.password,
	}).then(({data}) => {
		this.setAttendant(data);
		this.$router.push({name: 'Platform', params: {id: this.webinar}});
	})
});

This actually went to /api/webinars/{webinar}/login and I don't think cookies can be set via the api route? I'm not sure.

I updated it to a web route, same result. webinars/login/{webinar}

Route::group(['middleware' => 'web'], function()
{
	Route::post('webinars/login/{webinar}', 'AuthController@login');
});
JeffH's avatar
Level 8

Another update I authorized a user instead of an attendant and it worked. So this works

Auth::attempt(['email' => request()->email, 'password' => request()->password]);
return Auth::user();
	Route::group(['middleware' => [ 'auth:sanctum' ] ], function() {
//
});

but this does not

Auth::guard('web_attendant')->login($attendant);
return Auth::guard('web_attendant')->user();
	Route::group(['middleware' => [ 'auth:sanctum_attendant' ] ], function() { 
//
});

My attendant model

use Illuminate\Foundation\Auth\User as Authenticatable;

class Attendant extends Authenticatable
{
//

What am I missing? any ideas?

When I do

Auth::guard('web_attendant')->login($attendant);
return Auth::guard('web_attendant')->user();

I get the correct Attendant. but I get a 401 unauthorized.

JeffH's avatar
JeffH
OP
Best Answer
Level 8

I found out the Sanctum guard defaults to the web guard, which uses the Users Table. So Sanctum was trying to authenticate the Attendant with the users table.

public function __invoke(Request $request)
    {
        if ($user = $this->auth->guard(config('sanctum.guard', 'web'))->user()) {
            return $this->supportsTokens($user)
                        ? $user->withAccessToken(new TransientToken)
                        : $user;
        }

I got around this by adding

	config(['sanctum.guard' => 'web_attendant']);

To my api.php file. Since My admin panel authenticates users and webinars authenticate attendants, I can't hard code in a guard in the sanctum.php config file

3 likes
lalitesh's avatar

@JeffH This saved my day. A big thank you.

By the way, did you find a better way to do it?

JeffH's avatar
Level 8

@lalitesh There is a way to create a custom guard and provider for it but I was annoyed that I couldn't find good documentation on it and just moved on with my solution.

If all you have is one guard, you can go into your sanctum.php config and add

'guard' => 'your-custom-guard'
lalitesh's avatar

@JeffH Unfortunately, this would not work for me for I have two guards- web ( the default one ) and web_admin.

There are certain API endpoints only accessible to the web_admin guard while others can be accessed by both the web as well as web_admin.

So far I have not been lucky.

JeffH's avatar
Level 8

@lalitesh

I ran into your same issue. Honestly the best solution is to have one table for your users. Give admins an admin role and other uses a regular user role. I use this package for another client using this setup https://spatie.be/docs/laravel-permission/v5/introduction

Unfortunately, I inherited the two users table setup and this client wants to keep it this way. So here is what I did to use two guards.

  1. Made a middleware to change the guard
$user = Auth::user();
		
		if( $user )
		{
			/* set the config to web, which uses the admin table */
			config( [ 'sanctum.guard' => 'web_admin' ] );
			config( [ 'auth.guards.sanctum.provider' => 'admins' ] );
		}
		else 
		{
			/* set the config to web_attendants, which uses the webinars_attendants table */
			config( [ 'sanctum.guard' => 'web_user' ] );
			config( [ 'auth.guards.sanctum.provider' => 'users' ] );
		}
  1. In auth.php, setup my guards and providers
    'guards' => [
        'web_user' => [
            'driver' => 'session',
            'provider' => 'users',
		],
		
		'web_admin' => [
            'driver' => 'session',
            'provider' => 'admins',
		],

        'sanctum' => [
            'driver' => 'sanctum',
            'provider' => 'admins',
		],

		'sanctum_users => [
            'driver' => 'sanctum',
            'provider' => 'users',
        ],
    ],

    'providers' => [
        'admins' => [
            'driver' => 'eloquent',
            'model' => App\Admin::class,
            'table' => 'admins'
        ],

        'users' => [
			'driver' => 'eloquent',
			'model' => App\User::class,
			'table' => 'users'

        ],
    ],
  1. I grouped my routes, putting my custom middleware first, before the sanctum guard
		// sanctum_guard is step 1.
	Route::group(['middleware' => [ 'sanctum_guard', 'auth:sanctum' ]], function()

Thats how I got it to work this way. I hope it helps you

keizah7's avatar

@JeffH Saved my day, thanks! Postman client is redirected to login page, maybe someone know why?

picobaz's avatar

@JeffH I had the same problem, after a lot of effort, I realized that in the main configuration of my web server, the Authorization field is blocked and does not allow it to come to my codes. Check it too, maybe your problem will be solved in this way

sea403's avatar

Any update to this ?

I am getting the same issue... and it was working but now not working lol

Please or to participate in this conversation.