Laravel Passport Authenticate User Before Authorize - laravel

I am working on a project where 3rd party apps can access data from Laravel server. I also have created a client application in laravel for testing.
Following code ask for authorization and its working fine.
Route::get('/applyonline', function () {
$query = http_build_query([
'client_id' => 5,
'redirect_uri' => 'http://client.app/callback',
'response_type' => 'code',
'scope' => '',
]);
return redirect('http://server.app/oauth/authorize?'.$query);
});
How can I authenticate a user before authorization? Right now I can access data form server using this code.
Route::get('/callback', function (Request $request) {
$http = new GuzzleHttp\Client;
$response = $http->post('http://server.app/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 2,
'client_secret' => 'fcMKQc11SwDUdP1f8ioUf8OJwzIOxuF8b2VKZyip',
'username'=> 'ali#gmail.com',
'password' => 'password',
],
]);
$data = json_decode((string) $response->getBody(), true);
$access_token = 'Bearer '. $data['access_token'];
$response = $http->get('http://server.app/api/user', [
'headers' => [
'Authorization' => $access_token
]
]);
$applicant = json_decode((string) $response->getBody(), true);
return view('display.index',compact('applicant'));
});
Although above code works fine but I don't think its a good way to ask username and password at client side.
I want to use this flow (Same as facebook allows)
Click To Get Data From Server
Enter Username and Password
Authorize App
Access data for authenticated user

Well that was a stupid mistake. It works fine with authorization_code grant type. My mistake was that I was testing both server and client in same browser without logout. So client was accessing its own data from server. Also this flow diagram really helped me to understand the process of passport authorization.
http://developer.agaveapi.co/images/2014/09/Authorization-Code-Flow.png
Route::get('/callback', function (Request $request) {
$http = new GuzzleHttp\Client;
$response = $http->post('http://server.app/oauth/token', [
'form_params' => [
'grant_type' => 'authorization_code',
'client_id' => 5,
'client_secret' => 'fcMKQc11SwDUdP1f8ioUf8OJwzIOxuF8b2VKZyip',
'redirect_uri' => 'http://client.app/callback',
'code' => $request->code,
],
]);
return json_decode((string) $response->getBody(), true);});

Related

Lumen Passport (dusterio/lumen-passport) - make login accessible

I am a novice with Lumen and have recently integrated dusterio/lumen-passport via composer into my project. Following a tutorial I have successfully created authentication for 'client' instances, so I am able to send variables
grant_type: client_credentials
client_id: {my id}
client_secret: {my secret}
to /oauth/token and get a bearer token. That is working great.
What I need to be able to do, and I cannot find sufficient documentation anywhere, is to create user login functionality. This is so that I can hook a UI up to the Lumen API and users be able to enter their email address and password to get access. If any one has any information to help me achieve this I would be extremely grateful. Below are edits I have made to set up the passport process...
bootstrap/app.php
$app->routeMiddleware([
'client.credentials' => Laravel\Passport\Http\Middleware\CheckClientCredentials::class,
]);
$app->register(App\Providers\AuthServiceProvider::class);
$app->register(Laravel\Passport\PassportServiceProvider::class);
$app->register(Dusterio\LumenPassport\PassportServiceProvider::class);
config/auth.php
'defaults' => [
'guard' => env('AUTH_GUARD', 'api'),
'passwords' => 'users'
],
'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users'
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => \App\User::class
]
],
routes/web.php
$router->group(['middleware' => 'client.credentials'], function () use ($router) {
$router->get('/test', 'TestController#index');
});
The way I did it with my laravel based client (seperate apps) was to save the token to a cookie which gets called each request using middleware to authenticate the request heres my code.
<?php
namespace App\Http\Controllers;
use GuzzleHttp\Client;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cookie;
class AuthController extends Controller {
public function __construct()
{
}
public function showLoginPage()
{
return view('Login');
}
public function attemptLogin(Request $request)
{
$client_id = env('CLIENT_ID');
$client_secret = env('CLIENT_SECRET');
$username = $request->input('email');
$password = $request->input('password');
$guzzle = new Client;
$response = $guzzle->post('https://api.domain.com/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => $client_id,
'client_secret' => $client_secret,
'username' => $username,
'password' => $password,
'scope' => '*',
],
]);
$reply = json_decode($response->getBody(), true);
$token = $reply['access_token'];
return redirect('/')->cookie('token', $token);
}
public function attemptLogout(Request $request)
{
$accessToken = $request->cookie('token');
$client = new Client(['base_uri' => 'https://api.domain.com']);
$headers = [
'Authorization' => 'Bearer ' . $accessToken,
'Accept' => 'application/json',
];
$response = $client->request('GET', 'logout', [
'headers' => $headers
]);
$status = $response->getStatusCode();
if($status === 200)
{
return redirect('/login')->withCookie(Cookie::forget('token'))->with('success','Logout Successful');
} else {
return response('API Logout Failed', 500);
}
}
}

