OAuth Implicit flow Access Token expires every hour - google-oauth

I'm having a problem with the OAuth Implicit flow for the Google Assistant.
I managed to set up a OAuth server and got it to work. Here's the flow:
The user Is redirected to my endpoint, authenticates with a Google account, and gets send back to the Assistant with an Acces Token and result code=SUCCES.
In my fullfilment I get the users email address by doing a https request to: https://www.googleapis.com/plus/v1/people/me?access_token=access_token.
I then find the matching user in my database and add the acces token to the database for this user.
The next time the user logs in I check the acces token and greet the user by their name.
Now the problem is that this is the Implict flow which according to the documentation should have an access token that never expires:
Note: Google requires that access tokens issued using the implicit
flow never expire, so you don't need to record the grant time of an
access token, as you would with other OAuth 2.0 flows.
But the Assistant forces me to re-authenticate every hour, meaning the access token did expire.
My question is: Is this flow correct or am I missing something? Is there something I've done wrong in my OAuth endpoint?
I based my endpoint on https://developers.google.com/identity/protocols/OAuth2UserAgent.
<html>
<head>
<script src="https://apis.google.com/js/platform.js" async defer></script>
<meta name="google-signin-client_id" content="CLIENT_ID">
</head>
<body>
<script>
var YOUR_CLIENT_ID = 'CLIENT_ID';
function oauth2SignIn() {
// Google's OAuth 2.0 endpoint for requesting an access token
var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
// Create element to open OAuth 2.0 endpoint in new window.
var form = document.createElement('form');
form.setAttribute('method', 'GET'); // Send as a GET request.
form.setAttribute('action', oauth2Endpoint);
//Get the state and redirect_uri parameters from the request
var searchParams = new URLSearchParams(window.location.search);
var state = searchParams.get("state");
var redirect_uri = searchParams.get("redirect_uri");
//var client_id = searchParams.get("client_id");
// Parameters to pass to OAuth 2.0 endpoint.
var params = {
'client_id': YOUR_CLIENT_ID,
'redirect_uri': redirect_uri,
'scope': 'email',
'state': state,
'response_type': 'token',
'include_granted_scopes': 'true'
};
// Add form parameters as hidden input values.
for (var p in params) {
var input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', p);
input.setAttribute('value', params[p]);
form.appendChild(input);
}
// Add form to page and submit it to open the OAuth 2.0 endpoint.
document.body.appendChild(form);
form.submit();
}
oauth2SignIn();
</script>
</body>
</html>

It sounds like what you are doing is having the user log into your page, and using this to get an auth token from a Google service. You're then turning this around and passing this back to the Assistant and calling this the Identity Flow.
While clever - this isn't the Identity Flow.
This is you using the Auth Code Flow to authenticate the user with Google, and then returning this token to Google and pretending this is an Identity Flow token. However, since you're using the Auth Code Flow, the auth tokens that you get back expire after an hour. (You can check out the lifetime in the information you get back from Google.)
If you are trying to do Account Linking and not manage anything yourself, you need to actually implement an OAuth server that proxies the Auth Code Flow requests from the Assistant to Google and the replies from Google back to the Assistant. While doable, this may be in violation of their policy, and isn't generally advised anyway.
Update to address some questions/issues in your comment.
using the Google Auth endpoints doesn't store the session either, so you'd still have to re-authenticate every hour
Since the Google Auth endpoints use the Auth Code Flow, you can use the offline mode to request a refresh token. Then, when an auth token expires, you can use the refresh token to get a new auth token. So you still have a long-term authorization for access and can get the short-term token to do the work you need.
Trying to shoehorn this into the Identity Flow, however, doesn't work. (And would be a really bad idea, even if it did.)
Can you provide some clarification on how to create an endpoint for the implicit flow?
Beyond the step-by-step description of what your OAuth server code can do in the Assistant documentation, I'm not sure what clarification you need. Your OAuth server fundamentally just needs to:
Be able to have a user:
Connect to an HTTPS URL
Authenticate themselves
Authorize the Assistant to contact your service on their behalf
Return a code by redirecting the user to Google's URL with a code in the parameter
And the Action webhook needs to be able to:
Accept this code as part of the request from the Assistant and
Figure out who the user is from this code. (ie - map the code to a user account in your system.)
There are a variety of ways you can do all of that. The OAuth server and Action could be on the same server or separate, but they at least need to have some agreement about what that code is and how that maps to your user accounts.
If your primary need is to access Google APIs on behalf of your user - then the user account that you have will likely store the OAuth tokens that you use to access Google's server. But you should logically think of that as separate from the code that the Assistant uses to access your server.
(As an aside - those steps are for the Identity Flow. The Auth Code Flow has a few more steps, but the fundamentals are similar. Especially on the Action side.)

