I use Laravel 5.7.* and Passport ^7.0 .
To consume my API with javaScript, I added CreateFreshApiToken middleware.
I would also like to implement Client Credentials Grant Tokens authentication so that a machine can use the API.
But, the two middlewares "client" and "auth:api" doesn't work together.
If I put "auth:api" middleware only, I can't access my API from web. And reciprocally, if I put "client" middleware only, I can't access my API from another machine.
Best regards,
Sorry to reopen this, but how does #379 solve the current issue?
I found an alternative method for my specific problem. I wait next Passport version to update properly my code.
Ah sorry, I misunderstood the original question. You don't need to add the auth:api middleware for client credential routes. They only need the client middleware since it's a machine accessing those routes and not an actual user. See https://laravel.com/docs/5.7/passport#client-credentials-grant-tokens
If I'm not mistaken @jcharcosset is in the same boat as myself.
I'm building a public API where users and machines can access all the endpoints the same way.
auth:api middleware to the routes, client tokens can't access them.client middleware to the routes, password tokens can access them but have no user data.I'd like a simple way to allow both (client credentials and password) tokens to access the same endpoints over the same middleware. Like OP said they don't work simultaneously when you do, for example, something like :
Route::group(['middleware' => ['auth:api', 'client']], function () { });
The intended behavior I'm aiming for is that of an OR (if it passes any of those conditions, allow access) instead of an AND like it currently is for Laravel.
Hope that's a bit more clear.
i want to use both of client and auth:api to my routes how can i do that @driesvints
Using these at the same time isn't feasible as explained here: https://github.com/laravel/passport/issues/898#issuecomment-463583281
Okay thank you. @driesvints
if you want to use the same entry point in api.php
1) setup the route with middlware = > 'client' for your single api.
2) Please try to use below function to get your login user from password token in your controller function:
$user = Auth::guard('api')->user();
if $user is null, just do the process without auth in your controller.
else if you get $user, you can differ the process in the same function in your controller
Because both client token and user token can access the same route with the middleware "client" and also you get the user from guard.
After searching and trying many solutions i create this one
I create middleware for both api and client and its handle fn is :
public function handle($request, Closure $next, ...$scopes)
{
$auth_guard_middleware = app()->make(AuthGuardMiddleware::class);
try {
$response = $auth_guard_middleware->handle($request, $next, 'api');
} catch (AuthenticationException $e) {
$client_cred_middleware = app()->make(ClientCredMiddleware::class);
$response = $client_cred_middleware->handle($request, $next, ...$scopes);
}
return $response;
}
so now you can add it to your kernel file and use it
@driesvints
I have the same use case as @jcharcosset and @fer8a: an API that should be accessible with both client access tokens and personal access tokens. My implementation was roughly the same as @fer8a has posted, but as of v8 of Passport this isn't working any more due to PR #1040.
Is there anybody who has a suggestion how this can be fixed (except for writing my own middleware)?
I also have the same problem as @madman-81 and @jcharcosset and @fer8a.
In a lot of our projects we have for example an api route to register a new user. This route is protected with the client middleware. An access token is obtained via client_credentials grant type using a password oauth client. That client is also used to authenticate as user with password grant type.
Because of the changes by the PR #1040 it seems that I will now need 2 separate oauth clients? This seems very strange.
Since already 4 people run into this issue, shouldn't this issue be reopened?
@gendronb @madman-81 you can see my solution above your comments
I created middleware that I called ClientOrApi and handle function of it is:
public function handle($request, Closure $next, ...$scopes)
{
$auth_guard_middleware = app()->make(AuthGuardMiddleware::class);
try {
$response = $auth_guard_middleware->handle($request, $next, 'api');
} catch (AuthenticationException $e) {
$client_cred_middleware = app()->make(ClientCredMiddleware::class);
$response = $client_cred_middleware->handle($request, $next, ...$scopes);
}
return $response;
}
It seems link the AuthGuardMiddleware and ClientCredMiddleware are classes of your own?
For now I've done something similar and created my own middleware as well, but as an extension of the CheckClientCredentials middleware from Passport. I've overridden the handle() function and left out the firstparty-check that has been added in the PR. It looks like this now:
class CheckAPICredentials extends CheckClientCredentials
{
/**
* Validate the scopes and token on the incoming request.
*
* @param \Psr\Http\Message\ServerRequestInterface $psr
* @param array $scopes
* @return void
* @throws \Laravel\Passport\Exceptions\MissingScopeException|\Illuminate\Auth\AuthenticationException
*/
protected function validate($psr, $scopes)
{
$token = $this->repository->find($psr->getAttribute('oauth_access_token_id'));
if (! $token ) {
throw new AuthenticationException;
}
if (in_array('*', $token->scopes)) {
return;
}
foreach ($scopes as $scope) {
if ($token->cant($scope)) {
throw new MissingScopeException($scope);
}
}
}
}
I guess it could be nice to have this (or something similar) as a standard middleware in Passport? Because the main issue in the PR was that that the name suggested it was checking on Client tokens and it accepted all tokens.
#1125 is opened for this issue.
I'm sorry to ride this dead horse again, but can someone please explain how the problem got solved? Is it even solved? Because I still can't use endpoints with both. When I use the normal grant client and client middleware, I don't get the user. When I use the personal client and auth:api, the client grant auth fails completely.
+1
It's not possible to use both the auth:api and client middlewares together, because if any of them fail auth, it doesn't give a chance for the other to run. This way it's not possible to make routes that can be accessed both with API calls authenticated by a user, and API calls representing a machine.
I know that normally you can allow for multiple auth guards by separating the guards with a comma (e.g. auth:api,web), but client (for authenticating the Client Credential Grant) isn't a guard, so it can't be used with the auth middleware.
I think that while there's no out-of-the-box possibility to solve this issue, I'm either going to:
machine using a new machine provider instead of the UserProvider: MachineProvider that can only return a single fixed MachineUser which is a fake "user" that does not get saved to the DB, and just is there to indicate that the authenticated "user" is the machine. Then I can use auth:api,machine.
Most helpful comment
If I'm not mistaken @jcharcosset is in the same boat as myself.
I'm building a public API where users and machines can access all the endpoints the same way.
auth:apimiddleware to the routes, client tokens can't access them.clientmiddleware to the routes, password tokens can access them but have no user data.I'd like a simple way to allow both (client credentials and password) tokens to access the same endpoints over the same middleware. Like OP said they don't work simultaneously when you do, for example, something like :
Route::group(['middleware' => ['auth:api', 'client']], function () { });The intended behavior I'm aiming for is that of an OR (if it passes any of those conditions, allow access) instead of an AND like it currently is for Laravel.
Hope that's a bit more clear.