OAuth2.0 token strange behaviour (Invalid Credentials 401) - google-api

Usually, Google OAuth2.0 mechanism is working great.
The user confirms permission to access Google account with selected scopes.
The refresh token is retrieved and saved to long time storage.
Each time needed (if the access token expired) access token is retrieved and used to access APIs.
But sometimes (thus far only two times for more than 6 months) I've experienced strange behaviour:
Requests to Google APIs return Invalid Credentials (401) error.
Refreshing the access token (using the stored refresh token) does not help.
Here is some structured output I've got when testing this issue:
+ ------------------------------------------------------------------------- +
| 1.TRYING TO REFRESH THE TOKEN. |
| 2.DONE REFRESHING THE TOKEN. |
+ ------------------------------------------------------------------------- +
| access: **************************************************** |
| refresh: ********************************************* |
| expires: 3600 |
| created: 2013-07-23 13:12:36 |
+ ------------------------------------------------------------------------- +
I've also tried to verify the "fresh" access token by sending requests to
https://www.googleapis.com/oauth2/v1/tokeninfo
+ ------------------------------------------------------------------------- +
| 1. TRYING TO CHECK THE TOKEN . |
| 2. DONE CHECKING THE TOKEN THE TOKEN. |
+ ------------------------------------------------------------------------- +
| issued_to: ************.apps.googleusercontent.com |
| audience: ************.apps.googleusercontent.com |
| user_id: ************ |
| expires_in: 3600 |
| email: **********#gmail.com |
| verified_email: 1 |
| access_type: offline |
| scopes:: |
+ ------------------------------------------------------------------------- +
| https://www.googleapis.com/auth/userinfo.email |
| https://www.googleapis.com/auth/userinfo.profile |
| https://www.googleapis.com/auth/plus.me |
| https://www.googleapis.com/auth/drive |
+ ------------------------------------------------------------------------- +
But when I try to access drive feed the response is:
Error calling GET https://www.googleapis.com/drive/v2/files (401) Invalid Credentials
domain: global
reason: authError
message: Invalid Credentials
locationType: header
location: Authorization
We also experienced the same issue with calendars.
So:
Token was valid before (everything worked).
Refreshing token still works.
Requesting a feed responds with "Invalid Credentials" error.
All the other tokens are still working great, meaning that the code is valid.
Normally when the token is revoked "invalid_grant" error is returned when trying to refresh the token.
Questions
What can be the reason for this behaviour? If the refresh token was revoked or got invalid in some other way, should the request for new access token produce error?
Is there a way to validate the refresh token?

Per the Google API docs on errors & error codes:
https://developers.google.com/drive/handle-errors#401_invalid_credentials
401: Invalid Credentials
Invalid authorization header. The access token you're using is either expired or invalid.
error: {
errors: [
{
"domain": "global",
"reason": "authError",
"message": "Invalid Credentials",
"locationType": "header",
"location": "Authorization",
}
],
"code": 401,
"message": "Invalid Credentials"
}
}
This matches your version of the error exactly, and so is very probably what Google thinks is wrong with your request.
But, as you well know, Google API requests can return errors that are distinctly unhelpful to actually diagnosing the problem. I have gotten "Invalid Credentials" errors for a number of reasons. It is almost always really because I have made some sort of change that I thought would not matter, but really does.
My first thought (shot in the dark here) would be to go to the Google API console:
https://code.google.com/apis/console
Googles auth token verifier ( https://www.googleapis.com/oauth2/v1/tokeninfo ) can return a valid response, but maybe the client secret or client id will have been changed.
Even tiny changes in the response body can also cause this error.
I don't know how you are making requests, whether by REST calls or a client lib, but I use the ruby lib which allows a command line interface to making API calls. I have found this & the OAuth2 Playground very helpful in diagnosing Google API calls.
Just an FYI: I have only gotten 2 errors from the Google API: "Invalid Credentials" and "Insufficient Permissions". The latter has almost always had to do with bad scopes. The former is just about everything else.
I would also say that if you have only experienced 2 errors in 6 months, you are lucky!

I had this problem when I tried experimenting with changing the redirect url in google console and then updating my json credentials file on server. I had to clear the session variables before starting afresh. So in your project just do this once:
session_start(); //starts a session
session_unset(); //flushes out all the contents previously set
Remember to remove the session_unset() after dry running it once.

I'm on Development environment. I had this problem too.
First I tried refreshing the credentials. No result. Then I deleted my app (since I'm still on development enviroment, that was ok, but BE CAREFUL WITH THIS ACTION if you're already using this on production), created a new one, updated the credentials JSON on the client... still, no result.
I solved it by opening on a new browser instance which wasn't logged in my Google Account (Private Browsing, since I'm on Firefox), logged on my Google Account once again, and tried using my client (which is a Web Application). I was redirected to the authorization screen as expected and after that, it worked fine for me.

