Hi,
I'm running into some authentication/authorization issues. What's interesting is that this is with a client that works with many other users, and this particular issue can be recreated in a homemade client, the Google API Python bindings, and manual HTTP requests. The issue is that a seemingly valid refresh_token ends up giving 401s when making requests to the AdSense API.
Here's the flow--given a refresh token, we exchange it for an access token:
$ curl -vv -XPOST https://accounts.google.com/o/oauth2/token \
-d"client_id=869353560953.apps.googleusercontent.com" \
-d"client_secret=snip" \
-d"refresh_token=1/snip" \
-d"grant_type=refresh_token"
> POST /o/oauth2/token HTTP/1.1
> User-Agent: curl/7.37.0
> Host: accounts.google.com
> Accept: */*
> Content-Length: 173
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: Fri, 01 Jan 1990 00:00:00 GMT
< Date: Thu, 07 Aug 2014 21:24:50 GMT
< Content-Disposition: attachment; filename="json.txt"; filename*=UTF-8''json.txt
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Server: GSE
< Alternate-Protocol: 443:quic
< Transfer-Encoding: chunked
<
{
"access_token" : "ya29.snip",
"token_type" : "Bearer",
"expires_in" : 3600,
"id_token" : "snip"
}
Then see if the token is valid:
curl -vv "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=ya29.snip"
> GET /oauth2/v1/tokeninfo?access_token=ya29.snip HTTP/1.1
> User-Agent: curl/7.37.0
> Host: www.googleapis.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: Fri, 01 Jan 1990 00:00:00 GMT
< Date: Thu, 07 Aug 2014 21:26:09 GMT
< Content-Type: application/json; charset=UTF-8
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Server: GSE
< Alternate-Protocol: 443:quic
< Transfer-Encoding: chunked
<
{
"issued_to": "869353560953.apps.googleusercontent.com",
"audience": "869353560953.apps.googleusercontent.com",
"user_id": "snip",
"scope": "https://www.googleapis.com/auth/adsense.readonly https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/plus.me",
"expires_in": 3521,
"email": "em...@example.com",
"verified_email": true,
"access_type": "offline"
}
Then try to make a request with it that needs only the adsense.readonly scope:
$ curl -vv "https://www.googleapis.com/adsense/v1.3/accounts?access_token=ya29.snip"
> GET /adsense/v1.3/accounts?access_token=ya29.snip HTTP/1.1
> User-Agent: curl/7.37.0
> Host: www.googleapis.com
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< WWW-Authenticate: Bearer realm="https://accounts.google.com/AuthSubRequest", error=invalid_token
< Content-Type: application/json; charset=UTF-8
< Date: Thu, 07 Aug 2014 21:29:41 GMT
< Expires: Thu, 07 Aug 2014 21:29:41 GMT
< Cache-Control: private, max-age=0
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-XSS-Protection: 1; mode=block
< Server: GSE
< Alternate-Protocol: 443:quic
< Transfer-Encoding: chunked
<
{
"error": {
"errors": [
{
"domain": "global",
"reason": "authError",
"message": "Invalid Credentials",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Invalid Credentials"
}
}
The same is true when sending the token in the authentication header, (Authorization: Bearer ya29.snip).
Renegotiating the refresh_token yields the same results. Changing the deprecated scope '
https://www.googleapis.com/auth/userinfo.email' to its replacement 'email' also doesn't change the behavior. I have a feeling there's something about the users's account that is nonstandard but don't see anything in the documentation about this.
Has anyone seen something like this or have any ideas?