chimit's avatar

Multiple auth guards in Lumen framework

Please, can anybody explain how to implement multiple authentication guards in Lumen framework? Currently, I have two authenticatable models: Users and Clients. I'm using a custom implementation of JWT. A User has client_id and user_id fields in his token payload. While a Client only has client_id. Based on this I need to determine who came to me: client, user or guest (without a token).

auth.php

'guards' => [
    'client' => [
        'driver' => 'token',
        'provider' => 'clients',
    ],
    'user' => [
        'driver' => 'token',
        'provider' => 'users',
    ],
],

'providers' => [
    'clients' => [
        'driver' => 'eloquent',
        'model' => App\Client::class,
    ],
    'users' => [
        'driver' => 'eloquent',
        'model' => App\User::class,
    ],
],

AuthServiceProvider.php

public function boot()
{
    $this->app['auth']->viaRequest('token', function ($request) {
        $access_token = HelperClass::getTokenFromHeader($request->headers->get('Authorization'));

        if ($access_token) {
            $tokendata = JWT::decode($access_token, getenv('TOKEN_SECRET'), array('HS256'));

            if ($tokendata->user_id) {
                return User::find($tokendata->user_id);
            }

            return Client::find($tokendata->client_id);
        }
    });
}

routes.php

$app->get('/api/{item_id:\d+}', ['middleware' => 'auth:user', 'uses' => 'App\Http\Controllers\ItemController@get']);

I want to allow only Users (not Clients) to access this route, but Clients successfully pass this middleware too: Auth::check() returns true and Auth::user() returns an instance of App\Client.

Another situation: what if for some routes I want to allow both: clients and users. For other routes - guests, clients and users.

0 likes
4 replies
bobbybouwmann's avatar

You need to do the check the other way around. In your code you will always return a User since you always have a user_id. If you do it like this it should work

if ($access_token) {
    $tokendata = JWT::decode($access_token, getenv('TOKEN_SECRET'), array('HS256'));

    if ($tokendata->client_id) {
        return Client::find($tokendata->client_id);
    }

    return User::find($tokendata->user_id);
}
chimit's avatar

In this case, everybody would be logged in as Clients, because both types of tokens have client_id field. The difference between them is in user_id field, which is empty for Clients and not empty for Users.

It seems to me that the whole my approach is fundamentally wrong. Isn't it?

bobbybouwmann's avatar

Well you can do an extra check right?

if ($tokendata->client_id && !$tokendata->user_id) {
    return Client::find($tokendata->client_id);
}
chimit's avatar

Sorry, @bobbybouwmann, maybe I described my problem a little bit unclear. Currently, I came up with this code in my AuthServiceProvider.php:

$this->app['auth']->viaRequest('api', function ($request) {
            $access_token = HelperClass::getTokenFromHeader($request->headers->get('Authorization'));

            if ($access_token) {
                $tokendata = JWT::decode($access_token, getenv('TOKEN_SECRET'), array('HS256'));

                if ($tokendata->user_id) {
                    $visitor = User::find($tokendata->user_id);
                } else {
                    $visitor = Client::find($tokendata->client_id);
                }

                // Save permissions list from the JWT token into the user instance
                $visitor->scope = $tokendata->scope;

                return $visitor;
            }
        });

After that in Policies I check whether this user is Client or User:

if ($visitor instanceof User) {
    ...
} elseif ($visitor instanceof Client) {
    ...
}

In my AuthMiddleware.php I changed the third parameter of the handle method from $guard to $permissions and check its value comes from routes.php:

$app->get('/api/{item_id:\d+}', ['middleware' => 'auth:read', 'uses' => 'App\Http\Controllers\ItemController@get']);

It works well, but I'm confused with guards: what they are made for? How should I use them?

Please or to participate in this conversation.