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

bitmads's avatar

Laravel API with session driver and Sanctum

Hi guys. I'm trying to implement Sanctum with no luck, I'm getting an Unauthorized message doesn't matter what I do. I'm after reading every single article and watching streams, but still no luck.

The official documentation is very unclear as well.

CORS works well, I'm setting and getting the XSRF cookies well, those are present both in response and request.

The docs say, for a SPA (I use Angular) I should use the SPA flow and not the API token flow. On the other hand, the documentation requires sending the login request to the web middleware group (/login) instead of the API (/api/login) which I don't understand why.

Why I would call the web middleware when I need to call an API endpoint from my frontend application? This feels wrong.

Also, the docs below say, for mobile applications, I should use the token flow, but different than the API token flow. So strange this too. No further explanation.

I've read a lot about security issues with storing the API token in localstorage, it's not recommended, that's why I would like to go with the session flow using cookies and not locally stored tokens with Auth header

My question is: How to use /api/* endpoints with using the session driver and NOT the token flow (which is not recommended anymore)?

SANCTUM_STATEFUL_DOMAINS=192.168.88.10:4200
SESSION_DOMAIN=192.168.88.10
SESSION_DRIVER=cookie
SESSION_LIFETIME=120
## TODO: On production (https) set this true
SESSION_SECURE_COOKIE=false
Route::group(['prefix'=>'v1','as'=>'v1.'], function () {
    Route::get('/login', [UserController::class, 'login']);
    Route::group(['middleware' => ['auth:sanctum', 'throttle:500|2200,1']], function () {

        Route::get('/status', [AppController::class, 'getStatus']);
        Route::resource('users', UserController::class)->middleware('web');
        Route::get('/logout', [UserController::class, 'logout']);

    });
});
    public function login(Request $request): Response
    {
        $credentials = $request->only('email', 'password');

        if (Auth::attempt($credentials)) {
            $request->session()->regenerate();
            $user = Auth::user();
            return Response::data($user)
                            ->message('Login was successful!');

        }else{
            return Response::message('Wrong username or password!','error',401);

        }
    }

Before I used JWT but it seems it's not secure anymore since it's about storing the token in the local storage.

0 likes
23 replies
devingray_'s avatar

The flow should go like this

Install and Publish

composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

Sanctum Creates a migration

php artisan migrate

Alter the "api" middleware group like this

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

in sanctum config alter the stateful key

'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,127.0.0.1')),

Add the key to your .env

SANCTUM_STATEFUL_DOMAINS=localhost

Alter your session driver in your .env

SESSION_DRIVER=cookie

in config/cors.php configure your paths and allow supporting credentials

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

'supports_credentials' => true

You will need to make a login route available for that I would use Laravel Fortify or Laravel Breeze, both will work

Now you can make requests with axios

await axios.get('/sanctum/csrf-cookie')
await axios.post('/login',  {...})
1 like
bitmads's avatar

As I've mentioned in the question I don't want to use the web middleware login route, because it feels wrong. I use Laravel as backend, why I would use their web middleware group?

I need to use the api/login endpoint some way to setup the session and use it.

I've also mentioned I'm after reading and watching everything so copy-paste from the official doc won't help in this case. Thank you for helping btw, just want to cut this shorter.

2 likes
devingray_'s avatar

You have a GET request for your login in your route but are worried about a 'web' middleware feeling wrong... You should probably stick with JWT auth if you don't want to use the package the way it is intended

bitmads's avatar

Are you here to help or insulting me?

Please, read my question before you answer or don't answer.

If you think the first mistake I've made with this is a request method, you say that and I'll check if this is the issue, then if you are right I'll set it the best answer.

What is this new "roasting the OP" trend all around the internet forums? Am I too old? 15 years ago the people know how to communicate and help each other without insulting bullying those who need help.

:(

devingray_'s avatar

I am not insulting you, I put time and effort into helping you and you told me that you don't want to do it. That is fine, you are free to do whatever you want. Please refer to my other participation on this forum and see that I genuinely do my best to help with these questions.

I did give you a step by step how to solve your problem, it seems to me that you may be annoyed with how the package is built, but I did not write the package.

Using a GET request to send user passwords is a security issue. This will allow the user password to go into the URL in plain text. This is something you should consider.

Second, the WEB middleware in Laravel uses CSRF protection to protect the routes and sanctum uses that CSRF to make the login request safe. Using the web middleware in this case is not a bad thing.

Good luck with your life

1 like
bitmads's avatar

1st. Yes , you are instulting me:

,,You have a GET request for your login in your route but are worried about a 'web' middleware feeling wrong... "

,,Good luck with your life"

"You should probably stick with JWT auth if you don't want to use the package the way it is intended" *

This called passive-aggression which is still aggression, doesn't matter how you are trying to wrap it into "I'm just trying to help" BS. You are trying to answer without reading. Trying to help wrong is not helping just messing up the thread. Sorry for the bad news.

Next time explain to a guy with a wheelchair they should use their legs because the people move that way.

2nd. Where did I told I don't want to do something you told me?

Read the title of the topic please, then you'll see your copy-paste from the documentation is something that has nothing to do with my question. Still, it's not helping in this case.

3rd. I NEVER told you I don't like web route. ** My question is how can I reach the same using /api/login route to have the CRSF protection you are talking about** I think adding exceptions to several configs and other places is NOT the best Laravelish way to accomplish this.

Still do you think you tried to help here?

devingray_'s avatar

Yep I definitely could. But I don't really enjoy wasting my time :) I am truly sorry that You feel insulted, this was not my intention. I hope that you find your solution.

P.S The Laravel community is very helpful and friendly, at least in my experience. Remember that these forums are "free" and the replies that we post on here are of our own free will. Nobody has to help you, we choose to try and help you.

Have a good day

bitmads's avatar

I'm participating on the Discord channel for many years, I love this community, but it seems Laracast is not the way to find answers anymore. I came here for the first time, before, I was a read-only user.

Now, after days of struggling and working together with guys on Discord, we DIDN't find the solution so I came here for further information.

I've even explained that I'm after all of the blogs, youtube streams, then you give me a copy-paste from the docs.

Sorry again, but reading the original question is useful. Not everybody first-timer here.

devingray_'s avatar

on your User model add the HasApiTokens trait like this

use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;
}

Then in routes/api.php

Add a login route and create a token manually

Route::post('login', function () {
    if (! Auth::attempt($request->only('email', 'password'))) {
        return response()->json([
            'message' => 'Invalid login details'
        ], 401);
    }
    $user = User::where('email', $request['email'])->firstOrFail();

    $token = $user->createToken('auth_token')->plainTextToken;

    return response()->json([
        'access_token' => $token,
        'token_type' => 'Bearer',
    ]);
});

Now make axios

await axios.post('api/login', {'email': xxx, 'password': xxx})
1 like
bitmads's avatar

Thank you! But unfortunately, this is the API token flow, which is not recommended anymore as I mentioned in the OP too. Maybe it's not you it's me: I'm looking for the solution to use the session flow and not the token flow :)

devingray_'s avatar

The two flows are completely different. So perhaps it would be helpful to find out what you are actually trying to do.

How is the project set up?

Is it two repositories? EG backend-laravel and frontend-angular?

How are you running the backend? php artisan serve? How are you running the frontend? 'npm run watch'?

Message me on discord, in the Laravel server and I can screen share to help

martinbean's avatar

@bitmads You don’t use sessions per se with Sanctum’s SPA authentication. You get a CSRF token to make a POST request to your /login endpoint. This will then return a cookie, which you can use to authenticate your API requests.

Any requests to your API endpoints will include the session cookie. So long as you use the auth:sanctum guard on your API routes, the user will be authenticated. Your API routes are still stateless; it’s just that the cookie is checked on each request rather than your user being authenticated via a session.

You can read more about the login and authentication processes here:

bitmads's avatar

Hi thank you! This has been discussed above, but still these are not answering my question.

For better understanding:

I'm after these steps:

  • I can successfully get and set the headers and cookies
  • I can log in with my custom login endpoint
  • session cookie AND the XSRF cookies are present

AFTER all of these when I request a resource with auth:sanctum middleware and getting an Unauthorized error message. For some reason Laravel doesn't pick up the session cookie despite that it's present.

Other words, the only thing I would lie to do differently (from the official doc) is I don't like that I have to use /login route instead of /api/login. Since I'd use API for communicating with our Angular frontend feels strange to add exceptions for /login route all around Laravel instead of handling the cookies on API middleware.

1 like
bitmads's avatar
bitmads
OP
Best Answer
Level 1

After hours of trying together with the guys on the Discord channel, they made me drop Sanctum entirely, Sanctum package is a mess, and my feeling was right with this web/api routing mess up... I felt it wrong because the whole idea is wrong in this way.

Finally (thx to the suggestion on the channel), I ended up using Laravel session authentication without any 3rd party package and works like a charm:

I've added a session middleware group in Kernel.php

        'session' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
        ],

changed the middleware in RouteServiceProvider

        $this->routes(function () {
            Route::prefix('api')
                ->middleware('session')
                ->namespace($this->namespace)
                ->group(base_path('routes/api.php'));

created routing group with auth middleware

Route::group(['middleware' => ['auth', 'throttle:500|2200,1']], function () {
.
.
.
)}

This will do it. No JWT, no Sanctum, token in a httpOnly cookie (most secure way compared to other flows like storing token in the localstorage), it's perfect.

3 likes
theotherdy's avatar

Only tangentially related to the OP but, in case anyone else ends up here with endless Sanctum 401 problems, despite having followed all of @devingray_ 's very clear steps, do check that you're not loosely using SANCTUM_STATEFUL_DOMAINS=127.0.01 in your .env file and localhost in your axios gets - it matters!

1 like
teneshvignesan's avatar

After hours of searching around and finding nothing but this 401 error after successfully login in, i might just DROP this sanctum.

I have set everything as the doc says yet, I am still getting the same 401 error for any api route once logged in.

1 like
rhand's avatar

@teneshvignesan Better start a new thread and add details (code) on your setup. Easier to get help that way.

Please or to participate in this conversation.