Retrieving Client Credentials grant token returns 401 Unauthorized

I'm setting up Laravel Passport's Client Grant and using Guzzle to retrieve a token, but I'm getting a 401 Unauthorized error.
I created the client using: php artisan passport:client --client.
The client generated is:
Client ID: 1
Client secret: NmJsEVFClXVqWJuQnZTWA3bnZEVZ0KaC13anHZt1
I saved these in my .env file and ran the command
php artisan config:clear
Here's my code:
$url = 'http://hub.local/oauth/token';
$client_secret = env('CLIENT_SECRET');
$client_id = env('CLIENT_ID');
$client = new Client();
try {
$response = $client->post($url, [
'json' => [
'grant_type' => 'client_credentials',
'client_id' => $client_id,
'client_secret' => $client_secret,
],
'headers' => [
'Content-Type' => 'application/json',
],
]);
return $response;
} catch (RequestException $e) {
dd($e);
} catch (Exception $e) {
dd($e);
}
And the resulting error message:
ClientException {#660 ▼
-request: Request {#649 ▶}
-response: Response {#657 ▶}
-handlerContext: []
#message: """
Client error: `POST http://hub.local/oauth/token` resulted in a `401 Unauthorized` response:
{"error":"invalid_client","error_description":"Client authentication failed","message":"Client authentication failed"}
"""
#code: 401
#file: "C:\repos\client\vendor\guzzlehttp\guzzle\src\Exception\RequestException.php"
#line: 113
When I try the request using the same parameters and values in Insomnia, the request works.
Seems I can't post images yet.
https://i.imgur.com/w6Ollin.jpg
https://imgur.com/fXpzKjj.jpg
I'm using Laravel 5.8.12, Guzzle 6.3.3 and Passport 7.2.2.
What am I missing?
Update: The problem only occurred when I was running the ( Laravel) client and server instances on the same local xampp server. I separated them to run on their own individual xampp instances and the authentication worked without issue. What had confused me while testing solely locally was that the request worked with Insomnia, but not the browser. I'm assuming that the "machine to machine" part of this client grant was seeing my client + server sharing the same xampp instance as something unrecognized. In any event, there's no actual problem. Hopefully this helps someone else.
in laravel doc there is an example like this :
Route::get('/callback', function (Request $request) {
$http = new GuzzleHttp\Client;
$response = $http->post('http://your-app.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,
],
]);
return json_decode((string) $response->getBody(), true);
});
when I tested this one with my info I changed to some thing like this :
Route::get('/users/login', function (Request $request) {
$http = new GuzzleHttp\Client;
$response = $http->post('http://payment.devenv/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 2,
'client_secret' => 'z8YWlJeaW0d7o7SFyPLISUxYOcO5CxjGrZ5YuItl',
'username' => 'athamidn#gmail.com',
'password' => '123456',
'scope' => '',
'code' => 200,
'redirect_uri' => 'http://example.com/callback',
],
]);
return json_decode((string) $response->getBody(), true);
});
And I received an error same with your error. After many hours I got issue :
In database at oauth_clients table this is my info :
2 name: Laravel Password Grant Client redirect: http://localhost
when I changed redirect url to this it works.
finally :
Route::get('/users/login', function (Request $request) {
$http = new GuzzleHttp\Client;
$response = $http->post('http://payment.devenv/oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 2,
'client_secret' => 'z8YWlJeaW0d7o7SFyPLISUxYOcO5CxjGrZ5YuItl',
'username' => 'athamidn#gmail.com',
'password' => '123456',
'scope' => '',
'code' => 200,
'redirect_uri' => 'http://localhost'
],
]);
return json_decode((string) $response->getBody(), true);
});
I believe you can re-create passport public and private keys stored in /storage folder by executing the php artisan passport:install. Then you can define those keys in the .env file and pass it with client_id & client_secret.
command output
Personal access client created successfully.
Client ID: 31
Client secret: xxxx
Password grant client created successfully.
Client ID: 32
Client secret: xxxx
/.env
PERSONAL_CLIENT_ID=31
PERSONAL_CLIENT_SECRET=xxxx
PASSWORD_CLIENT_ID=32
PASSWORD_CLIENT_SECRET=xxxx
POST - API parameters - /oauth/token
'client_id' => env('PASSWORD_CLIENT_ID'),
'client_secret' => env('PASSWORD_CLIENT_SECRET')
This error could happen when those private and public keys are not matched properly with the already created ones.
Cheers!

Laravel passport refresh token

I am using a Laravel version 5.5 using Passport for authentication.
I have successfully create the token and can access it using the auth:api middleware.
But whenever user login into system it create new token for that user. I just want to refresh user last token and send it back instead of creating a new token.
I have used the following code to generate auth token
$token = $user->createToken('string-'.$user->id)->accessToken;
It generate the token with 1075 characters but when i checked in database table oauth_access_tokens it shows me the token with 80 characters.
How can i get last generated token using 80 character token and refresh it and send it back?
Thanks in Advance
If your application issues short-lived access tokens, users will need to refresh their access tokens via the refresh token that was provided to them when the access token was issued. In this example, we'll use the Guzzle HTTP library to refresh the 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);
This /oauth/token route will return a JSON response containing access_token, refresh_token, and expires_in attributes. The expires_in attribute contains the number of seconds until the access token expires.
I've done something like.
Created an endpoint for grant refresh token.
and in my controller,
public function userRefreshToken(Request $request)
{
$client = DB::table('oauth_clients')
->where('password_client', true)
->first();
$data = [
'grant_type' => 'refresh_token',
'refresh_token' => $request->refresh_token,
'client_id' => $client->id,
'client_secret' => $client->secret,
'scope' => ''
];
$request = Request::create('/oauth/token', 'POST', $data);
$content = json_decode(app()->handle($request)->getContent());
return response()->json([
'error' => false,
'data' => [
'meta' => [
'token' => $content->access_token,
'refresh_token' => $content->refresh_token,
'type' => 'Bearer'
]
]
], Response::HTTP_OK);
}

how can i get scope description in laravel passport

Using laravel passport for token base authentication. i have set up scope for
access token and now on controller i wanted to get the scope value and its description.
protected function authenticate(Request $request)
{
$request->request->add([
'username' => $request->username,
'password' => $request->password,
'grant_type' => 'password',
'client_id' => $this->client->id,
'client_secret' => $this->client->secret,
'scope' => 'admin'
]);
$proxy = Request::create(
'oauth/token',
'POST'
);
$data = Route::dispatch($proxy);
//$data = json_decode($data);
return $data;
}
late to the party (I was looking this up myself) but check out the Passport::tokensCan array. You can define scopes and scope descriptions in there.
https://laravel.com/docs/5.8/passport#defining-scopes

showing {"error":"Unauthenticated."} on laravel api call

showing {"error":"Unauthenticated."}, while calling larvel API in Laravel 5.4 and passport version: v1.0.9, using ajax call.
Calling from route api:
Route::get('category/get_tree_data', 'CategoryApiController#getTreeData')->middleware('auth:api');
If you set up Laravel Passport properly, you should have this in your view:
You need to create a client, that client has a client id and a client secret.
Now you need to open your consumer app which shall contain your client id and your client secret.
It looks like this(you have to change the token and id to your specific one):
class OAuthController extends Controller
{
public function redirect()
{
$query = http_build_query([
'client_id' => 3,
'redirect_uri' => 'http://localhost/app/public/callback',
'response_type' => 'code',
'scope' => '',
]);
return redirect('http://localhost/app/public/oauth/authorize?' . $query);
}
public function callback(Request $request)
{
$http = new Client;
$response = $http->post('http://localhost/app/public/oauth/token', [
'form_params' => [
'grant_type' => 'authorization_code',
'client_id' => 3, // from admin panel above
'client_secret' => 'BcTgzw6YiwWUaU8ShX4bMTqej9ccgoA4NU8a2U9j', // from admin panel above
'redirect_uri' => 'http://localhost/app/public/callback',
'code' => $request->code // Get code from the callback
]
]);
return json_decode((string) $response->getBody(), true);
}
}
Now you need to call that consumer app and authorize your application.
If that worked you get an access token + a refresh token.
It should look like this:
Now you can test this using a program like postman.
You basically call your get route and add the access token, which gives you access to the api, like this:
If you have any more question I recommend reading the docs.
Thus I highly recommend watching following video from Taylor Otwell.
Of course you can give me a comment aswell if you have any more questions.
Add this code inside your /resources/assets/js/bootstrap.js file
window.axios.defaults.headers.common = {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'X-Requested-With': 'XMLHttpRequest'
};
working perfectly...
I have the same issue with Laravel 5.8 when calling /api/user on auth:api middleware, I've tried to call the API url using Postman but got unauthenticated message
And I am able to fixed it by changing the hash value to false in config/auth.php file
// routes/api.php
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
// config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false, // <-- change this to false
],
],
I faced the same issue.
In my case, I was caching generated token after user login.
$token = $user->createToken('API'.'-'.strtoupper($user->username).'-'.'-X-AUTH-TOKEN', [$this->scope]);
Cache::add(strtoupper($user->username).'-'. 'X-TOKEN', $token->accessToken, $expiresAt);
After debugging, tried clearing cache
Cache::clear();
and it's working now.

Resources