OAuth 2.0 400 - error:invalid_grant and ideas?

51,708 views
Skip to first unread message

Ian Ibbotson

unread,
Mar 31, 2012, 4:30:03 AM3/31/12
to google-analytics...@googlegroups.com
Hi All.

Apologies if I'm asking a dumb question, did search around and I struggled to find anything that matched. Please feel free to tell me to go search harder if I'm just being dumb....

I have a server app that needs to collect it's own analytics data from the API, and had planned to use the service-accounts scenario to collect the data... At first I tried to use the google libraries, but they seem to come with a world of dependency hell that I'd like to avoid if at all possible. Aside from that, I thought it would be good to try and understand the process from the ground up. 

I've had some limited success getting the JWT request together and signing it.. I *think* everything is as it should be, but when I make my first call the server always returns 400 : error:invalid_grant

I've tried generating a new key, but nothing seems to help, and I'm a bit stuck to know whats going wrong and why.

I've attached a .groovy test that demonstrates the problem if anyone is sufficiently motivated to take a look. To run it, you'll need to generate a new service account from the api console, download the key and then update the test file with your client_id and path to the downloaded .p12 file.

The test script should grab all the dependencies it needs. Hopefully someone out there will take a look and spot that I've just done something really dumb.

Thanks for any assistance!

Ian.
test.groovy

Ian Ibbotson

unread,
Mar 31, 2012, 4:45:44 AM3/31/12
to google-analytics-api - GA Data Export API
Typically...

After fighting with this for 14 odd hours, and finally posting here...
I go and fix it in 3 mins!

I thought it was just worth trying the email address from the API
console instead of the client-Id.. Hey presto, 200OK.

Hopefully this will help someone else. Google friends, can I make a
suggestion that https://developers.google.com/accounts/docs/OAuth2ServiceAccount
could use a small edit in under the "Forming the JWT Claim Set"
heading, "Required Claims" section. Specifically, the table listing
the parameters reads "iss - the client_id of the application making
the access token request". I think it should really read "the Email
address as taken from the API Console service account setting".. I'm
pretty sure it says to send client_id in a few other places, but that
page seems to be the authoritative one, and is linked from the API
console. Making it a bit clearer would have saved me a bit of pain.

Thanks all!
Ian.
>  test.groovy
> 4KViewDownload

doucol

unread,
Mar 31, 2012, 5:11:14 AM3/31/12
to google-analytics...@googlegroups.com
Soooo funny.  I just spent the last two days on the same exact problem.  Yeah, very frustrating.

However, now all I get back is a 403 forbidden from the actual analytics service request.  :(

ian...@gmail.com

unread,
Mar 31, 2012, 5:19:24 AM3/31/12
to google-analytics...@googlegroups.com
heh looking forward to that pain then :)

Please drop me a line if you sort it!

Cheers,
Ian.

--
You received this message because you are subscribed to the Google Groups "GA Data Export API" group.
To view this discussion on the web visit https://groups.google.com/d/msg/google-analytics-data-export-api/-/rPZ3LGZYUD8J.

To post to this group, send email to google-analytics...@googlegroups.com.
To unsubscribe from this group, send email to google-analytics-data-...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-analytics-data-export-api?hl=en.



--
Ian Ibbotson
W: http://ianibbo.me
E: ian...@gmail.com
skype: ianibbo
twitter: ianibbo

Ian Ibbotson

