I previously worked on an OAuth2 application where the logic was to generate a new access token via refresh token once the old one expired.
Now working with Google APIs, I'm not experiencing the same thing. I have received both an access token and refresh token, and after allowing the access token to expire, I attempt to use the refresh token
var myToken = new TokenResponse
{
RefreshToken = sRefreshToken
};
var credentials = new UserCredential(new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = clientId,
ClientSecret = clientSecret
}
}), "user", myToken);
service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials,
ApplicationName = "XYZ",
});
It seems after doing so I can make API calls. But I have tried to retrieve the access/refresh tokens after doing this with:
ACCESS_TOKEN = credentials.Token.AccessToken;
REFRESH_TOKEN = credentials.Token.RefreshToken;
And both the access and refresh tokens are the same as the old ones. I had thought refreshing would generate a new token altogether? Is this not the case?
If the access token expires after 30 minutes and you then just need to pass in the refresh token (but nothing re-generates), what is the point of the refresh token?
I previously worked on an OAuth2 application where the logic was to generate a new access token via refresh token once the old one expired.
This idea also applies to Google access/refresh tokens. But if you're using the .NET client library (as your code snippet suggests), you don't have to perform the refreshes yourself. You can just continue using the UserCredential object and it'll automatically fetch a new access token every ~1h.
Related
I am requesting access to the user's Google Classroom data with the following PHP code block:
public function getGoogleClassroomToken(){
if($this->user==null){
return ["result"=>"error","message"=>"You need to log in to do that!"];
}
if ($this->user['gc_token']) {
return ["result"=>"error","message"=>"User already has access token!"];
}
$client = new Google_Client();
$client->setApplicationName('example.com');
$client->setAuthConfig('/var/www/client_secret.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
$client->addScope(Google_Service_Classroom::CLASSROOM_COURSEWORK_STUDENTS);
$client->addScope(Google_Service_Classroom::CLASSROOM_COURSES_READONLY);
$client->addScope(Google_Service_Classroom::CLASSROOM_ROSTERS_READONLY);
$redirect_uri = 'https://example.com/gc.php';
$client->setRedirectUri($redirect_uri);
$authUrl = $client->createAuthUrl();
return ["result"=>"success","authUrl"=>$authUrl];
}
..which seems to work fine. I am then instantiating the Google Client with the following code block:
public function getGoogleClassroomClient(){
if($this->user==null){return false;}
if (!$this->user['gc_token']) {return false;}
$client = new Google_Client();
$client->setAuthConfig('/var/www/client_secret.json');
$client->setAccessToken((array)json_decode($this->user['gc_token']));
if ($client->isAccessTokenExpired()) {
if ($client->getRefreshToken()) {
$token = $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
var_dump($token);
}
}
return $client;
}
.. which works fine UNLESS THE TOKEN HAS EXPIRED, in which case the $token variable outputs:
array(2) { ["error"]=> string(13) "invalid_grant" ["error_description"]=> string(11) "Bad Request" }
Does anyone know what could be going wrong here?
Answer
As per the Google APIs Client Library for PHP your refresh token can't be null without throwing this exception refresh token must be passed in or set as part of setAccessToken which makes sense because you are calling $client->getRefreshToken() and that returns 2 options: the refresh token or null.
If you take a look into the Google Identity Platform: Using OAuth 2.0 to Access Google APIs > Refresh token expiration it seems like your refresh token has expired, there are some possible scenarios:
The user has revoked your app's access.
The refresh token has not been used for six months.
The user changed passwords and the refresh token contains Gmail scopes.
The user account has exceeded a maximum number of granted (live) refresh tokens.
The client has reached a limit of 50 refresh tokens per account if it's not a service account.
Reference
Google API Client for PHP
Google Identity Platform: Using OAuth 2.0 to Access Google APIs
I am trying to access google sheets using Java client Library. First time access token and refresh token are generated and stored in File.(StoredCredential). Once the access token expires new token is not generated with Refresh token. Please suggest what can be done.
Sample Code:
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
.setAccessType("offline")
.build();
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
I have an Azure web app where I want users to sign into Microsoft (Azure AD). I need to get hold of the refresh token. I have already looked at examples which use libraries that refresh the token silently in the background, but for my design I need to get hold of the refresh token itself.
Is this possible to achieve with a high level authentication library (OpenID Connect?), and if so, how?
For the manual approach, it is described here: https://learn.microsoft.com/en-us/graph/auth-v2-user
Is there any example code or tutorial for this in ASP.NET or .NET Core?
Firstly i would suggest using MSAL which Maintains a token cache and refreshes tokens for you when they are close to expire. You don't need to handle token expiration on your own.
If you want to hold the refresh token , you can set the SaveTokens property to true when registering the OIDC middleware so that tokens will be saved into cookie . I assume you are using the Microsoft.AspNetCore.Authentication.AzureAD.UI library :
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.SaveTokens = true;
});
Then you can access the tokens in controller like :
var refreshToken = await HttpContext.GetTokenAsync("refresh_token");
var idToken = await HttpContext.GetTokenAsync("id_token");
Update:
That is because you don't get refresh token correctly . For testing , you can use code flow and add offline_access scope of OIDC :
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.SaveTokens = true;
options.ResponseType = "code";
options.ClientSecret = "xxxxxx";
options.Scope.Add("offline_access");
options.TokenValidationParameters.ValidateIssuer = false;
});
Replace ClientSecret with the one you config in Azure portal .
I have a Angular 4 site that I’m trying to use Microsoft Graph implicit flow to authenticate users then use token to call our APIs at another endpoint, so I use msal.js to get the access token.
After I bring the access token to my API endpoint and try to valid it, the token cannot be valid. I got a SignatureVerificationFailedException.
My understanding is that the access token is for Microsoft Graph API, not for my APIs, so I cannot valid it. (I can use it to call Graph API without problem)
How can I get a access token(not id token) using msal.js that can be used for my APIs but not Microsoft Graph? Thanks!
The reason I'm sending access token instead of id token to the API endpoint is that I want to get the puid claim from the token, which is not available for id token.
Here is what I was trying to valid the access token I got from client which is using msal.js
const string authority = "https://login.microsoftonline.com/common";
const string audience = "https://graph.microsoft.com";
string issuer = null;
string stsDiscoveryEndpoint = $"{authority}/v2.0/.well-known/openid-configuration";
List<SecurityToken> signingTokens = null;
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(stsDiscoveryEndpoint);
var config = await configManager.GetConfigurationAsync();
issuer = config.Issuer;
signingTokens = config.SigningTokens.ToList();
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
ValidAudience = audience,
ValidIssuer = issuer,
ValidateIssuer = false,
IssuerSigningTokens = signingTokens,
CertificateValidator = X509CertificateValidator.None
};
try
{
// Validate token.
SecurityToken validatedToken = new JwtSecurityToken();
var claimsPrincipal = tokenHandler.ValidateToken(jwtToken, validationParameters, out validatedToken);
var claimsIdentity = claimsPrincipal.Identity as ClaimsIdentity;
return ExtractAuthenticatedUserFromClaimsIdentity(claimsIdentity);
}
catch (SignatureVerificationFailedException)
{
throw;
}
Thanks,
If you want to get an access token for your API rather than the Microsoft Graph API, you must specify your API as the resource in the token request.
Make sure that:
Your Web API has configured OAuth2Permission Scopes. See here. Configuring a resource application to expose web APIs
Your Client Application has selected permissions to those exposed APIs. Configuring a client application to access web APIs
Finally, make sure you use your Web API's App ID URI or App ID GUID as the resource value in your token request.
Let me know if this helps!
I need to recover user info after javascript google login.
Authentication work fine and i send googleUser.Ka object to my php where there's this code
$api = new Google_Client();
$api->setApplicationName($this->_configCustomer['google']['api']['application_name']);
$api->setClientId($this->_configCustomer['google']['api']['client_id']); // Set Client ID
$api->setClientSecret($this->_configCustomer['google']['api']['client_secret']); //Set client Secret
$api->setAccessType('online'); // Access method
$api->setScopes(array('https://www.googleapis.com/auth/plus.login', 'https://www.googleapis.com/auth/plus.me', 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile'));
//$api->setRedirectUri('http://www.infotuts.com/demo/googlelogin/login.php');
$service = new Google_Service_Plus($api);
$oauth2 = new \Google_Service_Oauth2($api);
$api->setAccessToken(json_encode($_POST));
//ADDED AFTER ERROR
if($api->isAccessTokenExpired()) {
$api->authenticate();
$NewAccessToken = json_decode($api->getAccessToken());
$api->refreshToken($NewAccessToken->refresh_token);
}
//END ADDED AFTER FIRST ERROR
//$api->authenticate($_POST['access_token']);
if ($api->getAccessToken()) {
$data = $service->people->get('me');
$user_data = $oauth2->userinfo->get();
var_dump($user_data);
die();
}
this code generate the error:
The OAuth 2.0 access token has expired, and a refresh token is not available. Refresh tokens are not returned for responses that were auto-approved.
i added code indicate from comment follow this link
OAuth 2.0 access token has expired, and a refresh token is not available
but in the last version of php authenticate method require $code parameter. What is it?
Thanks
i solved in this way
$api = new Google_Client();
$api->setApplicationName($this->_configCustomer['google']['api']['application_name']);
$api->setClientId($this->_configCustomer['google']['api']['client_id']); // Set Client ID
$api->setClientSecret($this->_configCustomer['google']['api']['client_secret']); //Set client Secret
$api->setAccessType('online'); // Access method
$api->setScopes(array('https://www.googleapis.com/auth/plus.login', 'https://www.googleapis.com/auth/plus.me', 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile'));
$api->setRedirectUri('postmessage');
$result = $api->verifyIdToken($_POST['id_token']);
i send id_token, obtained from the ajax login, and recovery user info using verifyIdToken method of client library