In my tests, I "mock" my OAuth process as follows:
$oauthMiddleware = new OAuth2Middleware(new NullGrantType);
$oauthMiddleware->setAccessToken([
"access_token" => "290473f650...",
"expires_in" => 3600,
"token_type" => "Bearer",
"scope" => "*"
]);
$handlerStack = HandlerStack::create();
$handlerStack->push($oauthMiddleware);
$client = new GuzzleHttpClient([
'handler' => $handlerStack,
RequestOptions::AUTH => 'oauth',
]);
I follow the instructions given in the repository:
use kamermans\OAuth2\GrantType\NullGrantType;
$oauth = new OAuth2Middleware(new NullGrantType);
$oauth->setAccessToken([
// Your access token goes here
'access_token' => 'abcdefghijklmnop',
// You can specify 'expires_in` as well, but it doesn't make much sense in this scenario
// You can also specify 'scope' => 'list of scopes'
]);
But instead of using the manually set access token, it throws:
No access token is present, and there is no way to obtain one with NullGrantType.
I traced the exception back to kamermans\OAuth2\GrantType\NullGrantType::getRawData(). In the documentation, it says, for the exception not to be thrown, setAccessToken() must be called. But as you can see in my code, I do exactly that (correct me if I'm wrong). Does anyone see what I'm doing wrong?
Related
I have 2 methods in my feature test LoginTest.php that check for incorrect password or username.
When I run vendor/bin/phpunit I don't get any errors reported: OK (40 tests, 154 assertions)
However, I do get the following error displayed. This is expected since login is supposed to fail.
testing.ERROR: The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. {"exception":"[object] (Laravel\Passport\Exceptions\OAuthServerException(code: 10): The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client. at /app/vendor/laravel/passport/src/Http/Controllers/HandlesOAuthErrors.php:26)
Here are the 2 methods that are causing the issue.
{
$this->createAccount("test#test.com");
$loginForm = array(
"grant_type" => "password",
"client_id" => "2",
"client_secret" => "lGk9EYlEHdQfXKy0EdTJ5S4Y126y0lkz0ofXiUXe",
"scope" => "*",
"username" => "test#test12.com",
"password" => $this->formData["password"]
);
$response = $this->json('POST', '/oauth/token', $loginForm);
$response->assertStatus(400);
}
public function testLoginPasswordIncorrect()
{
$this->createAccount("test#test.com");
$loginForm = array(
"grant_type" => "password",
"client_id" => "2",
"client_secret" => "lGk9EYlEHdQfXKy0EdTJ5S4Y126y0lkz0ofXiUXe",
"scope" => "*",
"username" => $this->formData["email"],
"password" => "Wrong"
);
$response = $this->json('POST', '/oauth/token', $loginForm);
$response->assertStatus(400);
}
Should I not be bothered about these displayed errors? Or is there a way to not display them?
To avoid those messages, you can use the "expectException" and "withoutExceptionHandling" methods before making the HTTP request. However, if you choose to use them, you get an exception instead of an HTTP response (Because of this, you cannot use the "assertStatus" method).
// ...
$this->withoutExceptionHandling();
$this->expectException(\Laravel\Passport\Exceptions\OAuthServerException::class);
$response = $this->json('POST', '/oauth/token', $loginForm);
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
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);
}
At first please do not mark this as duplicated as this is related to server-side (PHP) and not to client-side as other posts.
I'm trying to refresh the token for Google API through Oauth.io. I followed this and many other posts but no luck. Unfortunately Oauth.io documentation is not the best one. I don't have this problem with Twitter and Facebook only Google.
I'm getting refresh_token when I connect for the first time. Then I need to do API call once a day.
{
"access_token":"xxx",
"token_type":"Bearer",
"expires_in":3600,
"refresh_token":"xxx",
"id_token":"xxx",
"provider":"google_analytics"
}
The question is how to refresh Google token through Oauth.io?
The documentation says:
// The auth method automatically refreshes the tokens if needed
$request_object = $oauth->auth('facebook', array(
'credentials' => $credentials
));
and points here, but it doesn't solve the problem. All it does is that I'm getting refresh_token value in response.
UPDATE
According to this post I tried to do:
$request_object->post('https://oauth.io/auth/access_token', array(
'code' => 'xxx', // here I tried access_token, refresh_token
'key' => 'xxx',
'secret' => 'xxx',
));
but all I'm getting is
array(4) {
'status' =>
string(5) "error"
'code' =>
int(401)
'message' =>
string(70) "Bearer token invalid. Follow the oauth2-token link to get a valid one!"
'data' =>
array(1) {
'code' =>
string(17) "UnauthorizedError"
}
}
Still nothing.
Finally I found it. You can refresh the access_token using:
$this->oauth = new OAuth();
$this->oauth->initialize($publicKey, $secretKey);
$refreshedCredentials = $this->oauth->refreshCredentials($oldCredentials, true);
or
$request_object = $oauth->auth('google_analytics', array(
'credentials' => $credentials,
'force_refresh' => true
));
Both answers can be found here. I had it in front of all day long and did not see it.
I prefer using 1st solution as I want to save new access_token so I can reuse it later. And you need to remember that you need to pass refresh_token, which you got with first access_token, along with last access_token to get refreshed access_token - refresh_token doesn't change until user revoke access.
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';