Maybe this behavior is due to a limitation which Google describes as follows:
There is currently a limit of 50 refresh tokens per user account per client. If the limit is reached, creating a new token automatically invalidates the oldest token without warning. This limit does not apply to service accounts.
There is also a larger limit on the total number of tokens a user account or service account can have across all clients. Most normal users won't exceed this limit but a developer's test account might.

I recently experienced this weird error. My fix: I put the function that unsets all of the sessions before redirecting to AuthUrl.

clearing storage in Google Chrome worked for me (don't know all the details of what 'Clear storage' is clearing):
F12 (Ctrl+Shift+I)
Application Tab
Clear storage

I had the same problem with this error:
The redirect URI in the request, does not match the ones authorized for the OAuth client.
But found this very simple solution by abhishek77in here:
https://coderwall.com/p/fmr5ag/avoid-invalid-credentials-with-google-oauth2
The solution is:
If you are using
https://github.com/zquestz/omniauth-google-oauth2 make sure to
follow the note in README. "You must enable the "Contacts API" and
"Google+ API" via the Google API console."
Enabling these in the Google API console fixed the "Invalid credentials" problem for me.

userInfo: Invalid Credentials
I got the following error because the scopes array elements i was trying to access ie profile and email whose links I got from google+ api scope page: came to be somehow false/invalid so I went to my consent screen and there
under Scopes for Google APIs was mentioned email profile openID on hovering on each I got there respective urls replacing my old ones with these resolved my error

I received (401) Invalid Credentials when I removed the access to my Google Account for the particular app. So what I had to do was to request the authorization URL (the one which starts with https://accounts.google.com/o/oauth2/auth), again.

I ran into this same problem when I needed to change my scopes from Read Only to Read And Write All Files. So, I updated my scopes from at the top of my file from Read Only to:
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/sheets.googleapis.com-nodejs-quickstart.json
var SCOPES = ['https://www.googleapis.com/auth/drive'];
Google, from their API guide, has these comments that say whenever you change scopes, you must update credentials. I believe this means, although I am not certain, that the token must be updated. The old token is still held by Google and it thought that I only had Read Only access, hence why it would return a 401 error. So, I need to remake my token, but Google never offered a new consent screen that would allow me to say allow Read And Write To All Files. So, I needed to get that screen to come up again, so it would create a new token to replace the old one:
fs.readFile(TOKEN_PATH, function(err, token) {
if (err) {
getNewToken(oauth2Client, callback);
} else {
getNewToken(oauth2Client, callback);
// oauth2Client.credentials = JSON.parse(token);
// callback(oauth2Client);
}
});
Since I already had a saved token, it was never creating a new one. So, I just commented out the using of the old token and told it to get a new token, no matter if we have one or not. Then, I went to my Connected Apps in Google and deleted my old connecting credential. I'm not sure if this step is necessary, but I am only trying to access my personal account. Then, when I ran my program, it prompted me to re-authenticate, and everything worked and I did not receive an authentication error. Once done, make sure to remove the commented out lines for using already made tokens. I was using the Google API quickstart.js file for all of this.
So, when I updated my scopes, the old token was still using the Read Only scope, therefore I would get (401) Invalid Credentials.

I resolved this problem when I removed files json in c:\Users\[user]\.credentials.

Maybe this is helpful to someone:
I had a similar issue using the JavaScript Google-API client for Calendar API. At random times it would work but mostly I got the same error. Adding scopes, testing key, nothing helped. After a few hours I found this solution, no idea why it works but it solved the issue for me:
gapi.client.init({
'apiKey': API_KEY, <-- DOESN'T WORK
'clientId': CLIENT_ID,
'discoveryDocs': DISCOVERY_URLS,
'scope': SCOPE
}).then(function() {
// gapi.client.setApiKey(API_KEY); <-- ADD THIS
})

If you're using an account that's part of a GSuite set up, you might need to add GSuite Basic to the account. You get to this by Users > Click on user > Licenses.

Related

my app is not able to receive refresh token from Google anymore

i have a very weird problem. I'm using the Google authentication API since moree than one month now and all working perfect. But now out of the sudden, my users can't get refresh token anymore. My app is on testing state, so i thought the refreesh token my testing user was having is expired after 7 days, but then i tried to get another refresh token by doing thee authorization from the beginning to receive a code that i use to get a refresh tokn. But no chance i'm only receiving this response back: Status code 400 { "error": "invalid_grant", "error_description": "Bad Request" }
Thank you very much for your help!
A Google Cloud Platform project with an OAuth consent screen configured for an external user type and a publishing status of "Testing" is issued a refresh token expiring in 7 days.
To stop your refresh tokens from expiring set it to production.
Why cant you refresh after seven days
What i am writing here is my opinion only from experience. There is no documented proof of any of this from googles side.
After seven days your refresh token will expire, but the question is how is google expiring these refresh tokens. From what i can see they are not using the normal method of expiring the refresh token. They are in fact revoking the users granted access on the google account. So the all of the refresh tokens granted will stop working at once.
So why are you having issues with the client library. Normally the way the client libraries were originally designed. if the refresh token expired it would prompt the user to authorize the app again. This does not happen with the seven day revoke method. IMO because the error message is different, and the libraries have not been updated to take this into account, and prompt for access again. The only way to fix it is to delete the old stored refresh token and request a new one.
So your not able to receive new refresh tokens because your code is stuck with the old one. Make sure to hard delete any old refresh tokens you have stored. They wont work and the library doesnt understand how to delete them on its own.

Alexa skill account linking with Google APIs credentials, problem refreshing token

I'm having some problems with the Alexa account linking authorization.
These are the steps I followed:
I got the credentials (client id, client secret...) from the Google Cloud Console
Setup on the Alexa Developer Console, using 'Auth Code Grant' as authorization grant type
Activated the skill on my Alexa application and successfully logged in with my Google account
Now I got the access token in the request, in handler_input.request_envelope.context.system.user.access_token
The problem is that the access token expires after one hour and Alexa does not manage the refreshment of the token.
What should I do to avoid having to ask my users to login every time after one hour? Should I use Implicit grant as authorization type? Should I get a refresh token somehow?
Additional info: it's a custom skill that connects to an AWS Lambda using Python3
While #pinoyyid's answer was correct, it didn't provide a solution so I'm posting one for future reference.
The problem was indeed that Amazon servers did not receive a refresh token from Google, thus making it impossible to refresh the access token after its expiration time of one hour.
Following this link and other Amazon forum posts, I got to a working solution.
Amazon Alexa developer console 'Account Linking' configuration:
Authorization grant type: Auth Code Grant
Authorization URI: https://accounts.google.com/o/oauth2/v2/auth?access_type=offline (even though the one from the google credentials was not v2, it shouldn't make a difference)
The access type is very important because, as documentation goes:
Set the [access_type] value to offline if your application needs to refresh access tokens when the user is not present at the browser. [...] This value instructs the Google authorization server to return a refresh token and an access token the first time that your application exchanges an authorization code for tokens.
Access Token URI: https://accounts.google.com/o/oauth2/token
Client ID & Secret: downloaded on Google Cloud Platform
Client Authentication Scheme: HTTP Basic
Domain List: google.com and googleapis.com
Default access Token Expiration Time: left empty
Now, after doing this and saving the configuration, be aware that you might not notice the change, as, from here:
When Alexa uses the refresh token to retrieve a new access token for an existing user, Alexa uses the access token URI that was configured at the time the user linked their account. Therefore, if you change the access token URI later, users who linked their accounts before continue to use the old URI for retrieving updated tokens. The users must unlink and re-link accounts to switch to the new access token URI.
So, in order to complete the procedure:
Deactivate your skill
Go to the Google third party applications that have access to your data and remove your Google Project associated
Reactivate your skill and login again (if done correctly it should ask you the permissions for the scope you specified in the Alexa developer console again
Done! after one hour you should re-try and it should have a renewed access token
Additional Info
I found that many suggested to retrieve the refresh token, I don't believe this is possible because, even if Google sends it, it's Amazon that stores it and uses it to refresh the access token.
EDIT:
This works fine for developing and testing but I discovered here that for publication purposes you must own the landing page that you redirect your users to. For me it was just necessary to create a simple HTML page hosted in a public S3 bucket that would redirect the request to the Authorization URI I wrote before, while the Access Token URI must remain the Google one.
Have you read https://developer.amazon.com/docs/account-linking/configure-authorization-code-grant.html ?
My guess is that the Refresh Token is missing because you have already auithorised the app. The RT is only issued once. Try going into https://myaccount.google.com/permissions?utm_source=google-account&utm_medium=web to revoke the permission and try again.

Google token refresh returns "Token has been expired or revoked."

I have a Google OAuth2 client approved by Google, which provides offline access to user's account with required scopes. My backend application stores and uses the refresh token to refresh the access tokens as and when needed.
Lately, we are seeing that our token refresh attempt is met with an error from Google with:
{
"error" : "invalid_grant",
"error_description" : "Token has been expired or revoked."
}
There is no additional information.
Nothing has changed in my Google OAuth client. The user has not changed account password. The user has not revoked access to my client.
What could be the reason for suddenly getting such errors for token refresh? And how do I avoid this in future (if possible)?
Are you inadvertently asking for the refresh token multiple times? There is a limit of approx. 25 refresh tokens that an account can have extant.
I had same issue, because I run my code in 2 different files and what I did remove token.pickle file, re-run it again.

403 Forbidden error when deleting YouTube video

I have been using the OAuth approach to upload,update and delete videos on YouTube. That all has been working fine until about February 12th where all of those processes stopped working. Now when I go to delete a YouTube video I get the following error:
"code": 403,
"message": "The video that you are trying to delete cannot be deleted. The request might not be properly authorized."
I know that the OAuth process is working because I can get the token and refresh the token if it has expired. I'm using the latest PHP library that Google have provided (installed using composer). And I can get information about a valid YouTube video by making the following call:
$videoId = "xxxxxxx"; //id of video on YouTube
$youtube->videos->listVideos("snippet", array('id' => $videoId));
But then the delete call gives that error above.
$youtube->videos->delete($videoId);
Since the listVideos works, that confirms that the client_id, secret key and token is correct. I also am setting the scopes to be the following
$client->setScopes(array('https://www.googleapis.com/auth/youtube',
'https://www.googleapis.com/auth/youtube.upload',
'https://www.googleapis.com/auth/youtubepartner'));
I also checked that the credentials and quotas set in the Google APIs are ok. I thought that maybe the quota had been reached but that doesn't appear to be the case. I did see an email from YouTube in early february saying that they have a new terms of service and a developer policies. I reviewed all that but nothing in there appears to point to the issue I am having?
Not sure what else to try?
Try to check my answer here in this SO question that focus on how to Refresh Token with the Google API.
Your error usually caused by:
When the token expires
The token’s scope (this is important)
If the token is invalid
If the token is invalid you can troubleshoot like this:
Remove the access token from your datastore or database.
Use the refresh token to acquire a new access token (if you are using a refresh token)
Try to make the API call again. If it works, you’re good! If not …
Check the access token against the tokenInfo API
If it’s still invalid, do a full reauth
Hope this helps!

Okta Session API Change Recently?

In our application we have been using the "Create Session with Credentials" (which is marked deprecated) which basically looks like:
POST {{url}}/api/v1/sessions
with body:
{
"username": "{{username}}",
"password": "{{password}}"
}
This returns an id which we then use in a Validate Session call any time the user visits another page:
GET {{url}}/api/v1/sessions/{{sessionId}}
This used to work but approximately around 2015-12-15 it stopped working.
Now I get an error like:
{
"errorCode": "E0000006",
"errorSummary": "You do not have permission to perform the requested action",
"errorLink": "E0000006",
"errorId": "oaee2frg7mCRGyp3TE9tgE0Gg",
"errorCauses": []
}
We originally thought it was MFA related but my admin removed me from the AD group that requires MFA and we are still having issues.
So it creates the session fine with my creds (and fails if my password is wrong) but validation of the session fails. I have been testing this with POSTMAN.
Any idea what changed? Are we using this incorrectly?
I'm not able to reproduce the error.
That said, we are moving away from using /sessions for authentication (hence the deprecation as you have mentioned). You should use /authn (http://developer.okta.com/docs/api/resources/authn.html#authentication-operations) to authenticate the user with username/password. You won't get a session created right away like before with /sessions. Instead, you will receive a session token which you can then use to create a session with /sessions (http://developer.okta.com/docs/api/resources/sessions.html#create-session-with-session-token)
The good thing about this new flow (besides a better use of /authn and /sessions) is that you do not need an API key for this. Only a valid set of creds would get you a one-time and short-live session token - which is then immediately used to create the session.

Resources