unread,
Mar 31, 2012, 1:05:50 PM3/31/12
to google-analytics-api - GA Data Export API
Yep.. same boat here and stuck again.. only getting 403 responses :(

doucol

unread,
Mar 31, 2012, 1:33:43 PM3/31/12
to google-analytics...@googlegroups.com
I suppose it's not just me then.  *please* send me a note if you figure it out - I will do the same.

Also, I would be happy to use the "simple api access" method if I could find some docs on how to use that.  Are you familiar with that approach?

Jeetendra Soneja

unread,
Mar 31, 2012, 4:21:46 PM3/31/12
to google-analytics-api - GA Data Export API
Hi,

Can you post the detailed error response that you are getting ? 403
forbidden generally means that you don't have access to the profile
that you're requesting the data for.

Jeetendra.

doucol

unread,
Mar 31, 2012, 4:39:07 PM3/31/12
to google-analytics...@googlegroups.com
Here is the response body that I get back:

"{"error":{"errors":[{"message":"Forbidden"}],"code":403,"message":"Forbidden"}}"

* Successful OAuth2 token request - I get back an http status 200 with <access_token>
  -- with http header:  "Authorization:  Bearer <access_token>"
* Receive a 403 forbidden

I can look at the GA reports from the web interface for this profile and I believe I am an admin for this profile.

Also, I have run through the google php api client and I get the same response back using that.

Any ideas?
-doug

doucol

unread,
Mar 31, 2012, 8:05:30 PM3/31/12
to google-analytics...@googlegroups.com
Hi Ian,

Just received a note from Nick - check his reply in my thread.

Bottom line from him is that the analytics service does not yet support OAuth "Server to Server" interactions.  He pointed towards a blog post which lists the services which support this type of interaction


Also note the suggestion in his response. 

good luck
-doug

doucol

unread,
Apr 1, 2012, 7:18:50 PM4/1/12
to google-analytics...@googlegroups.com
Ian,

I was able to get it all working.  First, you have to abandon any idea of getting a "server to server" authentication setup working against the analytics service - it doesn't support it (yet).  And, yes, it's very frustrating because everything I read lead me to believe that we should be using that - at least for my situation.  And, *nothing* I read ever gave me a sign that perhaps the analytics service did not support this authentication model yet.  <sigh>

Ok, so here is how I did it:
  • Go to Google APIs Console and make sure you have a "Client ID for web applications"
  • If you don't have that type of client ID, click "Create another Client ID..." button and create one
  • Go to OAuth 2.0 Playground  https://code.google.com/oauthplayground/
  • Click on the settings button in the top-right.
  • Click on "Use your own OAuth credentials"
  • Enter your OAuth client ID and client secret and close it
  • Go to step 1 and select "Analytics" and click "Authorize APIs"
  • Go through consent page
  • Go to step 2 and click "Exchange authorization code for tokens"
  • You should get an access token and a refresh token
  • Save this refresh token to a secure file which your groovy code can read 
  • Click on the "Refresh access token" button
  • Now you should see the POST on the right which gets a new access token using a refresh token
  • Write your groovy code which can perform that POST using the refresh token you stored in the file
  • You will get back an access code which you can use to perform queries against the analytics API
  • When it expires, you will need to obtain a new access code using the same technique
Here is the downside to this approach - first, this refresh token, and hence the queries, are now tied to the actual google user who gave the consent in the OAuth 2.0 playground.  secondly, I'm not sure about this, but I *think* that if you authorized again and got a new refresh token, the original refresh token might not work.  I would need to test this to be sure.

This worked for me and I hope it works for you. If / when the 'server to server' authentication is turned for analytics, I will switch over to that.

Let me know how it goes.

Nick

unread,
Apr 6, 2012, 2:49:03 AM4/6/12
to google-analytics...@googlegroups.com
Hi,

We're looking into supporting this.

In the mean time can you help me understand the use case on why you need 'server to server' authentication?

It seems like if you have GA today, you must have signed our TOS, and used our UI once. So how is going through the server side OAuth 2 flow once, (and storing / using refresh) tokens a less appealing option to access data though our API?

-Nick

Ian Ibbotson

unread,
Apr 6, 2012, 5:12:58 AM4/6/12
to google-analytics...@googlegroups.com
Hiya Nick, thanks for asking...

To be (brutally) honest, I think I've not explained myself very well..

the data API is completely fine for me.. it's just that the path I had taken through the documentation paints a very clear picture that older authentication mechanisms will be deprecated (In the very near future is the impression given). As this was starting out a new project, it seemed to only make sense to code it to the suggested api rather than the legacy one. If the old api isn't going away, I'll very happily re-code to use that. The docs certainly didn't make it obvious that OAuth2 isn't yet supported.

FWIW tho, I strongly believe that the documentation path devs are led through (from the sign-up / API screens) give the impression that OAuth2 is the only legitimate choice. So I'd suggest it's more a documentation bug than a feature request really.

Cheers, for following up, and many thanks to everyone else who has posted on this. I'll be having a go this weekend and will report back, but looks like it's sorted for now!

Ian.

Ian Ibbotson

unread,
Apr 6, 2012, 5:14:25 AM4/6/12
to google-analytics...@googlegroups.com
Wonderful!

thanks so much for this, you've saved me!

Ian.

Nick

unread,
Apr 6, 2012, 1:04:23 PM4/6/12
to google-analytics...@googlegroups.com
Hi ok,

fixing docs is easy enough.

OAuth2 is both currently supported and our best practice to handle authorization.

Though notice there are 6 OAuth 2 flows documented: https://developers.google.com/accounts/docs/OAuth2

My question is why you need the Service Account flow vs the Web Server flow?

Web Server works fine today. Service Account is quite new and we need to add support in our API for it.

-Nick

ian...@gmail.com

unread,
Apr 6, 2012, 1:55:38 PM4/6/12
to google-analytics...@googlegroups.com
well..

In essence, I have a server timer job that's kicked off once every 24 hours that I'd like to call into the api and get the stats from the previous day. Hence there's no browser involvement (There won't even be an attending user).. Whilst pasting the token from a previous browser session works around this, it just doesn't feel like the "right" flow to use for this case?

