Trying to wrap my head around using Laravel's Passport with mobile clients. The Password Grant type of authentication seems to be the way to go, and i have it working with my iOS app, however i can't get token refreshing to work.
When authenticating i get a token and a refresh token which i store, however when the token expires, calling the oauth/token/refresh route doesn't work. The route is using the web middleware which means my app using the api route can't access it. I'm not sure if they intended for mobile clients to never refresh or if they wanted you to roll your own refreshing? If anyone has insight on how this is supposed to work, that'd be great.
The oauth/token/refresh route is not for refreshing access tokens. It is used to refresh transient tokens, which are used when you consume your own API from your javascript.
To use your refresh_token to refresh your access token, you need to call the oauth/token route with the grant_type of refresh_token.
This is the example provided by the documentation:
$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);
One note about scopes, when you refresh the token, you can only obtain identical or narrower scopes than the original access token. If you attempt to get a scope that was not provided by the original access token, you will get an error.
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);
}
Related
To use Microsoft Graph, I'm trying to authenticate on behalf of the user. I'm following this tutorial, and I'm currently stuck at step 3.
When requesting the token, I receive a 400 Bad Request response:
{
"error": "invalid_scope",
"error_description": "AADSTS70011: The provided request must include a 'scope' input parameter."
}
Even though I'm including a scope parameter, this is my request:
$guzzle = new \GuzzleHttp\Client(['headers' => [
'Host' => 'https://login.microsoftonline.com',
'Content-Type' => 'application/x-www-form-urlencoded'
]
]);
$url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
$token = json_decode($guzzle->post($url, [
'form_params' => [
'client_id' => '################################',
'scope' => 'user.read%20mail.read',
'code' => $_GET['code'],
'grant_type' => 'authorization_code',
'redirect_uri' => 'https://eb3ef49e.ngrok.io/callback.php',
'client_secret' => '################'
],
])->getBody()->getContents());
What am I doing wrong?
I guess the complete error description says:
AADSTS70011: The provided request must include a 'scope' input
parameter. The provided value for the input parameter 'scope' is not
valid. The scope user.read%20mail.read is not valid. The scope
format is invalid. Scope must be in a valid URI form
<https://example/scope> or a valid Guid <guid/scope>.
If so, then Azure AD endpoint could not recognize the provided scope here. /token endpoint expects scope parameter to be specified as a space-separated list of scopes . Meaning there is no need to explicitly escape space symbol here:
'scope' => 'user.read%20mail.read'
instead specify it like this:
'scope' => 'user.read mail.read'
and and Guzzle client will do the rest of constructing the encoded body for /token endpoint
Im using Laravel passport for API authentication. I have two routes
/api/login
and
/oauth/token
Since I cannot hardcode my client id and the login receives from JS and the params and client id is hardcoded inside a login method(laravel), Im trying to post the values using Guzzle (6.0) to oauth/token (POST requests).
I followed a youtube video and there it works but not mine. Iam using 5.6, not sure which version was in the video. Could someone help?
Below is the Video
https://www.youtube.com/watch?v=HGh0cKEVXPI&t=838s
Below is the code
$http = new GuzzleHttp\Client();
$request = $http->post(URI, '/oauth/token', [
'form_params' => [
'username' => 'bar',
'password' => 'xxxxxx',
'client_id' => 2,
'grant_type' => 'password',
'client_secret' => '00000000000000000'
]
]);
return $request;
You are not getting the response only returning guzzle $request initalization so add getBody()
$http = new GuzzleHttp\Client();
$request = $http->post(URI, '/oauth/token', [
'form_params' => [
'username' => 'bar',
'password' => 'xxxxxx',
'client_id' => 2,
'grant_type' => 'password',
'client_secret' => '00000000000000000'
]
]);
return $request->getBody();
I think you are trying to request in build-in server.
So You try two servers to send the request.
It will be working.
Like localhost:8000 and localhost:9000 server
Use this command
php artisan serve
php artisan serve --port=9000
Thanks.
You should check the status first to make sure that everything is okay by using
$request->getStatusCode();
You can get your response by
$request->getBody();
you can see also full documentation of using GuzzulHttp from Here
http://docs.guzzlephp.org/en/stable/
If I make any request to http://localhost:8000 or http://127.0.0.1:8000 it hangs on status pending. (Exactly as here https://github.com/guzzle/guzzle/issues/1857)
I was told that it isn't related to guzzle and that I should better ask about it here.
I stumbled upon this problem while following laravel.com/docs/5.4/passport
This is the code that hangs:
$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,
],
]);
I tried making GET and POST request to working API routes (tested with postman) and it still hangs when calling the same routes using guzzle.
So is there a way to make requests to my own API while using php artisan serve?
Carl has a great solution to this. If you are looking for a quick fix to test your updates - you can get this done by opening up two command prompts. The first would be running php artisan serve (locally my default port is 8000 and you would be running your site on http://localhost:8000). The second would run php artisan serve --port 8001.
Then you would update your post request to:
$response = $http->post('http://localhost:8001/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,
],
]);
This should help during your testing until you are able to everything on server or a local virtual host.
try this.
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Route;
use App\User;
class UserController extends Controller
{
//use AuthenticatesUsers;
protected function login(Request $request)
{
$request->request->add([
'grant_type' => 'password',
'client_id' => '3',
'client_secret' => '6BHCRpB4tpXnQvC1DmpT7CXCSz7ukdw7IeZofiKn',
'scope' => '*'
]);
// forward the request to the oauth token request endpoint
$tokenRequest = Request::create('/oauth/token','post');
return Route::dispatch($tokenRequest);
}
}
I ended up solving it by using wamp virtualhost instead of php artisan serve. No idea why it doesn't work with localhost though.
UPDATE: Someone was kind enough to explain why it wouldn't work.
In https://github.com/guzzle/guzzle/issues/1857#issuecomment-506962175
The reason for this is php artisan serve is a single thread application. So when we use guzzle to request from it to itself, it basically just tries to finish guzzle request (as a client) first then come to finish that request (as a server), which is impossible.
More info about this: https://php.net/manual/en/features.commandline.webserver.php
Also this answer:
When making calls to itself the thread blocked waiting for its own reply. The solution is to either seperate the providing application and consuming application into their own instance or to run it on a multi-threaded webserver such as Apache or nginx.
/**
* Login function
*/
public function login(Request $request) {
/*
Sample post object
{
"username": "test#gmail.com",
"password": "test123"
}
*/
if (Auth::attempt(['email' => request('username'), 'password' => request('password')])) {
return $this->getToken($request);
}
else {
return response()->json(['error'=>'Unauthorised'], 401);
}
}
public function getToken(Request $request) {
//Get client ID and client Secret
$client = DB::table('oauth_clients')->where('password_client',1)->first();
$request->request->add([
"grant_type" => "password",
"username" => $request->username,
"password" => $request->password,
"client_id" => $client->id,
"client_secret" => $client->secret,
]);
// Post to "/oauth/token
$tokenRequest = $request->create('/oauth/token','post');
$instance = Route::dispatch($tokenRequest);
//return token_type, expires_in, access_token, refresh_token
return response()->json(json_decode($instance->getContent()));
}
I have a phpleague/oauth2 server implementation, which is working fine, ie generating access/refresh tokens, validating etc.
I have a following problem. When I refresh the token with grant_type=refresh_token with console curl, I successfully get the new access_token, but when doing this with a test:
$I->sendPOST('access_token', [
'grant_type' => 'password',
'client_id' => '111',
'client_secret' => '222',
'username' => 'exampleuser',
'password' => 'examplepass',
]);
$I->seeResponseCodeIs(200);
$I->seeResponseContainsJson(['token_type' => 'Bearer']);
// I receive a proper string, checked that out
$token = $I->grabDataFromResponseByJsonPath('$.refresh_token')[0];
$I->sendPOST('access_token', [
'grant_type' => 'refresh_token',
'client_id' => 1,
'client_secret' => 'pass2',
'refresh_token' => $token
]);
$I->seeResponseCodeIs(200); // Here I receive 403
...
I repeat, doing this manually in terminal works fine.
After debugging it myself I found out that refresh token validation fails
at oauth2-server/src/Grant/RefreshTokenGrant.php at:
$refreshToken = $this->decrypt($encryptedRefreshToken);
But still I can't understand why it works manually. I did urlencode/urldecode and tons of var dumps, but still can't get the solution.
I'm integrating with a affiliate platform for a client which provides an oAuth2 API, don't usually do massive amounts of work with oAuth2.
I've decided for my client, I'll use the PHP Leagues oAuth2 package: https://github.com/thephpleague/oauth2-client
Anyway, I've got an accessToken no problem! using the following:
$provider = new GenericProvider([
'clientId' => $this->config->affiliates->rakuten->clientId,
'clientSecret' => $this->config->affiliates->rakuten->clientSecret,
'redirectUri' => 'http://www.newintoday.com/',
'urlAuthorize' => 'https://api.rakutenmarketing.com/token', // Ignore
'urlAccessToken' => 'https://api.rakutenmarketing.com/token',
'urlResourceOwnerDetails' => 'https://api.rakutenmarketing.com/' // Ignore
]);
try {
// Try to get an access token using the resource owner password credentials grant.
$accessToken = $provider->getAccessToken('password', [
'username' => $this->config->affiliates->rakuten->username,
'password' => $this->config->affiliates->rakuten->password,
'scope' => $this->config->affiliates->rakuten->publisherId,
]);
$productSearchApiBaseUri = 'https://api.rakutenmarketing.com/productsearch/1.0';
$request = $provider->getAuthenticatedRequest('GET', $productSearchApiBaseUri, $accessToken, [
'body' => '?keyword=shirt',
]);
\Utils::dump($provider->getResponse($request));
} catch (IdentityProviderException $e) {
echo $e->getMessage();
}
My question is once we have the accessToken what do we use in it to make the request, I followed through the code and came up with the above but the API responds saying that the keyword is not specified? Is
$request = $provider->getAuthenticatedRequest('GET', $productSearchApiBaseUri, $accessToken, [
'body' => 'keyword=shirt',
]);
The correct way to provide it with a GET variable?
Thanks in advance.
Realised I could simply include the get vars in the URI alla:
$productSearchApiBaseUri = 'https://api.rakutenmarketing.com/productsearch/1.0?keyword=shirt';