Related

Clarification on Google Authentication and Authorization, with OAuth in 2022

I am writing an App and am trying to leverage Google for A&A. The app itself relies on access to the users Google Calendar, and so initially I leveraged their updated OAUTH2 library for A&A.
Here is my flow:
User goes to the index.html which has "https://accounts.google.com/gsi/client" script and google.accounts.oauth2.initCodeClient is called with my client_id, scopes, redirect url
<script src="https://accounts.google.com/gsi/client"></script>
<script>
let client;
function initClient() {
client = google.accounts.oauth2.initCodeClient({
client_id: 'xxxxx-xxxx.apps.googleusercontent.com',
scope:
'https://www.googleapis.com/auth/userinfo.profile \
https://www.googleapis.com/auth/userinfo.email \
https://www.googleapis.com/auth/calendar.readonly \
https://www.googleapis.com/auth/calendar.events',
ux_mode: 'redirect',
redirect_uri: 'http://localhost:5000/oauth2callback',
});
}
// Request an access token
function getAuthCode() {
client.requestCode();
}
The user clicks the login button, which kicks off requestCode() and they begin the login flow. They login or select their google account, then besides the unapproved app screen, they get to the consent screen with my requested scopes.
After, they are redirected to my expressjs endpoint and using the "googleapis" library I exchange with id_token for the access and refresh tokens.
...
const { tokens } = await oauth2Client.getToken(req.query.code); //exchange code for tokens
const userInfo = (
await oauth2Client.verifyIdToken({
idToken: tokens.id_token,
audience: config.google.clientID,
})
).payload;
if (!indexBy.email[userInfo.email]) { // check if user exists
const newUser = {
name: userInfo.name,
email: userInfo.email,
o_id: userInfo.sub,
picture: userInfo.picture,
r_token: tokens.refresh_token,
};
...
Ok, all good.... but not quite. The problem is, that next time the user wants to login to the app, they go through the entire flow again, including the consent screen (again).
So, after going through more docs, even looking at examples from google. I was surprised and I noticed that many of those apps used the passport oauth2 plugin :( Something i've done in the past, but was hoping to avoid that with the recently updated Google client and nodejs libraries.
Ok, how to not prompt for consent screen on subsequent logins?
Maybe separate A&A, so first I use "Sign In With Google" for Authentication, then when I get the user info, check if the user is already registered (hence I have already saved the refresh token) and they start the app.
On the other hand, if they are new (not in existing app user collection), after authenticating, I will then call the OAUTH2 authorization redirect, so again they on Googles site, this time to do the scopes api confirmation.
So, first question, is that the best practice with most apps with leverage a Google API via OAuth? To first Authenticate, then possibility Authorize (as needed). Hopefully this will still work ok when things come up with expired/invalid refresh token (fingers crossed the default google library handles that).
When doing the Authorize for consent, can I pass something from the previous Authenticate flow so they don't need to do that again.
Or maybe when doing the Authenticate process (Google Identity Service), there is some flag or param so that if they have already consented, they don't have to do that again on subsequent logins.
Incase I wasn't clear, in a nutshell the question is: should I be doing Authenticate for login, separately from Authorization (oauth2 token). Or should I go right into the Authorization flow, which first Authenticates the user, and can I skip the Authorization consent screens if they've already done that. Or maybe there's another way which is the best practice.
Thanks for your attention.
Background info
Authentication is the act where by a user logs in into a system using their login and password. With authentication we know that the user is behind the machine. For this we use Open id connect, which was built on top of Oauth2. Open id connect returns and id_token which can be used to identify the user, it is often a jwt containing some claims to identify the subject or the user behind the Authentication.
The scope used for open id connect is profile and email. open id connect grants you consent to access a users profile information.
This is an example of the decrypted id token returned by google from a simple call using profile scope only. All this id token is telling you is who the user behind the machine is.
{
"iss": "https://accounts.google.com",
"azp": "4074087181.apps.googleusercontent.com",
"aud": "4074087181.apps.googleusercontent.com",
"sub": "1172004755672775346",
"at_hash": "pYlH4icaIx8PssR32_4qWQ",
"name": "Linda Lawton",
"picture": "https://lh3.googleusercontent.com/a-/AOh14GhroCYJp2P9xeYeYk1npchBPK-zbtTxzNQo0WAHI20=s96-c",
"given_name": "Linda",
"family_name": "Lawton",
"locale": "en",
"iat": 1655219027,
"exp": 1655222627
}
In the same call google also returned an access token. Now my call contained only the scope for profile, due to the fact that its open id connect. This means that I will only have access to the data that the profile scope would grant access to. In this case most of what is behind the Google people api.
Note: The user does not see a consent screen with open id connect, even though they are consenting to profile scope. It is assumed by signing into your account that the system you are logging into would have access to your profile info.
Authorization
Authorization is the process by which a user grants your application authorization to access their private user data. The user is shown a consent screen where they consent to your application accessing data defined by some scopes.
In the case of google calendar api there are serval
https://www.googleapis.com/auth/calendar See, edit, share, and permanently delete all the calendars you can access using Google Calendar
https://www.googleapis.com/auth/calendar.events View and edit events on all your calendars
https://www.googleapis.com/auth/calendar.events.readonly View events on all your calendars
https://www.googleapis.com/auth/calendar.readonly See and download any calendar you can access using your Google Calendar
https://www.googleapis.com/auth/calendar.settings.readonly View your Calendar settings
In this case you are only given an access token this is again Oauth2 it is authorization to access the users calendar data it is not authentication this is not related to login.
Your question
So, first question, is that the best practice with most apps with leverage a Google API via OAuth? To first Authenticate, then possibility Authorize (as needed).
You would do both at the same time.
When you authencation your user make sure to include your google calendar scope then the access token and refresh token returned will grant you access to google calendar.
I am going to assume that you have some kind of user system. When you store the user be sure to store the refresh token that is returned.
As far as Authentication goes i will assume you either have a remember me system which will set a cookie on their machine and remember the user so that you can then get the refresh token from their system the next time they come back.
If they did not chose to select a remember me option then will then have to login every time they visit your site but part of the login will return the "sub": "1172004755672775346", this is the users id on google system so you can use that in your database to match the user when they come back.
Your question is quite complex and will depend upon the type of system you have what it is designed to do as well as what programming language you are using. That being said I hope this very long answer clears things up a bit.

OpenID Connect server. Generate access token based on 3-rd party token (social login)

I had implemented OpenID Connect server that generates access tokens for mobile client based on username/password using OpenIddict.
My next goal was to provide ability to generate Access Token using 3-rd party tokens (social login for example), and I started from integration with Google token, but stuck as cannot find any samples/informations about how to do this.
The only one idea that I currently have is to make request to "/connect/token" endpoint and send Google token in "code" parameter, for example in "google:" format, then override OpenIdConnectServerProvider.DeserializeAuthorizationCode method:
Called when receiving an authorization code. An application may use this context to deserialize the code using a custom format and to skip the default logic using
So I have created own CustomProvider class based on OpenIddictProvider, registered it
services.AddOpenIddict<ApplicationUser, ApplicationRole, ApplicationDbContext, int>()
.Configure(builder =>
{ builder.Provider = new CustomProvider(sp.GetRequiredService<SignInService>()); }
and overridden the DeserializeAuthorizationCode method:
public override async Task DeserializeAuthorizationCode(DeserializeAuthorizationCodeContext context)
{
string code = context.Request.Code;
if (code.StartsWith("google:"))
{
string token = code.Replace("google:", "");
var principal = new GoogleTokenValidator().ValidateToken(token, null).Result;
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), "Bearer");
ticket.SetPresenters(context.Request.ClientId);
context.Ticket = ticket;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(1);
context.HandleResponse();
await _signInService.Login(principal);
return;
}
else
{
base.DeserializeAuthorizationCode(context);
}
}
where GoogleTokenValidator is a custom class for Google token handling (it makes call to Google User Information Endpoint and generate ClaimsPrincipal), based on "copy-pasted" code from GoogleHandler class in aspnet/Security repo.
In general it is working with some additional hacks, but I have strong feeling that reinventing the wheel...
In general it is working with some additional hacks, but I have strong feeling that reinventing the wheel...
You're not only reinventing the wheel, but you're also implementing something totally non-standard that is not supported (at all) by OpenIddict.
Here's the approach I recommend (which is the one we use in the MVC server sample):
The OAuth2/OpenID Connect client application redirects the user agent to your authorization controller (you can take a look at this controller for an example).
OpenIddict will validate the authorization request and allow your controller to be invoked if it's fully valid.
If the user is not already logged in, your authorization controller will redirect the user to the login endpoint, provided by AccountController. At this point, you're free to propose local authentication (e.g using a username/password couple) or Google authentication (you can use the Google authentication middleware for that). You can even offer 2-FA as part of this login process.
Once the user is logged in (e.g after a registration process and/or an external authentication association), his/her browser is redirected back to the authorization endpoint, where a consent form indicating he/she's about to allow your JS app to access his personal data on his/her behalf is displayed.
When the user allows your client application to access his data, the request is handled by your authorization controller, that calls SignInAsync to inform OpenIddict that an authorization code/access token should be returned to your application.

Problems configuring user authentication by external API on Symfony2

I have a problem authenticating users for my new Symfony2 application.
This applications gets all the info through an API, so no database is used. When a user goes to login page, he introduce the user and password in the login form. Then, I have to authenticate him using an API call. This API call returns "false" if it's not a user, and return a token key and a token secret if its a correct user. With this token key and secret, during the user session, I can make all the API requests I need for rendering all the pages of the application. Once the user session is over and token key and secret are erased, the user has to login again.
I don't know really how ti implement that. I read this http://symfony.com/doc/current/cookbook/security/custom_provider.html and that http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html, and I'm still so lost... :(
Can any one help me?
Thank you so much :)
If you want to write custom authentication you have found the correct links. As an example you can see the implementation of the OAuth authorization HWIOAuthBundle. But keep in mind that this type of authentication creates a user on your system. If you do not use a database, you must make a request to the API every time user send a request.
First you need to understand that there is no magic. On every request symfony checks if url matches one of the specified firewalls (see secutity.yml). Listener that fired you can see in the firewall factory. If matches are found, the action switches to the corresponding AuthenticationListener. Listener attempts to authenticate the credewntials by creating Token, which is sended to AuthenticationProvider
$this->authenticationManager->authenticate(new UsernamePasswordToken($username, $password, $this->providerKey));
in AuthenticationProvider
public function authenticate(TokenInterface $token) {
...
}
AuthenticationProvider try to get user via UserProvider. In case of success, Token stored in the session. On subsequent requests, ContextListener comes into play first, checks the session, extract token and send it to AuthenticationProvider similar.
In general terms, the scheme looks like that. More info you can find examining the source code of Symfony Security component.
Really good starting point is a UsernamePasswordFormAuthenticationListener. It just take login and password from request and make simplest UsernamePasswordToken.
protected function attemptAuthentication(Request $request)
{
...
}
Good luck!

How can I use Google's OpenID and/or OAuth services to login and allow access to APIs with only ever one prompt to the user?

I am attempting to create a login system for my website that permits both authentication via Google's API and access to any of the OAuth-supported Google Data APIs while ideally only showing the user one prompt ever, no matter if he's creating an account or logging into his existing one. I want to minimize the number of times he's asked for approval.
I am aware that Google provides Hybrid OpenID/OAuth for this purpose, but the issue is that every time I add OAuth extensions to my OpenID request, it never remembers the user's approval for that request. Is there any way for the approval to be remembered when I am doing Hybrid OpenID/OAuth? If I just do OpenID without OAuth extensions, everything is remembered just fine and it doesn't keep bugging the user with the prompt.
Here are the pertinent extensions I'm sending in addition to my OpenID request, which result in me getting an OAuth request token (good) but cause the approval to never get remembered (bad).
PHP syntax:
$args["openid.ns.ext2"] = 'http://specs.openid.net/extensions/oauth/1.0';
$args["openid.ext2.consumer"] = 'www.MYSITE.com';
$args["openid.ext2.scope"] = 'http://www-opensocial.googleusercontent.com/api/people/';
$args["openid.mode"] = 'checkid_setup';
$args["openid.realm"] = 'http://www.MYSITE.com/';
Is it normal for Hybrid OpenID/OAuth to act this way (not remembering the last OAuth authorization)? What is the best way to get around this? I have thought of storing cookies on the user's computer to link to somewhere in my database so I could use the last access token again, etc... (the issue here being I don't know whose token to look up unless I know who the user is...a circular problem). And doing an OpenID-only request to get his user ID to see if he has an account in order to look up his access token, followed by an OpenID+OAuth request (if an access token for him isn't stored) would result in two prompts, which really wouldn't help.
It also seems like Hybrid only supports OAuth 1.0, which I think is fine until 2015, so it's not an issue right now for me. I am assuming they will support OAuth 2.0 in the future.
Is checkid_immediate relevant to this in any way? I'm just not sure how to use this to accomplish what I want.
I would suggest using OAuth 2.0. This supports getting both identity and access to APIs -- so accomplishes the same end goal, but is much easier than OAuth 1 Hybrid.
Take a look here:
https://developers.google.com/accounts/docs/OAuth2Login
The scopes you're trying to access are included in the URL (see "Forming the URL"). The referenced doc lists the scopes required for getting identity/profile information. You can simply add additional scopes to the string, comma-delimited in order to request access to other APIs. The resulting access token will access both the APIs and identity information (via the UserInfo API endpoint mentioned).
That said, what you're trying to do with OpenID 2.0/OAuth 1 hybrid should work-- and the user should see a checkbox for "remembering" the authorization. If you really want to debug this further, it'd be helpful to have a webpage you can point to which kicks off this authentication+authorization flow so we can see what's happening.
I figured out that checkid_immediate (and x-has-session, not sure if that's needed or even working) is allowing me to determine whether or not a user is logged in without prompting him, and if he is, it is giving me a claimed_id by which I can identify the user. That's exactly what I needed. The original question is solved, but I do want to figure out how to use identify with OAuth 2.0 because I have already implemented that.
Furthermore, I've noticed that when using OpenID/OAuth that the user still gets asked to authorize OAuth even after he's authorized OpenID. I can't see the advantage to the hybrid approach from the user's perspective.
If the user is logged out of Google, that's a total of three prompts just to sign up for my website and grab his name and profile image.
If anyone wondered, here are the steps necessary to get Hybrid OpenID/OAuth completely working (an overview). I was confused thoroughly throughout this process, so I hope this helps someone.
Do normal OpenID handshake and add on AX extensions for OAuth 1.0.
Use 'checkid_immediate' to permit probing for an active Google session without prompting the user. Use *claimed_id* as a unique identifier to link the user to your database.
If 'setup_needed' is returned, use 'checkid_setup' so the user is prompted and verified before continuing.
This leaves you with two possibilities. *checkid_immediate* returning immediately giving you a claimed_id, or a claimed_id coming through after *checkid_setup* (basically sign-up) succeeds.
Hybrid OpenID/OAuth 1.0 will give you an authorized request token.
Use the authorized request token to get an access token (you only need to call OAuthGetAccessToken)
Use that OAuth 1.0 access token to do whatever you want.
I was successful in using OAuthGetAccessToken to get an access token from the authorized request token my Hybrid OAuth dance, omitting the 'oauth_verifier' parameter (irrelevant to Hybrid).
I was successful in using OAuthGetAccessToken to get an access token after my Hybrid OAuth dance, omitting the 'oauth_verifier' parameter (irrelevant to Hybrid).
In a PHP/Zend environment:
$config = array(
'accessTokenUrl' => 'https://www.google.com/accounts/OAuthGetAccessToken',
'consumerKey' => $consumer_key,
'consumerSecret' => $consumer_secret
);
$consumer = new Zend_Oauth_Consumer($config);
$zendRToken = new Zend_Oauth_Token_Request(); // create class from request token we already have
$zendRToken->setToken($requestToken);
try{
$accessToken = $consumer->getAccessToken(array(
'oauth_token' => $requestToken,
// 'oauth_verifier' => '', // unneeded for Hybrid
'oauth_timestamp' => time(),
'oauth_nonce' => md5(microtime() . mt_rand()),
'oauth_version' => '1.0'
), $zendRToken);
} catch (Zend_Oauth_Exception $e){
echo $e->getMessage() . PHP_EOL;
exit;
}
echo "OAuth Token: {$accessToken->getToken()}" . PHP_EOL;
echo "OAuth Secret: {$accessToken->getTokenSecret()}" . PHP_EOL;

How to identify a Google OAuth2 user?

I used Facebook login to identify users. When a new user comes, I store their userID in my database. Next time they come, I recognized their Facebook ID and I know which user it is in my database.
Now I am trying to do the same with Google's OAuth2, but how can I recognize the users?
Google sends me several codes and tokens (access_token, id_token, refresh_token), however none of them are constant. Meaning if I log out and log back in 2 minutes later, all 3 values have changed. How can I uniquely identify the user?
I am using their PHP client library: https://code.google.com/p/google-api-php-client/
As others have mentioned, you can send a GET to https://www.googleapis.com/oauth2/v3/userinfo, using the OAuth2 bearer token you just received, and you will get a response with some information about the user (id, name, etc.).
It's also worth mentioning that Google implements OpenID Connect and that this user info endpoint is just one part of it.
OpenID Connect is an authentication layer on top of OAuth2. When exchanging a authorization code at Google's token endpoint, you get an access token (the access_token parameter) as well as an OpenID Connect ID token (the id_token parameter).
Both these tokens are JWT (JSON Web Token, https://datatracker.ietf.org/doc/html/draft-ietf-oauth-json-web-token).
If you decode them, you'll get some assertions, including the id of the user. If you link this ID to a user in your DB, you can immediately identify them without having to do an extra userinfo GET (saves time).
As mentioned in the comments, these tokens are signed with Google's private key and you may want to verify the signature using Google's public key (https://www.googleapis.com/oauth2/v3/certs) to make sure they are authentic.
You can see what's in a JWT by pasting it at https://jwt.io/ (scroll down for the JWT debugger). The assertions look something like:
{
"iss":"accounts.google.com",
"id":"1625346125341653",
"cid":"8932346534566-hoaf42fgdfgie1lm5nnl5675g7f167ovk8.apps.googleusercontent.com",
"aud":"8932346534566-hoaf42fgdfgie1lm5nnl5675g7f167ovk8.apps.googleusercontent.com",
"token_hash":"WQfLjdG1mDJHgJutmkjhKDCdA",
"iat":1567923785,
"exp":1350926995
}
There are also libraries for various programming languages to programatically decode JWTs.
PS: to get an up to date list of URLs and features supported by Google's OpenID Connect provider you can check that URL: https://accounts.google.com/.well-known/openid-configuration.
I inserted this method into google-api-php-client/src/apiClient.php:
public function getUserInfo()
{
$req = new apiHttpRequest('https://www.googleapis.com/oauth2/v1/userinfo');
// XXX error handling missing, this is just a rough draft
$req = $this->auth->sign($req);
$resp = $this->io->makeRequest($req)->getResponseBody();
return json_decode($resp, 1);
}
Now I can call:
$client->setAccessToken($_SESSION[ 'token' ]);
$userinfo = $client->getUserInfo();
It returns an array like this (plus e-mail if that scope has been requested):
Array
(
[id] => 1045636599999999999
[name] => Tim Strehle
[given_name] => Tim
[family_name] => Strehle
[locale] => de
)
The solution originated from this thread: https://groups.google.com/forum/#!msg/google-api-php-client/o1BRsQ9NvUQ/xa532MxegFIJ
It should be mentioned, that the OpenID Connect API returns no id attribute anymore.
It's now the sub attribute which serves as a unique user identification.
See Google Dev OpenID Connect UserInfo
"Who is this?" is essentially a service; you have to request access to it as a scope and then make a request to the Google profile resource server to get the identity. See OAuth 2.0 for Login for the details.
Altough JWTs can be validated locally with the public key, (Google APIs Client Library downloads and caches they public keys automatically) checking the token on Google's side via the https://www.googleapis.com/oauth2/v1/tokeninfo endpoint is necessary to check if the access for the applicaton has been revoked since the creation of the token.
Java version
OAuth2Sample.java