Cheers,
Ian.

--
You received this message because you are subscribed to the Google Groups "GA Data Export API" group.

To post to this group, send email to google-analytics...@googlegroups.com.
To unsubscribe from this group, send email to google-analytics-data-...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-analytics-data-export-api?hl=en.

doucol

unread,
Apr 8, 2012, 12:11:52 AM4/8/12
to google-analytics...@googlegroups.com
np, happy to hear it helped 

-doug

doucol

unread,
Apr 8, 2012, 12:16:50 AM4/8/12
to google-analytics...@googlegroups.com
Nick,

I have a similar use case to Ian.  We have a server job which runs periodically to get data for internal reports.

-doug

John Lancaster

unread,
Apr 19, 2012, 11:43:26 AM4/19/12
to google-analytics-api - GA Data Export API
I have the same use case.. need to pull stats periodically from GA and
process them automatically on my server. I didn't want to use the old
api because it will be deprecated eventually and this is a new project
so I want to make sure it is good for some time to come. What is the
best way to stay updated on the status of GA s2s oauth2 support?
> >>    - Go to Google APIs Console and make sure you have a "Client ID for
> >>    web applications"
> >>    - If you don't have that type of client ID, click "Create another
> >>    Client ID..." button and create one
> >>    - Go to OAuth 2.0 Playground  https://code.google.com/oauthplayground/
> >>    - Click on the settings button in the top-right.
> >>    - Click on "Use your own OAuth credentials"
> >>    - Enter your OAuth client ID and client secret and close it
> >>    - Go to step 1 and select "Analytics" and click "Authorize APIs"
> >>    - Go through consent page
> >>    - Go to step 2 and click "Exchange authorization code for tokens"
> >>    - You should get an access token and a refresh token
> >>    - Save this refresh token to a secure file which your groovy code can
> >>    read
> >>    - Click on the "Refresh access token" button
> >>    - Now you should see the POST on the right which gets a new access
> >>    token using a refresh token
> >>    - Write your groovy code which can perform that POST using the
> >>    refresh token you stored in the file
> >>    - You will get back an access code which you can use to perform
> >>    queries against the analytics API
> >>    - When it expires, you will need to obtain a new access code using

mcloh

unread,
May 27, 2012, 6:08:27 PM5/27/12
to google-analytics...@googlegroups.com
I am facing 400 code as well... this is the response content...


apiHttpRequest Object
(
    [batchHeaders:apiHttpRequest:private] => Array
        (
            [Content-Type] => application/http
            [Content-Transfer-Encoding] => binary
            [MIME-Version] => 1.0
            [Content-Length] => 
        )

    [url:protected] => https://accounts.google.com/o/oauth2/token
    [requestMethod:protected] => POST
    [requestHeaders:protected] => Array
        (
            [content-type] => application/x-www-form-urlencoded
            [content-length] => 534
        )

    [postBody:protected] => grant_type=assertion&assertion_type=http%3A%2F%2Foauth.net%2Fgrant_type%2Fjwt%2F1.0%2Fbearer&assertion=quitealongstringtopaste
    [userAgent:protected] => Google Oauth2 Sample google-api-php-client/0.5.0
    [responseHttpCode:protected] => 400
    [responseHeaders:protected] => Array
        (
            [cache-control] => no-cache, no-store, max-age=0, must-revalidate
            [pragma] => no-cache
            [expires] => Fri, 01 Jan 1990 00:00:00 GMT
            [date] => Sun, 27 May 2012 21:52:03 GMT
            [content-type] => application/json
            [x-content-type-options] => nosniff
            [x-frame-options] => SAMEORIGIN
            [x-xss-protection] => 1; mode=block
            [server] => GSE
            [transfer-encoding] => chunked
        )

    [responseBody:protected] => {
  "error" : "invalid_grant"
}
    [accessKey] => 



my script uses the google-api-php-client lib provided by google, and is quite simple...

require_once("../_includes/config/header.php"); //contains the definitions of the following constants
require_once (G_API_DIR.'/src/apiClient.php');
require_once (G_API_DIR.'/src/contrib/apiOauth2Service.php');

$client = new apiClient();
$client->setApplicationName("Google Oauth2 Sample");

// Set your cached access token. Remember to replace $_SESSION with a real database or memcached.
if (isset($_SESSION['token'])) {
 $client->setAccessToken($_SESSION['token']);
}

$key = file_get_contents(G_KEY_FILE);

$cred = new apiAssertionCredentials(
  G_SERVICE_ACCOUNT_NAME,
  array(G_USER_PROFILE, G_USER_EMAIL),
  $key
);

print '<h2>JWT:</h2><pre>' . print_r($cred->generateAssertion(), true) . '</pre>';

$client->setAssertionCredentials($cred);

$client->setClientId(G_CLIENT_ID);
$service = new apiOauth2Service($client);

$result = $service->userinfo->get();
print '<h2>Oauth2 Result:</h2><pre>' . print_r($result, true) . '</pre>';

if ($client->getAccessToken()) {
  $_SESSION['token'] = $client->getAccessToken();
}

any thoughts?

NgocLung

unread,
Jun 13, 2012, 7:01:10 AM6/13/12
to google-analytics...@googlegroups.com
Currenly, i have get

 "{"error":{"errors":[{"
message":"Forbidden"}],"code":403,"message":"Forbidden"}}"
when get data from google analytic api. I had create service account and I get access_token in Ruby successfully.
But i can't get data. It return above error.
How to get ids to attack on request:
GET https://www.googleapis.com/analytics/v3/data/ga
  ?ids=ga:12345
  &start-date=2008-10-01
  &end-date=2008-10-31
  &metrics=ga:visits,ga:bounces

Ids is gotten from profile_id in google analytics
Any ideas.

Roni

unread,
Aug 7, 2012, 6:18:03 AM8/7/12
to google-analytics...@googlegroups.com
Hi all, 

If it helps anyone, i was getting a 400 error, until i changed "redirect_uri" in the POST request to be the same uri as the one i used as the "redirect_uri" parameter when i first retrieved the authorization token.

That fixed it for me. Might also help someone else :)

