API login from android app using laravel 5.3 passport - laravel

For two days I am digging google but could not find the starting thread for my problem, now I am out of option. Please help me with some direction/howTo
I have a web application running built with laravel 5.3, I have installed passport as described here . if I go /home its showing perfectly.
Now I have to make an android app from which
An already existing user of web app can login
get all the task list of that user TaskModel (ons_tasks(id, title, description))
routes related only
in web.php
Auth::routes();
in api.php
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:api');
Route::group(['middleware' => ['auth:api']], function () {
Route::get('/test', function (Request $request) {
return response()->json(['name' => 'test']);
});
Route::get('/task/list', function (Request $request) {
$list = \App\Model\TaskModel::all();
return response()->json($list);
});
});
To login : if I send post request /login with email & password get the TokenMismatchException error but Where do I obtain a token for
android app in mobile? Do I need the Auth::routes() in the api too?
if then what else Do I need to just login and get a token so later I
can send it for getting the task lists.
Secondly,
If I go to /api/test it redirects me to /home page without
showing any error !!!
Thanks in advance.

To authenticate with your Passport-enabled API
You'll need to use the Password Grant Client in this situation, see this section of Passport's documentation.
Once you've generated a Password Grant Client, using:
php artisan passport:client --password
You will need to request an access token from your application, and send it with your subsequent requests, in order to access the protected auth:api middleware routes.
To get an access token, send a request to your app's /oauth/token route (this is a PHP implementation obviously, ensure you are correctly formatting below request in your Java implementation):
$http = new GuzzleHttp\Client;
$response = $http->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => '<client id returned from the artisan command above>',
' client_secret' => '<secret returned from artisan command above>',
'username' => 'taylor#laravel.com',
'password' => 'my-password',
'scope' => '',
],
]);
return json_decode((string) $response->getBody(), true);
Ensure you add the client_secret and client_id that was returned from the artisan call above, and ensure username and password references a valid user in your database.
If everything is fine here, you should receive an access_token and refresh_token in the response. The access_token is what you need to authenticate using the auth:api guard. To correctly pass this back to your api, you will need to send your subsequent requests with the headers Authorization: Bearer <your accessToken> and Accept: application/json
For example, to access your "test" route:
$response = $client->request('GET', '/api/test', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '. <accessToken from /oauth/token call>,
],
]);
If you've set these correctly, you should see a JSON response with the array you have specified.
Why is /api/test redirecting me with no error?
You are requesting a route with the auth:api middleware. This will redirect you as you have not specified the correct headers as described above, this is expected behavior.
Hope this helps.

Related

Laravel API response Unauthenticated even when Authentication is passed

I am using the jwt for creating the tokens while login. After I login, I try to hit the /me api pointing to the function:
public function me()
{
$user = auth()->user();
return response()->json($user);
}
I followed the JWT official documentation, initially I was able to get the response for the API. Suddenly it started throwing a
{
"message": "Unauthenticated."
}
Why is this happening?? Is there any workaround? It would be great if someone could help.
i tried documentation setup and worked fine, you might missed passing authentication header in your api call. since idk what's your setup i can only tell when you logged in, you should use received token in api calls with authentication.
PostMan Software: In headers tab add a key as Authorization and assign token for value with Bearer, like Breaer token......
for more help please clarify how you're trying api calls.
Edit: added an alternate way for using middleware
Another way of implementing or using middleware :
Create a Middleware with JWT name and put below code in handle function
Don't forget to import
use JWAuth;
public function handle($request, Closure $next)
{
JWTAuth::parseToken()->authenticate();
return $next($request);
}
Then in Kernel add jwt to $routeMiddleware like this :
protected $routeMiddleware = [
// you should add below code.
'jwt' => \App\Http\Middleware\JWT::class,
];
in routes/api
Route::apiResource('/posts', 'PostController');
now in PostController add your middleware to Constructor like this.
public function __construct()
{
$this->middleware('jwt', ['except' => ['index','show']]);
}
So in construct you will set your middleware base on JWT, then with except you can modify which one of your functions don't need to authentication base on JWT token. now when you use auth()->user() you can get your info or etc.
So if i had index, show, update, delete, store, create when i try to do API call if i use GET METHOD for url.com/posts or url.com/posts/23 i can get my posts without passing JWT token.
When you tried to use JWT you should realize that it's working base on token you're passing, you're getting token when you using login, but you're not getting user info because you're not passing user's token to app, before all of this you should consider to verify token then do the rest Logics. Good Luck.
Edit : added more info
auth.php
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],

401 Unauthorized when creating Password Grant Tokens with laravel passport

I have created an api with passport and want to use password grant tokens because it is more of application to application interactions. But i get below error when i try to access it.
GuzzleHttp\Exception\ClientException
Client error: `POST http://127.0.0.1:8000/oauth/token` resulted in a `401 Unauthorized` response: {"error":"invalid_client","error_description":"Client authentication failed","message":"Client authentication failed"}
my route
Route::get('/get_token_by_password', function (Request $request) {
$http = new GuzzleHttp\Client;
$response = $http->post('http://192.168.0.103:8000/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 13,
'client_secret' => 'f37AwQGsVMiZDVu786KiRdbpn4MXYSBWCvqNcqiC',
'username' => 'developer#ymail.com',
'password' => '123456789',
'scope' => '*',
],
]);
return json_decode((string) $response->getBody(), true);
})->name('get_token_by_password');
What might I be doing wrong?
Also, how do I use postman to get the token?
How do i get a link to be used in other applications that want to consume my api
The request seems correct. Check if you have correct keys and client. After, check in DB the new value of client_secret.
php artisan passport:keys
php artisan passport:client --password
Your client id and/or client secret is not correct, since the error is "invalid_client".

