I did an API REST with Laravel and now I'm trying to consume it. The thing is I need to authenticate users in the API and I am using the Password Grant method. I can authenticate users correctly and I can get an access token but from then, I don't see a way to retrieve the authenticated user with the access token in my consuming application.
I tried in the API with a route like this:
Route::get('/user', function(Request $request) {
$user = $request->user();
// Even with
$user = Auth::user();
return $user;
});
No dice. I am reading Passport code but I can't figure it out. My guess is that I would need to specify a new guard type or something because It doesn't seem that Laravel Passport provides one for this kind of grant type...
To clarify things:
I have an API REST application, which is the oAuth2 Server.
I have another application consuming the API REST.
I do know the workflow. In my case, with Password Grant, I get the user credentials in my consumer application, then I make a request to /oauth/token specifying the grant_type to password, I provide the user credentials along with my client credentials, which I am sure they were generated with "php artisan passport:client --password" (note the --password option)
I can get the access token with no problems. What I need now, is to get a JSON representation of the user I just authenticated from the API REST. But here is the problem: I just have an access token. Nothing I can relate with the user.
Or can I? Maybe I can extend the method that authenticates password grant requests to relate the generated access token to the user it is authenticating... *light bulb turns on*
Consuming application test code:
try {
$client = new Client();
$result = $client->post('https://myapi.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => '5',
'client_secret' => 'my_secret',
'username' => 'user_I_am_authenticating',
'password' => 'the_user_password',
'scope' => '',
]
]);
$access_token = json_decode((string) $result->getBody(), true)['access_token'];
$result = $client->get('https://myapi.com/client/user', [
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Authorization' => "Bearer $access_token",
]
]);
return (string) $result->getBody();
} catch (GuzzleException $e) {
return "Exception!: " . $e->getMessage();
}
Note that https://myapi.com/client/user route is just a route I made for testing in the API. That route is defined as:
Route::get('/user', function(Request $request) {
return $request->user();
});
Now. I know this is not working. This is what I want to achieve. Know the user making the request given the access_token/bearer_token.
You forgot the appropriate middleware.
Route::get('/user', function(Request $request) {
return Auth::user();
})->middleware('auth:api');
The authentication flow is not fired when you don't mention the auth middleware. That's why you get null.
I had the same problem with you. And i solved it after I manually defined the auth guard.
Route::get('/user', function (Request $request) {
return auth()->guard('api')->user();
});
You need to pass the Access token back with every request. Please check the documentation for this part here
Related
I am using guzzle HTTP client to issue a password grant based access token after successful user login. I am using passport package for oauth and I have done all the setup which includes the Password Grant Client it creates. In my login controller I override the sendLoginResponse method of the AuthenticatesUsers trait so as to issue an access token on successful email/password authentication
public function sendLoginResponse(Request $request)
{
try {
Log::debug("Auth attempt sucessful, obtaining access_token for user :: ".$request->email);
$client = new Client();
$token_response = $client->post(config('app.url').'/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => config('auth.password_grant.client.id'),
'client_secret' => config('auth.password_grant.client.secret'),
'username' => $request->email,
'password' => $request->password,
'scope' => '*',
],
]);
if($token_response->getStatusCode()!=200) {
Log:error("Login failed to generate Access Token");
throw new InvalidCredentialsException();
}
$request->session()->regenerate();
$this->clearLoginAttempts($request);
$data = json_decode((string) $token_response->getBody(), true);
Cookie::queue('refresh_token',$data->refresh_token,config('auth.tokens.refresh.expire.days')*1440);
Log::debug("Adding Bearer token to Authorization header");
return response()->view('dashboard', [
'expires_in' => $data->expires_in
], 200)->header('Authorization', $data->token_type.' '.$data->access_token);
} catch(Exception $e){
Log::error('Error :: '.$e->getMessage());
throw $e;
}
}
The whole PHP process goes unresponsive when I make this post request, and there are no errors in any of the logs. Exactly at this line
$token_response = $client->post($token_url, .......
I ran this in Debug session; and the URL, Client ID and Secret are generated correctly via configuration properties; the only Exception I could see was a FileNoFoundException that occurs when it does find any cache key for throttle login and that all happens much before this call is made and the app proceeds to authenticate the user.
When I make this very request with same parameters through Postman or via artisan tinker I can get a response with access_token, refresh_token and expires_in data.
A couple of hours with 'Hit And Trial' does really save you 10 minutes of going through 'Documentation'.
Turns out I really don't have to do all this heavy lifting this link shows how we can add the \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, to web middleware in app/http/Kernel.php which takes care of generating ApiToken for first party apps such as the React JS which I'd be using to consume my own API.
While that solved the intent of writing all this code, I'm still not sure what was causing the process unresponsiveness making the access_token from within the php code.
I just read the https://laravel.com/docs/5.6/passport documentation and I have some doubts that hopefully someone could help me with:
First, some context, I want to use Passport as a way to provide Oauth authentication for my mobile app (first-party app).
When I use php artisan passport:client --password I get back a Client ID and a Client Secret. Does this value have to be fixed on my app? for example storing them hardcoded or as a "settings" file? If the values shouldn't be stored then how should it work?
To register a user to my app I use: $user->createToken('The-App')->accessToken; I get that the accessToken will be the one used for sending on all my requests as a Header (Authorization => Bearer $accessToken) but what exactly is "The-App" value for?
For login the user I'm using the URL: http://example.com/oauth/token and sending as parameters:
{
"username": "user#email.com",
"password": "userpassword",
"grant_type": "password",
"client_id": 1, // The Client ID that I got from the command (question 1)
"client_secret": "Shhh" // The Client Secret that I got from the command (question 1)
}
When I login the user using the previous endpoint I get back a refresh_token, I read that I could refresh the token through http://example.com/oauth/token/refresh but I try to request the refresh I got Error 419, I removed the url oauth/token/refresh from the csrf verification and now I get back "message": "Unauthenticated.", I'm making the following request:
Content-Type: x-www-form-urlencoded
grant_type: refresh_token
refresh_token: the-refresh-token // The Refresh Token that I got from the command (question 3)
client_id: 1 // The Client ID that I got from the command (question 1)
client_secret: Shhh // The Client Secret that I got from the command (question 1)
scope: ''
Should I use this endpoint? or is not necessary given the app I'm trying to develop.
Finally, there are a lot of endpoints that I get from passport that I don't think I will use for example: oauth/clients*, oauth/personal-access-tokens* is there a way to remove them from the endpoints published by passport?
Thanks a lot for your help!
If you are consuming your own api then you don't need to call http://example.com/oauth/token
for user login because then you need to store client_id and client_secret at app side. Better you create an api for login and there you can check the credentials and generate the personal token.
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
// Authentication passed...
$user = Auth::user();
$token = $user->createToken('Token Name')->accessToken;
return response()->json($token);
}
}
Finally, there are a lot of endpoints that I get from passport that I
don't think I will use for example: oauth/clients*,
oauth/personal-access-tokens* is there a way to remove them from the
endpoints published by passport?
You need to remove Passport::routes(); from AuthServiceProvider and manually put only required passport routes. I think you only need oauth/token route.
what exactly is "The-App" value for?
if you check oauth_access_tokens table it has name field. $user->createToken('Token Name')->accessToken; here the "Token Name" stored in name field.
How to use Laravel Passport with Password Grant Tokens?
To generate password grant token you have to store client_id and client_secret at app side (not recommended, check this ) and suppose if you have to reset the client_secret then the old version app stop working, these are the problems. To generate password grant token you have to call this api like you mention in step 3.
$http = new GuzzleHttp\Client;
$response = $http->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => 'taylor#laravel.com',
'password' => 'my-password',
'scope' => '',
],
]);
return json_decode((string) $response->getBody(), true);
Generate token from refresh_token
$http = new GuzzleHttp\Client;
$response = $http->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'refresh_token',
'refresh_token' => 'the-refresh-token',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'scope' => '',
],
]);
return json_decode((string) $response->getBody(), true);
You can look this https://laravel.com/docs/5.6/passport#implicit-grant-tokens too.
Tackling Question 5
Finally, there are a lot of endpoints that I get from passport that I don't think I will use for example: oauth/clients*, oauth/personal-access-tokens* is there a way to remove them from the endpoints published by passport?
Passport::routes($callback = null, array $options = []) takes an optional $callback function and optional $options argument.
The callback function takes a $router argument from which you can then choose which routes to install as shown below in your AuthServiceProvider.php that is enabling a more granular configuration:
Passport::routes(function ($router) {
$router->forAccessTokens();
$router->forPersonalAccessTokens();
$router->forTransientTokens();
});
Passport::tokensExpireIn(Carbon::now()->addMinutes(10));
Passport::refreshTokensExpireIn(Carbon::now()->addDays(10));
This way we only create the passport routes that we need.
forAccessTokens(); enable us to create access tokens.
forPersonalAccessTokens(); enable us to create personal tokens although we will not use this in this article. Lastly,
forTransientTokens(); creates the route for refreshing tokens.
If you run php artisan route:list you can see the new endpoints installed by Laravel Passport.
| POST | oauth/token | \Laravel\Passport\Http\Controllers\AccessTokenController#issueToken
| POST | oauth/token/refresh | \Laravel\Passport\Http\Controllers\TransientTokenController#refresh
I am developing a laravel back-end to work with Vue JS, and trying to implement user registration. I have downloaded passport to authenticate users, but I am having trouble understanding exactly how a new user registers for the website.
From the research I have done it seems like you would want to make a route for the registration that can be accessed without using laravel passport, and then once the user is created grant it a token.
Once the user is registered Would I use a personal grant token, Implicit, or use passports CreateFreshApiToken middleware.
Should I put all my routes in web or the api route file?
You may create a route in your api where anybody can register a new user , so you do not use any auth middlewares, like for example:
Route::post('users', 'AuthController#register'); // Signup
and in your controller the related method:
/**
* API Register
*
* #param Request $request
* #return \Illuminate\Http\JsonResponse
*/
public function register(Request $request)
{
$rules = [
'name' => 'unique:users|required',
'email' => 'unique:users|required',
'password' => 'required',
];
$input = $request->only('name', 'email','password');
$validator = Validator::make($input, $rules);
if ($validator->fails()) {
return response()->json(['success' => false, 'error' => $validator->messages()]);
}
$name = $request->name;
$email = $request->email;
$password = $request->password;
$user = User::create(['name' => $name, 'email' => $email, 'password' => Hash::make($password)]);
}
from this point the type of passport OAuth2 authorization code flow you choose will influence your implementation on the consumer app.
You may go for the classical OAuth2 flow where basically you have this steps:
Register the consumer application to the OAuth2 server and obtain the Client Id and Secret
The consumer application request an authorization code to the OAuth2 server using the Client Id and Secret
Once obtained the authorization code the consumer application can now request an access token to the OAuth2 server
The consumer application can now have access to the Api using in every request to the server its access token that it is sent in the header request.
Obviously each step above is an HTTP request and how you do it depends on the technology you use in the consumer application.
For example in php, you can use Guzzle and send an access token request like this:
$http = new \GuzzleHttp\Client;
$response = $http->post('http://yourserver.com/oauth/token', [
'form_params' => [
'grant_type' => 'authorization_code',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'redirect_uri' => 'http://example.com/callback',
'code' => $request->code,
],
]);
On the other side you may want to use a simpler way to retrieve access tokens, avoiding all the authorization code request etc. using Laravel passport Personal Access Tokens. In this way you can issue access tokens for the user simply doing:
$user = App\User::find(1);
// Creating a token without scopes...
$token = $user->createToken('Token Name')->accessToken;
I encourage you to take a look to the Passport Laravel documentation and check all the possibilities offered.
I have set up the Laravel Passport package for Laravel 5.3 just as described in the official documentation (https://laravel.com/docs/5.3/passport#introduction).
I want the API to be consumed by a mobile application, so I am trying to implement Password Grant Tokens. I have created a password grant client, and the token request process...
$response = $http->post('http://my-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => 'my#email.com',
'password' => 'my-password',
'scope' => '',
],
]);
...Just works as expected, returning an access-token and a refresh-token for one of my users.
On the one hand,
php artisan route:list
Lists correct middleware for api/user URI: api,auth:api
And driver for api guard is correctly set to passport in config/auth.php.
Summing up, every step of the installation process has been done (https://laravel.com/docs/5.3/passport#installation).
Defaults contents of api.php:
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:api');
The problem comes when I access to http://my-app.com/api/user, because it seems it is authenticating the request using the 'web' middleware, not the 'api'...
When I access, I am redirected to /login (login form) if the user was not logged in, and to /home if it was...
Any help would be really appreciated.
Thanks in advance.
Solved! Just for the record, the solution:
I was sending the request to http://my-app.com/api/user with HTTP Header wrong. I was sending:
Type: Authorization - Content: Bearer: $accessToken
...and the correct way was:
Type: Authorization - Content: Bearer $accessToken (without colon)
I never thought it could be a typo... Anyway, the error was not easy to detect because the redirection to the login form misleaded me from the beginning. I believe it was such an strange behaviour indeed...
The correct solution is removing redirectTo() from this file Authenticate middleware in app/http/middleware/Authenticate.php
I'm building a Laravel API which authenticates users using an authentication token. For any routes that need authentication, I'm wrapping them in an auth filter:
Route::group(array('before' => 'auth'), function() {
Route::get('user/account', 'UserController#getAccountDetails');
});
My auth filter basically decrypts the passed in authentication token and checks if it's valid:
Route::filter('auth', function()
{
// Try catch because Crypt::decrypt throws an exception if it's not a valid string to decrypt
try {
$authToken = Crypt::decrypt(Request::header('Authorization'));
// If there's a user tied to this auth token, it's valid
$user = AuthToken::where('token', '=', $authToken)->first()->user()->first();
if (!$user) {
throw new \Exception();
}
// Make the user globally accessible in controllers
} catch (\Exception $e) {
return Response::json([
'data' => [
'error' => 'You must be logged in to access this resource.'
],
'success' => false,
'status' => 403
], 403);
}
});
Pretty simple stuff, but I'm stuck on the next part. I want to be able to easily retrieve the current user record in my controllers and models.
For example, if I used Laravel's Auth library I could get the current user by doing Auth::user() in my controllers. I'd like to have that kind of functionality but I'm not sure how to build it. Could I write a class that gets instantiated after authentication with a static method that returns a User model?
Not sure if that's an option for you, but maybe you would like to use oauth2 instead of writing "your own" token based authentication?
There is quite nice ouath2 server wrapper for laravel project: oauth2-server-laravel.
According to it's documentation you can (for example for password flow authentication) put this in it's config:
'password' => array(
'class' => 'League\OAuth2\Server\Grant\Password',
'access_token_ttl' => 604800,
'callback' => function($username, $password){
$credentials = array(
'email' => $username,
'password' => $password,
);
$valid = Auth::validate($credentials);
if (!$valid) {
return false;
}
return Auth::getProvider()->retrieveByCredentials($credentials)->id;
}
)
And than you can you can authenticate (via username and password in that case) sending post request like that:
POST https://www.example.com/oauth/access_token?
grant_type=password&
client_id=the_client_id&
client_secret=the_client_secret&
username=the_username&
password=the_password&
scope=scope1,scope2&
state=123456789
Request will return generated token, and then you can make api calls as usual, just putting the token in the post data.
In your api logic getting the user by token is quite simple in that case, just run:
User::find(ResourceServer::getOwnerId());
It will makes stuff like: refresh tokens, other grant flows, scope access, clients management a lot easier. Out of the box in fact.
You can also secure any particular route like that:
Route::get('secure-route', array('before' => 'oauth', function(){
return "oauth secured route";
}));
You can find more details in oauth2-server-laravel documentation: https://github.com/lucadegasperi/oauth2-server-laravel
And oauth2 documentation: http://oauth.net/documentation/
It is true that the Auth::user() method is quite convenient. So, why not simply extend the Auth class to write your own authentication driver ? You can find all needed doc here.
You can then just use the Auth facade just like in every other laravel app you could write… wonderful, isn't it ?