Uh..I'm not sure what's wrong with my code.
Q1.
I made reference to the oauth2-server's demo.
And using Memory storage can work But when I try Pdo storage.
I get the "500 Internal Server Error" and "Unexpected character (<) at position 1."
Server
$storage = new OAuth2\Storage\Pdo(array(
'dsn' => $dsn,
'username' => $username,
'password' => $password));
$this->server = new OAuth2\Server($storage, array(
'use_jwt_access_tokens' => true,
));
$this->server->addGrantType(new OAuth2\GrantType\ClientCredentials($storage));
Token
public function token_post(){
$this->server->handleTokenRequest(OAuth2\Request::createFromGlobals())->send();
}
Q2.
In JWT Bearer page
I can get access_token with demo,but this access_token don't work.
alway get "Failed" With resource page
resource
public function resource_get()
{
if (!$this->server->verifyResourceRequest(OAuth2\Request::createFromGlobals())) {
exit("Failed");
}
echo json_encode(array('success' => true, 'message' => 'You accessed my APIs!'));
}
Q3.
I have no idea about what is the different between JWT Access Tokens and JWT Bearer to use?
Remark
Sorry for bad English.
I use Google'translation to help me completion of these.
So i can't use realy correct description.
Sorry again for bad English.
Related
i use passport in laravel and after user verification i want to generate a token for them with refresh token . for get refresh token i have to send curl request with password grant_type . after generate access token i want to get Password Grant Clientfrom database and pass it to my curl body . i fount this code snippet:
$tokenId = (new Parser(new JoseEncoder()))->parse($token)->claims()->get('jti');
$client = \Laravel\Passport\Token::find($tokenId)->client;
and the problem result of client variable is Personal Access Client not Password Grant Client and get this error because of its personal type:
{
"error": "invalid_client",
"error_description": "Client authentication failed",
"message": "Client authentication failed"
}
this is my code:
$token = $user->createToken(Config::get('auth.guards.api.token_name'))->accessToken;
$tokenId = (new Parser(new JoseEncoder()))->parse($token)->claims()->get('jti');
$client = \Laravel\Passport\Token::find($tokenId)->client;
$http = new \GuzzleHttp\Client();
try {
$response = $http->request('POST', url("/oauth/token"), [
'form_params' => [
'grant_type' => 'password',
'client_id' => $oClient->id,
'client_secret' => $oClient->secret,
'username' => $username,
'password' => $password,
'scope' => '*',
],
]);
} catch (\Exception $exception) {
}
how can i deal with this?
For obtaining the client_id and client_secret for Password Grant Client you need to run the following command on your authorization server (OAuth server) as stated here https://laravel.com/docs/9.x/passport#creating-a-password-grant-client
php artisan passport:client --password
The above command is not necessary to run if you already ran passport:install. The easiest way is to check your oauth_clients table for the column password_client there should be a row that has this value set to 1 (enabled).
It seems from your question that you are trying to obtain the client_id and client_secret programmatically from your client. This is not the correct way of doing it.
Basically after you run the above command to generate your client_id and client_secret you need to hard code them in your .env and use them in you CURL such as:
$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'password',
'client_id' => env('OAUTH_CLIENT_ID'),
'client_secret' => env('OAUTH_CLIENT_SECRET'),
'username' => $username,
'password' => $password,
'scope' => '*',
]);
return $response->json();
You can obtain your client_id and client_secret from the oauth_clients table. Just make sure to copy the values where the password_client column is set to 1.
There should not be any security concern if your client is storing these credentials in the backend and doing the CURL from the backend.
In the case you are trying to do this from a mobile app and you might not have a way to securely store the client_id and client_secret. In this case you should not be using the Password Grant Client flow but instead the Authorization Code Grant with PKCE: https://laravel.com/docs/9.x/passport#code-grant-pkce
I've been reading and trying to implement what's instructed from the docs at Protect user accounts with Cross-Account Protection
What I've done so far as per below:
JWT::$leeway = 60;
$key = file_get_contents('location.json');
$time = time();
$payload = [
"iss" => "account email",
"sub" => "account email",
"aud" => "https://risc.googleapis.com/google.identity.risc.v1beta.RiscManagementService",
"iat" => $time,
"exp" => $time + 3600,
];
/**
* IMPORTANT:
* You must specify supported algorithms for your application. See
* https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
* for a list of spec-compliant algorithms.
*/
$jwt = JWT::encode($payload, $key);
$decoded = JWT::decode($jwt, $key, ['HS256']);
print_r($jwt);
print_r($decoded);
$client = new Client();
try {
$request = $client->post('https://risc.googleapis.com/v1beta/stream:update', [
'headers' => [
'Authorization' => 'Bearer ' . $jwt,
'Accept' => 'application/json',
],
'form_params' => [
'delivery' => [
'delivery_method' => 'https://schemas.openid.net/secevent/risc/delivery-method/push',
'url' => 'https://test.myapp.com/webhooks/google',
],
'events_requested' => [
'https://schemas.openid.net/secevent/oauth/event-type/tokens-revoked',
],
],
]);
$response = $request->getBody();
dd($response);
} catch (ClientException $exception) {
dd($exception->getResponse()->getBody()->getContents());
}
Issues I am facing:
I do not understand well how to use JWT from what I read in the docs, what am I doing wrong in my implementation?
The examples are in JAVA, but I need it in php and I tried reading the JAVA code but do not understand where several things are coming from.
From the much I've read, I assume I won't be able to test these events on local environment? That is point these events to be triggered on local? Or would services such as ultrahook allow doing so? Otherwise I would have to test the endpoint straight on server.
The error I get from the code above is Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
Trying the encoding suggested below RS256 gives me the error UnexpectedValueException: Algorithm not allowed I believe I do lack the necessary knowledge around JWT and doing something wrong there.
I'm also looking at how to do this cross-account protection from this link https://developers.google.com/identity/protocols/risc#java_1 I assume you are talking about this part (Generate an authorization token) https://developers.google.com/identity/protocols/risc#auth_token
I'm using php though but looking at the java code in the page it's using RS256 instead of HS256 in your code. If you use php then you can try firebase php and they have a simple JWT class you can use. https://github.com/firebase/php-jwt You can just use the example and replace the payload with yours then change to RS256. That's what I'm gonna try I can let you know if it works after.
I'm currently trying to implement a way to synchronize my PHP App calendar with the Outlook calendar of my clients, using Azure API.
I use OAuth2 and the custom Microsoft provider by Steven Maguire.
I currently run in an issue where I get an error in my response :
{"error":"unsupported_grant_type","error_description":"The provided value for the input parameter 'grant_type' is not valid. Expected values are the following: 'authorization_code', 'refresh_token'."}
I'm having trouble understanding why the grant_type password is not supported, even though it says on the documentation of Azure that it is.
The request looks like this :
client_id=44bef79b-**********************&client_secret=H****************&redirect_uri=https%3A%2F%2F192.168.1.123%2Fmapeyral%2Fcalendarsync.php&grant_type=password&username=******************&password=***********&scope=openid%20profile%20offline_access%20Calendars.ReadWrite
The Authorize url used is : https://login.live.com/oauth20_token.srf
as defined in the Steven Maguire provider.
The header contains the content-type application/x-www-form-urlencoded (I've seen a lot of post where this was what caused the error).
Some of my code :
$this->provider = new Microsoft([
'clientId' => MicrosoftGraphConstants::CLIENT_ID,
'clientSecret' => MicrosoftGraphConstants::CLIENT_SECRET,
'redirectUri' => MicrosoftGraphConstants::REDIRECT_URI,
'urlAuthorize' => MicrosoftGraphConstants::AUTHORITY_URL . MicrosoftGraphConstants::AUTHORIZE_ENDPOINT,
'urlAccessToken' => MicrosoftGraphConstants::AUTHORITY_URL . MicrosoftGraphConstants::TOKEN_ENDPOINT,
'urlResourceOwnerDetails' => MicrosoftGraphConstants::RESOURCE_ID,
'scope' => MicrosoftGraphConstants::SCOPES
]);
if ($_SERVER['REQUEST_METHOD'] === 'GET' && !isset($_GET['code']))
{
// Try getting access token from Database
$workingAccount = $GLOBALS['AppUI']->getState('working_account');
if (isset($workingAccount))
{
// DB access
$DB = new DatabaseConnection();
$dbAccess = $DB->getConnection();
$contactData = DBUserUtils::getContactDataFromEmail($GLOBALS['AppUI']->getState('working_account'), $dbAccess);
// If at least one user contact found
if (!is_null($contactData))
{
// If has refresh token => fill session variables using refresh token
if (!is_null($contactData['contact_refreshToken']))
{
log_msg('debug.log', 'Has refresh token');
$GLOBALS['AppUI']->setState('preferred_username', $contactData['contact_email']);
$GLOBALS['AppUI']->setState('given_name', $contactData['contact_first_name']." ".$contactData['contact_last_name']);
// Get new tokens
$newAccessToken = $this->provider->getAccessToken('refresh_token', [
'refresh_token' => $contactData['contact_refreshToken']
]);
// Update tokens and DB
$GLOBALS['AppUI']->setState('refresh_token', $newAccessToken->getRefreshToken());
$GLOBALS['AppUI']->setState('access_token', $newAccessToken->getToken());
DBOAuthUtils::updateTokenForUser($contactData['contact_id'], $GLOBALS['AppUI']->getState('refresh_token'), $dbAccess);
$this->redirectTo($redirectURL);
}
else
{
$this->getAccessToken();
}
}
else
{
$this->getAccessToken();
}
}
else
{
$this->getAccessToken();
}
function getAccessToken(){
$accessToken = $this->provider->getAccessToken('password', [
'username' => '*************',
'password' => '********',
'scope' => MicrosoftGraphConstants::SCOPES
]);
}
During the first try it doesn't pass the if (isset($workingAccount)) condition (as expected) and go straight to the last else.
Code is a bit ugly for now but I don't think it has an impact on my problem.
Any help would be appreciated !
Thanks
Edit : added code
That helped me, the problem was that I need to use Azure Active Directory and not Azure AD 2.0.
Problem solved !
I try to create a oauth connection to get data from user's channel views. I begin my request like this:
public function getAuthorizationUrl()
{
// Make redirect
$this->params = [
'client_id' => '###########&',
'redirect_uri' => '########&',
'scope' => 'https://www.googleapis.com/auth/yt-analytics.readonly&',
'response_type'=> 'code&',
'access_type' => 'offline'
];
$redirect_url = 'https://accounts.google.com/o/oauth2/auth?' . http_build_query($this->params);
return $redirect_url;
}
But sadly I fail right from the start, because I get this error:
Error: invalid_request
Invalid response_type: code&
Request Details
access_type=offline
scope=https://www.googleapis.com/auth/yt-analytics.readonly&
response_type=code&
redirect_uri=#####&
client_id=######&
Any ideas why I get this error? Any help is welcomed, thank you all for your time!
Error says you have an invalid parameter in there (response type). Have you tried removing those trailing ampersands? http_build_query should take care of that for you
I am following the docs from link below:
https://developers.google.com/+/mobile/android/sign-in#enable_server-side_api_access_for_your_app
Specifically the part that says:
If you do not require offline access, you can retrieve the access token and send it to your server over a secure connection. You can obtain the access token directly using GoogleAuthUtil.getToken() by specifying the scopes without your server's OAuth 2.0 client ID. For example:
I retrieve the access token like this:
accessToken = GoogleAuthUtil.getToken(
AuthenticatorActivity.this,
Plus.AccountApi.getAccountName(Common.mGoogleApiClient),
"oauth2:https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/plus.login email"
);
After I retrieve the access token I send it to a web server, on the web server i can see that it's a valid access token by calling
https://www.googleapis.com/oauth2/v1/tokeninfo?access_token='.$_POST['google_access_token']
The request above returns the android apps client id, it also returns the users email correctly.
The problem is that when I try to run $client->authenticate($_POST['google_access_token']); I get an exception with the message: "invalid_grant: Incorrect token type".
To prevent getToken caching I always invalidate the token in android app:
if (accessToken != null && !accessToken.isEmpty()) {
GoogleAuthUtil.invalidateToken(AuthenticatorActivity.this, accessToken);
}
Here's the php code:
if (!isset($_POST['google_access_token'])) {
throw new Exception('missing google_access_token');
}
$client = new \Google_Client();
$client->setApplicationName("GiverHub");
$client->setClientId($this->config->item('google_client_id'));
$client->setClientSecret($this->config->item('google_client_secret'));
$client->setDeveloperKey($this->config->item('google_developer_key'));
$client->setRedirectUri($this->config->item('google_redirect_uri'));
$client->setScopes([
'https://www.googleapis.com/auth/plus.login',
'https://www.googleapis.com/auth/plus.me',
'email',
]);
try {
$client->authenticate($_POST['google_access_token']); // if i remove this the rest of the code below works! ...
$reqUrl = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token='.$_POST['google_access_token'];
$req = new \Google_Http_Request($reqUrl);
$io = $client->getIo();
$response = $io->executeRequest($req);
$response = $response[0];
$response = json_decode($response, true);
if ($response === null) {
throw new Exception('Failed to check token. response null');
}
if ($response['issued_to'] !== '466530377541-s7cfm34jpf818gbr0547pndpq9songkg.apps.googleusercontent.com') {
throw new Exception('Invalid access token. issued to wrong client id: '. print_r($response, true));
}
if (!isset($response['user_id'])) {
throw new Exception('Missing user_id');
}
if (!isset($response['email'])) {
throw new Exception('Missing email');
}
/** #var \Entity\User $user */
$user = Common::create_member_google([
'id' => $response['user_id'],
'email' => $response['email'],
'given_name' => '',
'family_name' => '',
]);
$user->login($this->session);
if ($user instanceof \Entity\User) {
echo json_encode( [ 'success' => true, 'user' => $user ] );
} else {
echo json_encode( [ 'success' => false, 'msg' => $user ] );
}
} catch(Exception $e) {
echo json_encode(['success' => false, 'msg' => $e->getMessage()]);
}
The above code works if i remove the $client->authenticate(); line ... The problem is that I can't get the given_name / family_name etc .. only email / google_user_id from the tokeninfo ...
Any thoughts about why the key works for tokeninfo but not for authenticate?
I have tried many different variations of the scopes .. both on the server side and the android side ..
The $client->authenticate() method doesn't quite do what you're trying to do. It takes a one-time code from an earlier OAuth transaction and exchanges it for the access token. In your case - you're saying you already have the access token.
You should be able to call $client->setAccessToken() to set the token instead, so it may look something like
$client->setAccessToken($_POST['google_access_token']);
This is the solution I came up with after user158443 suggested I use $client->setAccessToken();
// first json_encode the access token before sending it to $client->setAccessToken();
$json_encoded_access_token = json_encode([
'access_token' => $_POST['google_access_token'],
'created' => time(), // make up values for these.. otherwise the client thinks the token has expired..
'expires_in' => time()+60 // made up a value in the future...
]);
// and then set it
$client->setAccessToken($json_encoded_access_token);
// and then get userinfo or whatever you want from google api !! :)
$oauth2 = new \Google_Service_Oauth2($client);
$user_info = $oauth2->userinfo->get();
NOTE: it's probably not smart to "emulate" the expires_in and created that i just did if you are in production ... You should probably call tokeninfo first and get the expires time from there...
NOTE: I still have no idea how to get a refresh token for this... but I don't need one for my use case..