GoogleCredential OAuth access token invalid_grant error - google-oauth

I'm trying to acquire a 2 legged oauth2 access token for google's storage api through a service account.
The relevant documentation is contained here:
https://developers.google.com/identity/protocols/OAuth2ServiceAccount
I first tried attempted to acquire the token through their HTTP/REST api. I eventually gave up being unable to get past a 400 response with the following details:
{ "error" : "invalid_grant" }
I then turned to the recommended method: using the java Google api client library (I'm using scala).
val credentialBuilder = new GoogleCredential.Builder()
.setTransport(GoogleNetHttpTransport.newTrustedTransport())
.setJsonFactory(JacksonFactory.getDefaultInstance())
.setServiceAccountId(Configuration.GoogleAPI.ServiceAccount.email)
.setServiceAccountPrivateKeyFromP12File(new File(Configuration.GoogleAPI.ServiceAccount.pkcs12))
.setServiceAccountScopes(Collections.singleton(StorageScopes.DEVSTORAGE_FULL_CONTROL))
def updateToken = {
val credential = credentialBuilder.build()
credential.refreshToken()
// ... do stuff here with token
}
Running the above, I get a TokenResponseException, with a payload identical to the error I was getting directly accessing the REST api. 400 Bad Request, invalid_grant error.
Doing some research (on both stackoverflow and elsewhere), I find that the most common reasons for this error message are:
1) The local clock is not synced with Google's servers. I suspected this even back when I was using the REST api because I've encountered this issue for other services before. I'm fairly sure its not a clock issue however, because I have repeatedly synced my clocks with an external ntp server:
sudo ntpdate -s ntp.ubuntu.com
2) The other common cause for this error is setting the service account client ID incorrectly. Most people set it to their service account client id (ending with "apps.googleusercontent.com") instead of their service account client email. Its pretty clear that this isn't the problem, since I'm correctly pointing to the email (the docs instructed this, so I followed the directions), ending with "#developer.gserviceaccount.com".
I'm stuck. I've checked the user documentation, the javadocs, various forums. The error message isn't very helpful (probably designed that way, since it's a security endpoint).
What other possible causes for this error exist? What can I do to get my service account access token?

If you are using OAuth2.0 to access a google analytics account then there could be one more reason of getting invalid_grant. As per developer docs:
If the application attempts to use an invalidated refresh token, an
invalid_grant error response is returned. The limit for each unique
pair of OAuth 2.0 client and Google Analytics account is 25 refresh
tokens (note that this limit is subject to change). If the application
continues to request refresh tokens for the same Client/Account pair,
once the 26th token is issued, the 1st refresh token that was
previously issued will become invalid. The 27th requested refresh
token would invalidate the 2nd previously issued token and so on.

Related

Skype For Business Online Authentication Error - 403 Permission Denied