Roni.

Mathieu

unread,
Aug 7, 2012, 8:31:42 AM8/7/12
to google-analytics...@googlegroups.com
Hi,

I'm facing an error 400 on the php api.
The strange thing, is that when i'm testing on my local computer it work perfectly an an other computer too, but on my server.
The only difference i've found for now is the return of openssl_pkey_get_private. wich give me something like: Resource id #34 on local but Resource id #33 on server.

Is there anyone who have faced this before?

sorry for my poor english.

Mathieu.


Mathieu

unread,
Aug 7, 2012, 10:00:27 AM8/7/12
to google-analytics...@googlegroups.com
I've print out the HttpRequest array, and they are exactly the same on both server and local computer. no package are missing or anything like that.
Everithing is going great, no errors until the response from the token server, who returned 400 invalid grant.

It's the just like the token server doesn't like my server ip adresse or something like that...

anyone have an idea of what i'm missing? 

Graeme

unread,
Aug 8, 2012, 6:34:47 PM8/8/12
to google-analytics...@googlegroups.com
Hello Nick,

We also have an identical use case, for periodically (not spamming your API!) downloading and storing GA data for further analysis.

It would be fantastic if you can support this in the GA API. :)

Graeme

MHsu

unread,
Aug 9, 2012, 4:26:53 PM8/9/12
to google-analytics...@googlegroups.com
THANK YOU TANK YOU, A THOUSAND THANK YOU!!

I've been looking at the invalid request for two days until seeing your post.

Rodrigo Alves Sarmento

unread,
Aug 10, 2012, 2:12:06 PM8/10/12
to google-analytics...@googlegroups.com
Hi Mathieu,

   I am having the same problem, when i try to connect against Google API in my local server, evrething goes fine, but when i try in my server on the web i can't do it. I have this problem a few days, did you solve the problem?

Akram

unread,
Aug 22, 2012, 12:09:37 PM8/22/12
to google-analytics...@googlegroups.com
Is the redirect_url should absolutly be a https ?

Akram

unread,
Aug 23, 2012, 12:54:30 PM8/23/12
to google-analytics...@googlegroups.com
No

Rodrigo Alves Sarmento

unread,
Aug 24, 2012, 12:26:15 PM8/24/12
to google-analytics...@googlegroups.com
PROBLEM SOLVED.

   The time of my server was one minute in the future, compared with google's server time. So i put my server to update the time with ntpdate and now i don't have problem with invalid_grant anymore.

Pol Mol

unread,
Jan 28, 2013, 9:28:00 AM1/28/13
to google-analytics...@googlegroups.com
Hi, i had been same problem when i try to make request on webmaster tool and used token from OAuth2ServiceAccount and i got 403 code error.

is Webmaster tool supported in OAuth2ServiceAccount?

Hope anyone help me with this.

Thanks

Pablo J. Novas

unread,
May 23, 2015, 2:46:36 PM5/23/15
to google-analytics...@googlegroups.com
same error invalid_grant here .. it was the server time (was 5 min in the future compared to google time).

Thanks to Rodrigo Alves Sarmento!
Reply all
Reply to author
Forward
0 new messages