Using client credentials middleware for all API requests - laravel

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.

Related

Laravel sanctum API, retrieve the token for use in view components

Here is the context. I have two sites using the same domain.
The first using Laravel and view components The second is an "API", I use Laravel Sanctum. This API has a single user. Later, there will be a third site using this same API as well.
The API authentication system works perfectly. When I switch from Postman my user, my token is returned.
I'm wondering about token retrieval and usage on my first site using vue components.
My idea was to store API user login credentials in my .env file and retrieve the token in controllers where I use vue components.
Another solution would be to fetch my token on each call to the API, but that could be cumbersome.
Finally store the token at the user's connection and reuse it but where to store it, in session, in cookies,... Security level is not ideal.
Thanks in advance for your ideas.
The default thing to do to use token as a bearer token from the front end is as following
login attempt to backend and you will get the token to authenticate your request later.
store it using vuex store in you user store
then do your API request using that token
destroy the token after logout
if you are concerning about security, well the token should have expiration time and the token itself should be destroyed from the backend when the user is logged out. Every time the user do the login, new token will be created for them
If your API authentication is working properly, Then you've to store your token on the database every time the user logs in. And delete on some other occasions. To store your token on database every time the user logs in use the following code:
public function login(Request $request)
{
if (!Auth::attempt($request->only('email', 'password'))) {
return response()->json([
'message' => 'Invalid credential'
], 401);
}
$user = User::where('email', $request['email'])->firstOrFail();
//store the hashed token on db and return plain text token for the user
$token = $user->createToken('token_name')->plainTextToken;
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
]);
}
I took the code from here

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.

Check if user is authenticated in laravel API route

So I know API routes are not supposed to rely on sessions authentication, but my idea was to create some API routes, that could be used if needs be by third-party with proper authentication, but that could also be used internally, ie called to get the data I need for my web pages.
I changed the LoginController so that every time a user logs in, a Personal Access token is generated and stored in the database. When logging out, this token is deleted.
So as not to expose the token to the client side, I would like to use a middleware, that would detect, on an API call, if the request comes from a user who is already authenticated. If that's the case, I would retrieve the Personal Access token that belongs to the user, attach it to the request, and pass it onto the API.
Browser -- Query site.com/api/myRoute --> Middleware adds user's token to request if Auth::check() -- Pass-on request --> Controller
I've created a dummy API route to see if I can detect whether a user is authenticated, but that doesn't seem to work... I guess because the 'auth' middleware is not included.. however, if I do include it, I get redirected to home on every request...
Route::get('/test', function() {
if(Auth::check()) {
dd('Hello logged-in');
} else {
dd('Hello not logged-in');
}
});
Any lead on how to achieve that much appreciated!

Laravel 5.3 and VueJS 2 Authenticating

Im using Laravels default auth to lock down some paths in the routes/api.php file.
Route::get('/projects', 'ProjectController#index')->middleware('auth:api');
I log the user in via a PHP form (not via a http request via Vue).
This creates the session and when I submit a HTTP request via Vue I can see that the header includes the Cookie and X-CSRF-Token however I keep getting a 401 {"error":"Unauthenticated."}
In my config/auth I have api driver set as 'token' (have tried changing this to 'session' but that did work :/)
From my understanding and what I have read online I should be able to use the default Laravel auth functionality to accomplish API calls from Vue HTTP requests.
this.$http.get('/api/projects')
.then(response => {
this.projects = response.body;
})
.catch (err => {
console.log(err);
});
I've read about methods of authenticating by generating an JWT token and storing that in local storage when the user logs in. Is this the method I should use or should I be able to accomplish it with the default Laravel Auth middleware?
Hope my questions make sense, any help/advice would be appreciated.
The auth:api middleware doesn't use cookies, it uses api_token param, which can be passed via get or Bearer <token> header. Just use web middleware.
I suppose you need to access the same route in two ways - for API users and for browser users. So why don't you create two routes for one action?
// api group with /api prefix
Route::get('/projects', 'ProjectController#index')->middleware('auth:api');
// web group
Route::get('/projects', 'ProjectController#index')->middleware('web');

Resources