It's not clear to me that a Passport Server would ever be able to authenticate a client credentials token using the TokenGuard. It seems that tokens passed out for a client_credentials grant type do not contain a subject claim ('sub').
This subject gets translated into what the League's server calls 'oauth_user_id' but that is set to null (because of the no subject issue).
The default checks try to find a user but there is never any user with a null identifier so it should always fail.
The code is a little hairy to track down but:
I used a basic route like this for testing, it was in the API routes (routes/api.php):
Route::middleware('auth:api')->get('/test', function () {
return response()->json([ 'status' => 'ok' ], 200);
});
After successfully retrieving a client_credentials token and passing it, though, I'd always get unauthenticated. By putting a bunch of 'dds' (I know, kludgy but it worked) I found what I've noted above.
It's possible I'm doing something wrong, of course.
System details:
php 7.1.0 run via fastcgid on apache
from composer.lock:
582 "name": "laravel/passport",
583 "version": "v2.0.0",
584 "source": {
585 "type": "git",
586 "url": "https://github.com/laravel/passport.git",
587 "reference": "da617aa36720d11ad7f358630715b4ebd585b596"
588 },
453 "name": "laravel/framework",
454 "version": "v5.4.0",
455 "source": {
456 "type": "git",
457 "url": "https://github.com/laravel/framework.git",
458 "reference": "7212b1e9620c36bf806e444f6931cf5f379c68ff"
459 },
Client credentials are validated with a different middleware since there is no user. https://github.com/laravel/passport/blob/1.0/src/Http/Middleware/CheckClientCredentials.php
@RDelorier - that makes sense but then it also means it should make sense that the default Middleware SHOULD NOT appear to grant access when that access isn't valid or there's a bug where a clien_credentials grant is being sent to the wrong Middleware during the authentication process (or more likely a misconfiguration somewhere).
I adjusted the handle method in a local copy of https://github.com/laravel/passport/blob/1.0/src/Http/Middleware/CheckClientCredentials.php to:
31 /**
32 * Handle an incoming request.
33 *
34 * @param \Illuminate\Http\Request $request
35 * @param \Closure $next
36 * @return mixed
37 *
38 * @throws \Illuminate\Auth\AuthenticationException
39 */
40 public function handle($request, Closure $next, ...$scopes)
41 {
42 $psr = (new DiactorosFactory)->createRequest($request);
43 die($psr);
44
45 try{
46 $psr = $this->server->validateAuthenticatedRequest($psr);
47 } catch (OAuthServerException $e) {
48 throw new AuthenticationException;
49 }
50
51 foreach ($scopes as $scope) {
52 if (!in_array($scope,$psr->getAttribute('oauth_scopes'))) {
53 throw new AuthenticationException;
54 }
55 }
56
57 return $next($request);
58 }
(the first two numbers are line numbers)
...and that's not being called.
i.e.
curl -X POST -H "Cache-Control: no-cache" -H "Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" -F "grant_type=client_credentials" -F "client_id=2" -F "client_secret=Eou6kZjdETSQjf2mAlwbSXxlkfAnnkQNkaNCHO61" "http://playground.dev/oauth/token"
That just granted me:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImE4NjFmM2NlNTI1Zjg0NDM0NDJhZTE1ZTY4NzhmMTE0ODY3OGY4NGY2NjYxNTM2MjEzNmMyMGI2OWRlMDQyZjZhMjliY2RmMWE4MzQ2MGI2In0.eyJhdWQiOiIyIiwianRpIjoiYTg2MWYzY2U1MjVmODQ0MzQ0MmFlMTVlNjg3OGYxMTQ4Njc4Zjg0ZjY2NjE1MzYyMTM2YzIwYjY5ZGUwNDJmNmEyOWJjZGYxYTgzNDYwYjYiLCJpYXQiOjE0ODU0NTAzMDgsIm5iZiI6MTQ4NTQ1MDMwOCwiZXhwIjoxNDg2NzQ2MzA4LCJzdWIiOiIiLCJzY29wZXMiOltdfQ.fi3jP5BAvTPXV-c5O36oezibbRd5GwsFpEgNU1uE76zpAsojGpqdb-dDPKA5kMgh75Z3zCLxxA79YKmQEFTCJ_Ablm32f39sNnt7YpJq3ddVGFjyDceXYM2aanwMGEUjAXr1Q9Mt3OXGHju1lPLs7WY7uNJL1HGTAaaWCBFHgUi9DiO-l6A9WUwrlLj914RRqK_b3b7SLfUJ15nO-3uju9QXhtqZ1t51MHxAtqy4fUY1EMM_oJ_03L1wOgg7Soa2hfWOoH6HUp1vuMRvg5NIxZWJeA6V4Zk9MYB-nfTJfpZV13vUYoYeNDnKL7n6d_ROG9Vlu5Rgxb6MGk2Prl4KElbSITn2rgIS9S0I_vvNt1XDf8SHRUBLlKMPLYaBVl7GymAJEbp1nC7N6JTyIIhv3ZJv-b0j-vjLwcS7fcNk7XNCD8xFhK0iUlehXnLFGTnH6ZorKWOiA841snSicJl1fYzDdZBV4ljCPHzOweylcJeplSYSyPrNzqk6RfeWt6r1txpggi0fvKNmmPVF9X9V1pY6Zon5WGzMD-SlXqfCUaGavweK-UUt4CKzQPHXXiHiIexFXxxD__F0r7byszaXOGPaHB8gH4KqU4dlYnqrLMaFrYIsNxWA8qPYOQrGlp0W3f07hj7_g6sGErlCgT4Yf2Pzq1n1MKHx2MSR5-eKVzI
I added a 'testapi' to my kernel and added a 'routes/testapi' as attached.
I also created a 'slow' middleware group (bad name but I'm just testing) because leaving the bindings in seemed to trigger some error (i.e. I couldn't get the bearer token to work).
curl -X GET -H "Accept: application/json" -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImZkZGM3M2NhNGMwOWVlNzVhNzY1ZWM4ZWEwMGUwNTc3ZDliYzJlNTM3MDcyZTRmNjQzOGQxM2JiNWQxZjliY2FmZmY4OWJjOWYwYWU2ZWZmIn0.eyJhdWQiOiIyIiwianRpIjoiZmRkYzczY2E0YzA5ZWU3NWE3NjVlYzhlYTAwZTA1NzdkOWJjMmU1MzcwNzJlNGY2NDM4ZDEzYmI1ZDFmOWJjYWZmZjg5YmM5ZjBhZTZlZmYiLCJpYXQiOjE0ODU0NTA1ODQsIm5iZiI6MTQ4NTQ1MDU4NCwiZXhwIjoxNDg2NzQ2NTg0LCJzdWIiOiIiLCJzY29wZXMiOltdfQ.Vvnte_alru9PIArvXNtybXixuNtHMdOhztoWMPKUgEb6iM956r3f4IbHNpcngRGmWKP6wha_NhMlnMetLaU1QumZQBXhuVThX-6W3DQQG2AgspV8vDTc2PGB-23v7PGwsS7_vE5gHJ3t51PKEVCAHHyDfOFKM3jOrDj4tUDNL7ndJI1TBWhqhifnPtu04jn3RzaadvJPfiKWimc5eFYEpOEYevyL0IbJb3bPORSghMkdacuY6k_C88eNRd3z9ld4clI37_-H0SRfj4QFdl_pS5d_OlghcoIa7SFhbNuHJ6i3x5BLQKIhdePeyL72v5idJt8vYZXAsPSO1T7Cx-Kos-LhYPqhWVWMNO13_Oat-cPrljA6k3nTg7T_zBlUzUzMS03eCszMP3g3a5OMBQQ_ROAcHI0STObbEqBNy5ewCRBfGSPyfzWpETfdFHbgwqe_FGLKeuaYFDqCnyORCULA1jzKFE-MeUruX8_1r6fQ_QZfK4BeDTFGqnVGVSY7yoVmo-yyg_5exkWlEn_HBw_PpNXutyy1stfAC9yq_LefbQ2YFjU8eqPWoQ5VnXymCrWu_deN5vWkz9a4Zsc8HVnpIORmN4xSFycfU8kEJMO00bXqpbSfq4q4fS-XLGYRGX7ZLPPUO7akiSdd1X1_106rji3zrhTDSiWnGrdsPvaCvdQ" -H "Cache-Control: no-cache" -H "Postman-Token: 2894f548-8d6a-fc93-2678-1d6306d2e175" "http://playground.dev/testapi/status
Did you also enable implicit grant?
https://github.com/laravel/passport/blob/1.0/src/Passport.php#L89
Passport::enableImplicitGrant();
@RDelorier - no, I don't have implicit grant(s) enabled - is that a requirement? If it is, I think I missed where it said it is.
I'm using passport in lumen 5.4 and I have problems to.
if I use the CheckClientCredentials instead auth middleware I can authenticate the client but not retrieve the authenticated user (Auth::user() return null)
if I use both middlewares all requests return a 401 Unauthorized
@mapb1990 - The CheckClientCredentials is not meant to return any user.
See:
@lloy0076 Your die statement won't be called if you are not explicitly using the CheckClientCredentials middleware. You have to define the middleware and apply it to your routes. Lumen example below (should be pretty similar in Laravel, defining middleware in your service provider instead):
bootstrap/app.php
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
'client_credentials' => \Laravel\Passport\Http\Middleware\CheckClientCredentials::class,
]);
routes/web.php
$api->group(['middleware' => ['client_credentials']], function () use ($api) {
// define client_credentials protected routes
});
I have my own issue though. I was wondering how one could use both auth and this client_credentials middleware depending on the type of token used, so I could use the same route for both.
@lloy0076 I found the same issue today.
And I found it caused by league/oauth2-server. in LeagueOAuth2ServerGrantClientCredentialsGrant, Line 38, it set null for user_id, thus no user_id while issueAccessToken, (the same: no user_id store in database table 'oauth_clients').
`class ClientCredentialsGrant extends AbstractGrant
{
/**
* {@inheritdoc}
*/
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
ResponseTypeInterface $responseType,
DateInterval $accessTokenTTL
) {
// Validate request
$client = $this->validateClient($request);
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request));
// Finalize the requested scopes
$scopes = $this->scopeRepository->finalizeScopes($scopes, $this->getIdentifier(), $client);
// Issue and persist access token
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, null, $scopes);
// Inject access token into response type
$responseType->setAccessToken($accessToken);
return $responseType;
}
/**
* {@inheritdoc}
*/
public function getIdentifier()
{
return 'client_credentials';
}
}`
Then I think this should be update in TokenGuard, after Line 128, if $user == null, find user by clientId
Closing for lack of activity, hope you got the help you needed :)
Most helpful comment
@lloy0076 Your die statement won't be called if you are not explicitly using the CheckClientCredentials middleware. You have to define the middleware and apply it to your routes. Lumen example below (should be pretty similar in Laravel, defining middleware in your service provider instead):
bootstrap/app.php
routes/web.php
I have my own issue though. I was wondering how one could use both auth and this client_credentials middleware depending on the type of token used, so I could use the same route for both.