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

eslGman's avatar

Passport: Requests with client credentials token always result in 401

Hey there,

I'm loosing my mind right now, as I can't figure out what I'm doing wrong. I already went down the call stack until the point where I see what's wrong/missing, but I can't find out how this should ever work.

First, our setup: It's pretty much following the guidance here: https://laravel.com/docs/8.x/passport (We're using Passport v10.2.2)

  • User model uses the HasApiTokens trait (not that I would need a user, as we're using the client credentials grant, but ok)
  • The api guard is using the passport driver (which uses the "users" provider. Again, don't think we would care)
  • We have 'client' => CheckClientCredentials::class added to the $routeMiddleware and added 'client' to the api group in $middlewareGroups!
  • Also, we have the auth:api middleware added to the api $middlewareGroups group, so all our APIs are using the api guard we set in auth.php!
  • The only addition we made is a custom client model, but this properly extends the Client model from Passport and simply only adds a relation to a "vendor" table we have. And we are telling Passport to use it in theAuthServiceProvider::boot() method: Passport::useClientModel(OAuthClient::class);

We can successfully obtain tokens using the client credentials grant, that's no issue at all.

The actual issue we see is that all requests we do using a client credential access token are 401 Unauthorized. I went down the call stack and ended up scratching my head when looking at the TokenGuard::authenticateViaBearerToken() method, as it will just return if it can't find a user using the PassportUserProvider for an oauth_user_id, which is never set for a request done with a client grant access token. And just returning here will make the RequestGuard::check() / RequestGuard::user() not do anything and with that, Authenticate::authenticate() just call unauthenticated()...

So for me, it looks like TokenGuard::authenticateViaBearerToken() will only work for personal access tokens, but will never work for client credentials access tokens...

HOWEVER: If I remove the auth:api middleware, so effectively not have it use the passport driver, it works! And it's actually validating the tokens (I tried providing invalid/expired/no tokens and it doesn't work. So it does something useful, but I didn't go down the call stack for this yet...). But it's now using the SessionGuard... I don't know if that is expected or not

I must be doing something wrong or missing something. Maybe somebody can point me in the right direction.

Thanks and greetings,

Andy!

0 likes
7 replies
eslGman's avatar
eslGman
OP
Best Answer
Level 1

Answering myself here: The solution is simply to not use the auth:api middleware, if you do client credentials grant, as this will always fail for client tokens. So the docs are a bit misleading in that case: It tells you to add it to the routes you want to protect, but this only applies to routes not using client credentials grant. You either:

  • Add the client middleware for routes protecting with client credentials grant
  • Add the auth:api middleware for routes NOT protecting with client credentials grant
1 like
martinbean's avatar

@eslgman The docs aren’t misleading? The client credentials grant section says to use the client middleware and nothing about auth:api:

Next, to use this grant type, you need to add the CheckClientCredentials middleware to the $routeMiddleware property of your app/Http/Kernel.php file:

use Laravel\Passport\Http\Middleware\CheckClientCredentials;

protected $routeMiddleware = [
    'client' => CheckClientCredentials::class,
];

Then, attach the middleware to a route:

Route::get('/orders', function (Request $request) {
    ...
})->middleware('client');
martinbean's avatar

@eslGman Yes. And as you found from your other question, the client credentials grant is a special case, so you should be following the instructions from that sub-section and not a generic one that occurs further on.

The client credentials example clearly shows using the client middleware and not the auth:api middleware.

eslGman's avatar

@martinbean Sure, why would a generic section apply to another generic section on the same level in the docs. Totally true, that's how everybody has always treated docs. Client Credentials are not a "special" case, they're a case. Maybe for you or Passport they are, but what an annoying assumption is that!? Client credentials are as a normal case as personal tokens are, there is nothing special about them. They are just different. But ok, be resilent against feedback that the docs are missleading ¯_(ツ)_/¯

Please or to participate in this conversation.