Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use middleware 'auth:api' and 'client' simultanaous #898

Closed
jcharcosset opened this issue Nov 30, 2018 · 18 comments
Closed

Use middleware 'auth:api' and 'client' simultanaous #898

jcharcosset opened this issue Nov 30, 2018 · 18 comments

Comments

@jcharcosset
Copy link

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,

@driesvints
Copy link
Member

Please see #379

@gendronb
Copy link

Sorry to reopen this, but how does #379 solve the current issue?

@jcharcosset
Copy link
Author

I found an alternative method for my specific problem. I wait next Passport version to update properly my code.

@driesvints
Copy link
Member

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

@fer8a
Copy link

fer8a commented Apr 18, 2019

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.

  1. If I add auth:api middleware to the routes, client tokens can't access them.
  2. If I add 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.

@mohammadaktaa1995
Copy link

i want to use both of client and auth:api to my routes how can i do that @driesvints

@driesvints
Copy link
Member

driesvints commented Apr 22, 2019

Using these at the same time isn't feasible as explained here: #898 (comment)

@mohammadaktaa1995
Copy link

Okay thank you. @driesvints

@383819640
Copy link

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.

@mohammedaktaa
Copy link

mohammedaktaa commented Sep 23, 2019

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

@madman-81
Copy link

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)?

@gdebrauwer
Copy link
Contributor

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.

@madman-81
Copy link

Since already 4 people run into this issue, shouldn't this issue be reopened?

@mohammedaktaa
Copy link

@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;
}

@madman-81
Copy link

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.

@alvirbismonte
Copy link

#1125 is opened for this issue.

@Baysen
Copy link

Baysen commented Oct 27, 2020

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.

@amcsi
Copy link

amcsi commented Mar 4, 2021

+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:

  1. Write my own custom middleware similar to the ones mentioned in this issue.
  2. Instead of using a Client Credentials Grant, I'm going to create a Password or Personal grant client (not sure which is more appropriate) tied to a new guard 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests