Dingo/Api and JWT auth - laravel

I'm looking for the way to implement auth with JWT and Dingo/Api. I've added JWT package to my project. Added 'jwt' => 'Dingo\Api\Auth\Provider\JWT', into my api.php auth part.
And also added into my BaseController
public function __construct()
{
$this->middleware('api.auth');
}
How do I check if the user has permission (by role) using FormRequest? It has an authorize method, however I'm not sure how to get my user.
Since I'm using JWT the token is sent in the headers.

One way to do it is to adding the role validation to the middleware.
You can try adding this custom validation to the part where it verifies the JWT the user gave as that is the part where you can determine who is the user that owns the token.

You can use the Auth Facade to retrieve the currently authenticated user:
$user = \Auth::user()
For authorization you can use policies, see https://laravel.com/docs/5.6/authorization
Once enabled you can check for a users ability in your authorize method(s), e.g.
public function authorize() {
$user = \Auth::user();
return $user->can("do-something");
}
But there are multiple possibilities how to perform authorization checks, read the docs mentioned above for more details.

Related

Auth::check() for client credential workflow with Passport

I have a Laravel app where some routes are protected with the 'client' middleware from Laravel Passport (Example from the docs):
Route::get('/orders', function (Request $request) {
...
})->middleware('client');
When I need to explicitly check for the success of authentication in my code, I normally do this with:
\Illuminate\Support\Facades\Auth::check();
However, this function appears to always return false with the client credentials workflow. I understand that e.g. Auth::user() does not make sense in this context but Auth::check() should be well-defined in this situation.
Question: Is there an alternative to Auth::check() for the client credentials workflow or am I missing something?
You need to pass the Access Token in header first, after doing so you should be able to check auth like this:
Auth::guard('api')->check()

Magic Link login with Laravel Sanctum

For my project I have a set of users that should only be able to login by requesting a Magic Link. So they have an email address but no password. To avoid security issues, my goal was to get this working without having to save an authentication token in LocalStorage.
I've tried setting this up the following way with Laravel Sanctum:
When requested, I create a token for the user and email them the plaintext version.
The user would open the link containing the token in the querystring.
I would attach the (Bearer) token with the Authorization Header.
The next step (I assumed) would be to call a custom /api/login endpoint that uses the 'auth:sanctum' middleware. The Bearer token would authenticate the user and then I would manually login the user with Auth::login(). After this the active Session would be used to authenticate the user, thus avoiding having to save the token in localStorage.
But I can't call the Auth::login() method manually without getting an error (BadMethodCallException: Method Illuminate\Auth\RequestGuard::login does not exist.).
I can't figure out why this isn't working, or maybe I am going at this all wrong?
if you sending Sanctum token to user via email so in 1st request you will get token from url and you can use that token to login to application like this
use Laravel\Sanctum\PersonalAccessToken;
public function login(Request $request)
{
$personalAccessToken = PersonalAccessToken::findToken($request->token);
$user = $personalAccessToken->tokenable;
auth()->login($user);
return redirect('/');
}

Laravel passport: assigning the scopes to access token overriding oauth/token route

I am working on a Laravel project. I am using Laravel Passport for API authentication. I am trying to assign the scopes to the access token based on the user role after the user has logged in or generated the access token through oauth/token route with passport grant-type. How can I override it?
I cannot do this as mentioned in the documentation.
$token = $user->createToken('My Token', ['place-orders'])->accessToken;
Because it is explicitly generating the token. It seems like I have to write my own login method to use that. How can I override the oauth/token route instead?
I'm not exactly sure of your use case, but if you are authenticating over OAuth, you would include your scopes in the /authorize GET request as per the OAuth spec.
i.e.
https://localhost/oauth/authorize?response_type=code&client_id=abc123 &scope=myfirstscope,mysecondscope etc.
See OAuth spec here:
https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1
You can also define a default scope with:
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
Passport::setDefaultScope([
'check-status',
'place-orders',
]);
https://laravel.com/docs/9.x/passport#default-scope