Hello Microsoft/Azure/Skype experts,
I'm tasked with accessing presence data from Skype For Business Online accounts from my macOS app (native).
I'm unfortunately stuck and i always get a 403 error when i access the autodiscover request and never get the link to the applications resource
I have been following this documentation
https://learn.microsoft.com/en-us/skype-sdk/ucwa/authenticationusingazuread
STEP 1
We have registered the app in the Azure Management Portal using our Office 365 account credentials.
We have used custome redirect URL (http://localhost)
Allow Implicit Flow is set to true in manifest
We pre-configure the permissions needed for Skype for business
online
STEP 2
Issuing a GET as specified in the documentation to initiate sign in and authorization check.
GET https://login.microsoftonline.com/common/oauth2/authorize?response_type=token&client_id=c#####-4d41-485e-871f-0a22aa79e52b&redirect_uri=http://localhost
This returns a 200 OK.
STEP 3
We got the Auto discover URL as described in the documentation.
This is what i get - i use the domain marked in RED.
STEP 4
As per the documentation, they ask me to do this
Requesting an access token using implicit grant flow
So i issue a GET as described
https://login.microsoftonline.com/oauth2/authorize?
response_type=id_token &client_id=######-4d41-485e-871f-0a22aa79e52b
&redirect_uri=http://localhost
&state=8f0f4eff-360f-4c50-acf0-99cf8174a58b
&resource=https://webdirin1.online.lync.com
Now this shows the sign in page, i sign in and then it throws an error
AADSTS90014%3a+The+required+field+%27nonce%27+is+missing.
I researched and could not fix this error.
So after lots of research and looking at this Microsoft documentation LINK (https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#requesting-individual-user-consent) , apparently there is another way of getting the bearer token.
STEP 4 - SECOND TRY
I then Request individual user consent by sending the SCOPE parameter for Skype for Business.
I then issue a GET request to
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=#######-4d41-485e-871f-0a22aa79e52b&response_type=code&redirect_uri=http://localhost&response_mode=query&scope=https://api.skypeforbusiness.com/User.ReadWrite&state=12345
This returns an access code which i use in next step to get the TOKEN
STEP 5 - Get the bearer TOKEN
Issue a POST to following URL
https://login.microsoftonline.com/common/oauth2/v2.0/token With the
following data in POST body
"grant_type": "authorization_code", "client_id":
"######-4d41-485e-871f-0a22aa79e52b", "scope":
"https://api.skypeforbusiness.com/User.ReadWrite", "code":
"OAQABAAIAAACEfexX.........", "redirect_uri": "https://localhost"
This returns the bearer token in the following response JSON
{
"access_token" = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1........w4b-- gnWG_iOGtQ";
"expires_in" = 3599;
"ext_expires_in" = 3599;
scope = "https://api.skypeforbusiness.com/User.ReadWrite";
"token_type" = Bearer;
}
STEP 6
Yay! Got the bearer token at laaast!
Now back to the main documentation
https://learn.microsoft.com/en-us/skype-sdk/ucwa/authenticationusingazuread
And where we do this - 'Resending an autodiscovery request with the bearer token'
We execute a GET request to
https://webdirin1.online.lync.com/Autodiscover/AutodiscoverService.svc/root/oauth/user
Now this, as per the documentation should return this JSON
{
"_links":{
"self":
{"href":"https://webdirX.online.lync.com/Autodiscover/AutodiscoverService.svc/root/user"},
"applications":
{"href":"https://webpoolXY.infra.lync.com/ucwa/oauth/v1/applications"}
}
}
BUT i GET A 403: PERMISSIONS denied error
<div class="content-container"><fieldset>
<h2>403 - Forbidden: Access is denied.</h2>
<h3>You do not have permission to view this directory or page
using the credentials that you supplied.</h3>
</fieldset></div>
So thus i have never got the applications url and I have checked the manifest, registration and i have no idea, why i get this error.
Any inputs would be appreciated.
For step 4, you need to specify nonce=somestring in the URL. Typically this should be a securely random value that is only used once. It can contain any value.
Also, you are only requesting an id token. Set response_type=id_token+token.

Get Nike API Access Key

I am trying to make requests to the Nike+ API to get information but I do not know where to get a long-term access token from.
I know that I am making the actual request correctly (using AJAX) because I send the same request that Nike+ send when using their test console (I take it from the Chrome network tab when the test console sends). This request works for about an hour before saying the token has expired.
How do I get a long-term token? I have looked around the API and can't find it which makes me think I am missing something pretty obvious here.
Update (12/09/2016)
I need to have a client id to start making the implicit flow requests. I took one from the network tab of the chrome browser (from when I logged in) and used it as a parameter(as well as other things) for the first api call but it said it couldn't load partner details.
Any ideas? Am i even allowed to do what I want to do?
Nike+ API uses OAuth 2 for authentication. In his API there is no long-term token, but you can use refresh_token (30 days) to get new access_token (bound with expired_in) when your authentication expires and get unauthorized response. You can read more at Nike+ API documentation.

OAuth v2 (Google API) expiry Access Token

I am building an integration component using a graphical framework who has a pre-build OAuth2 connector.
This framework required following fields for OAuth v2:
Grant type
Scope
Auth Server URL
Client Id
Client Secret
Access Token
Refresh token
I need to get data from Google Analytics API, so I went to Google Dev Console
(https://console.developers.google.com/project/927890000889/apiui/credential). I generated a 'Client ID for web application'. From the parameter of this object I was able to fill some of the parameters above
Grant type : 'authorisation_code'
Client Id : 'RANDOMCHARSam5o37nsiu730d.apps.googleusercontent.com'
Client Secret : 'RANDOMCHARSiSwBA5OH5qYLUa'
Then using Google Oauth Playground (https://developers.google.com/oauthplayground) I was able to fill the missing bits
Scope : 'https://www.googleapis.com/oauth/analytics'
Auth Server URL : 'https://accounts.google.com/o/oauth2/auth'
Access Token : 'RANDOMCHARSQAQv4HRF5-JsQEzUS61lj2YremyCocv0PQ4-agpzJe'
Refresh token : 'RANDOMCHARSLPJnL4FPaDc2KP6V8kCzjjHO2Kj4Np_3X0'
Everything works fine, I am authorised to access and I get data from Google Analytics, but just for a while, after few minutes if I retry I receive an authorisation failure error.
I believe that the problem is related to the expiration of the Access Token, but I don't know how to solve that.
Worth to mention that this activity it's batch (no human interaction), so nobody can request a new access token.
The integration framework is not extensible (I cannot write code to renew the code) so I believe there's a way to get a access token that never expire or some other mechanism to achieve the same result.
Bottom line, I am not sure if I approached the requirement correctly since the beginning (Client ID for web application).
Any help is much appreciated,
Giovanni
Access tokens typically expire after 60 minutes. If you have a refresh token you can use the refresh token to get a new (valid) access token.
This doc explains how to do that:
https://developers.google.com/accounts/docs/OAuth2WebServer#refresh
To answer your overarching question, yes, you are approaching everything correctly. All you need to do is handle the case where the access token has expired by refreshing it. Also, when you originally requested the access token the response should tell you how long it's valid for, so you should only refresh that token if it's expired.
You can use Refresh tokens to make it more long used.
The Google Auth server issued Refresh tokens never expire,
A token might stop working for one of these reasons:
The user has revoked access.
The token has not been used for six months.
The user changed passwords and the token contains Gmail scopes.
The user account has exceeded a certain number of token requests. 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.
from: https://developers.google.com/identity/protocols/OAuth2

Got error whenever trying to get access token from service account

INFO: com.google.api.client.auth.oauth2.TokenResponseException: 400 OK
{
"error" : "invalid_grant"
}
my code is
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(httpTransport)
.setJsonFactory(js)
.setServiceAccountId(emailId)
.setServiceAccountScopes(scopes)
.setServiceAccountPrivateKeyFromP12File(new File(privateKeyPath)).build();
System.out.println("SERVICE ACCOUNT SERVLET CALLED");
credential.refreshToken();
this method is running fine but suddenly it starts to give error.
Possible reasons:
1) I was having a similar problem caused by the time on my server being incorrect. Make sure your system clock is synchronized.
2) You should reuse the access token you get after the first successful authentication. You will get an invalid_grant error if your previous token has not expired yet. Cache it somewhere so you can reuse it.
3) Make sure that you pass the access token, and not refresh token as that fails. When you run, it turns the OAUth2 refresh token into an access token and passes that to the service. If you pass the raw refresh token, this is not acceptable for API access without turning it into a short-lived access token.
4) You might have reached the refresh token limit. Certain Google APIs have this, some explanation you can find here: https://developers.google.com/compute/docs/authentication
5) You also can revoke it. Go to your Google API Console ( https://code.google.com/apis/console/ ) and revoke your Client Secret under Client ID for installed applications. Be sure to also update your code with the new Client Secret

Quickbooks API reconnect issue

I've been implementing connectivity to Quickbooks via the v3 API with ColdFusion, and have most everything working except reconnect (https://appcenter.intuit.com/api/v1/connection/reconnect). I can get customer info, create/update invoices, etc, but no luck with reconnect.
From my app I get an Error Code 22 (Authentication required), but I'm sending the same auth header that I send for any of the other API call to access a protected resource. The oauth spec does not specifically have a "reconnect" action so my question is what specific oauth properties need to be included in the auth header for the Quickbooks reconnect call?
From the oauth 1.0 spec is it the headers for Consumer Requests an Access Token?
oauth_consumer_key
oauth_token
oauth_signature_method
oauth_signature
oauth_timestamp
oauth_nonce
oauth_version
oauth_verifier
Or is it the header sent for accessing a protected resource
oauth_consumer_key
oauth_token
oauth_signature_method
oauth_signature:
oauth_timestamp
oauth_nonce
oauth_version
Or, is it some other header set?
Also, I've tried using the Dev Playgorund to test reconnect, and from there I get a
24 - Invalid App Token
So I'm at a loss at this point. For what it's worth Disconnect works fine :)
Any help, guidance, suggestions would be appreciated.
Thanks
The first issue if you are doing things in the order you specify is that you cannot disconnect first and then subsequently reconnect. The reconnect API must be called with a valid access token, as the current access token will be invalidated and a new access token will be issued.
Secondly, there is a window for calling this API to refresh the 6 month life of the access token, it must be called in the last 30 days. This allows your app to refresh the token without prompting the customer again.
However in all cases if you disconnect, or the customer manually disconnects you cannot call the reconnect API. You need to take them through the authorize flow again.
hope that helps
Jarred
To test reconnect from developer playground, use the following steps-
1. Navigate to IPP Playground- Go to Manage My Apps->Click on your app
Fill in consumer key and consumer secret in links below.
Prod: https://appcenter.intuit.com/Playground/OAuth/IA/?ck=<>&cs=<>
Stage: https://appcenter-stage.intuit.com/Playground/OAuth/IA/?ck=<>&cs=<>
Alternatively, you can navigate to the Manage page for your app on stage or prod and click ‘Test connect to app (OAuth)’. Screen shot attached.
2. Enter the duration you would like for the issued OAuth tokens (e.g., 3600 for successful Reconnect) in the ‘Access Token Duration’ field. Screen shot attached.
3. Click on the Connect to QuickBooks button, go through OAuth flow to authorize a connection to a realm.
4. Under the resulting Post-Connection Interactions heading, click ‘Reconnect API Test’. Screen shot attached.
5. A new page will launch where your OAuth tokens are displayed. Copy these values to your application to test Reconnect
As Jarred mentioned that for dev/live apps, there is a window for calling this API to refresh the 6 month life of the access token, it must be called in the last 30 days.
I received this error as well. Error Code 22 (Authentication required) for me meant that the OAuth signature was wrong. This was confusing because I couldn't find this error listed in the Quickbooks documents for reconnect.
I was signing the request as a "POST" request instead of a "GET" request which is what Quickbooks requires for calls to the reconnect endpoint.