What Need to Pass for Logout Passport Function

Using Passport for Access Token and Refresh Token. For Logout function what should be Posted/Get and What will be the URL?
API Route:
Route::group([
'middleware' => 'auth:api'
], function() {
Route::get('logout', 'api\LoginController#logout');
});
public function logout(Request $request)
{
$request->user()->token()->revoke();
return $this->loggedOut($request);
}
URL: http://localhost:8000/api/auth/logout ???
What should be posted for this URL ?
If you have a prefix of auth then that should be a correct logout route. If not, remove auth from url.
Token should be present in axios headers, since the logout route is under auth:api middleware, and you will revoke it inside logout method.
Code snippets as requested in the comment
Once you login the user you add token to axios headers
axios.defaults.headers['Authorization']='Bearer ' + token;
axios.defaults.headers['Content-Type']='application/json';
When you make logout request
axios.get('/api/auth/logout')
.then(response => {
localStorage.removeItem("token");
localStorage.removeItem("token_type");
delete axios.defaults.headers['Authorization'];
}).catch(error=> {
});
And in your logout method in controller you revoke the user token
$request->user()->token()->revoke();
return response()->json([
'message' => 'Successfully logged out'
]);
You have to pass access_token in Authorization header which you have got after successfully logged in.
Your logout route is protected by passport so When calling routes that are protected by Passport, your application's API consumers should specify their access token as a Bearer token in the Authorization header of their request.
For example, when using the Guzzle HTTP library:
$response = $client->request('GET', '/api/logout', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
],
]);
from the doc laravel passport passing access token

Laravel undefined method revoke

I am test building an application, using Laravel, Laravel passport and socialite to login and logout users in a vue SPA. No problem to login and create users, it is only when I try to logout users that I get the error: call to undefined method: revoke
This is in Auth\LoginController:
public function logout(Request $request) {
$request->user()->token()->revoke();
return response()->json([
'message' => 'Successfully logged out.'
]);
}
This is in Api.php:
Route::group(['middleware' => 'auth:api'], function(){
Route::post('/logout', 'Auth\LoginController#logout');
});
This is axios called in vue SPA:
logout() {
axios.post('/api/logout')
.then(res=>{
console.log(res.data);
});
}
If revoke works I should get the message for successfully logged out. Any help here is appreciated.
Additional: in my LoginController handleProviderCallback function to handle the socialite logins I have this
auth()->login($user);
$tokenResult = $user->createToken('Personal Access Token');
$token = $tokenResult->token;
$token->expires_at = Carbon::now()->addWeeks(1);
$token->save();
return response()->json([
'access_token' => $tokenResult->accessToken,
'token_type' => 'Bearer',
'expires_at' => Carbon::parse($tokenResult->token->expires_at)->toDateTimeString()
]);
The outcome is when users click on social logins, it shows the access token. I have always thought laravel_token from cookies is the JWT which Laravel automatically handles. Now I am not very sure. So if with this additional code, the access token is the right way to handle JWT, how do I pass the response into Vue since it is a redirect from socialite, not an Axios request?
After the JWT is tested i can try out on the logout again to see if the JWT is the issue.
Are you sure that the user has a token ? And are you using the HasApiTokens trait in your User model ?
I have solved this with the default Auth::logout(). The code for the 'Personal Access Token' is not needed. In the documentation of passport Laravel attaches a JWT in a cookie called laravel_token which passport will check if user is authenticated.

Conception, separate Laravel Passport in resource and authorization server

Ok, i've a small question of application conception. I want to distinct the ressource and the authorization server with laravel passport.
I've an OAuth2 server and a classic laravel app with stateless authentification system homemade.
I've an SPA with Vue.js which send login request to an REST interface to the front laravel app.
This app called GuzzleClient to send an oauth password grant-type request to get a token.
public function login(Request $request, Client $client)
{
$url = config('app.auth.server') .'/oauth/token';
$response = $client->post($url, [
'form_params' => [
'grant_type' => 'password',
'username' => $request->name,
'password' => $request->password,
'client_id' => config('auth.oauth.password.client-id'),
'client_secret' => config('auth.oauth.password.client-secret'),
]
]);
if ($response->getStatusCode() !== Response::HTTP_OK) {
return http_response_code($response->getStatusCode());
}
return response()->json($response->getBody()->getContent(), Response::HTTP_OK);
}
But, if i want to protect an API route, i cannot use the auth:api guard provided by passport.
2 solutions.
Create middleware in front app which call a custom route created in authorization server (laravel passport) to send bearer access_token (sent from javascript) and verify it's validity by the OAuth server.
Get the secret key used by passport for jwt and let the resource server verify itself the token validity. (using custom guard)
The second solution is probably better. But what do you think ? Do you have any idea or best practices ?
Thx for reading.

Resources