Laravel Gates, how to use them in the API route?

I'm defining my Gates for my API service in AuthServiceProvider (following the Laravel docs https://laravel.com/docs/7.x/authorization#gates):
Gate::define('view-users', function ($user) {
return $user->hasAccess(['view-users'])
or $user->inRole(['admin', 'operator']);
});
Here is my route:
Route::group(['namespace' => 'Api', 'middleware' => ['auth:api']], function () {
Route::get('/users', 'UserController#listing')->name('user.listing')->middleware(['can:view-users']);
});
How can I find the get the user from the Route API file to use in the Gate?
But I don't exactly understand where this $user is coming from. In my request I am sending an Authorization Bearer token. Should I be using this within my gate to fetch the correct user from the DB? How does Laravel know who $user is?
The $user is the current logged in user. You do not need to provide the additional $user, Or pass the user in.
So if your app currently has a login user, that will be the one. If there is no login user the gate will return false which is protecting your resources.
You'll notice from the documentation that Laravel provides the User for you in a Gate.
It says (emphasis mine):
Gates are Closures that determine if a user is authorized to perform a given action and are typically defined in the App\Providers\AuthServiceProvider class using the Gate facade. Gates always receive a user instance as their first argument.
As Andy Song has pointed out in the comments, Laravel will resolve the User via Auth. If there is no user (not logged in, or no authentication), then, per the docs:
By default, all gates and policies automatically return false if the incoming HTTP request was not initiated by an authenticated user.
If you want to trace how your user gets authenticated, your code snippet defines this middleware:
'middleware' => ['auth:api']
This uses the auth middleware, with api as a parameter. Your middleware is defined in app/Http/Kernel.php, and assuming you're using stock Laravel, it will then go on to authenticate your user, prior to your gate check taking place.
Laravel doesn't know who the $user is. You need to pass it when you use the Gate. If you pass Auth::user() as a first argument of the Gate you are using, the code you wrote will work. In other cases, you will need to fetch the user from the Database with any given input.

Using client credentials middleware for all API requests

In my routes/api.php file, I have a route group like this:
Route::group([
'prefix' => config('api.route_prefix'),
'middleware' => ['api', 'auth:api'],
], function() {
// ...
This correctly only allows users with tokens retrieved via password grant access to those routes. When trying to implement client credentials grant, I found that a separate middleware is necessary. Since the auth:api middleware raises an exception, this presents a conflict because I want requests with valid tokens of either grant type to access these routes.
What I found is that using just the client credential middleware seems to validate both, but I am unsure if there are any bad implications of doing so.
Is there anything wrong with circumventing the auth:api middleware and replacing it with Laravel\Passport\Http\Middleware\CheckClientCredentials?
One apparent big downside is that the client credentials doesn't seem to have any user information in the JWT token. This causes the user resolver for the request to return null for calls to request()->user(). From Laravel\Passport\Guards\TokenGuard::authenticateViaBearerToken, this was returning null:
// If the access token is valid we will retrieve the user according to the user ID
// associated with the token. We will use the provider implementation which may
// be used to retrieve users from Eloquent. Next, we'll be ready to continue.
$user = $this->provider->retrieveById(
$psr->getAttribute('oauth_user_id')
);
Tracing $psr->getAttribute led me to League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator::validateAuthorization:
// Return the request with additional attributes
return $request
->withAttribute('oauth_access_token_id', $token->getClaim('jti'))
->withAttribute('oauth_client_id', $token->getClaim('aud'))
->withAttribute('oauth_user_id', $token->getClaim('sub'))
->withAttribute('oauth_scopes', $token->getClaim('scopes'));
All of the attributes except oauth_user_id were being set correctly via the claims on the token, $token in my case is an instance of Lcobucci\JWT\Token. So only using the client credentials middleware is not a good solution to having a single set of routes, even if using an oauth client with a specified user_id.

Resources