27

I am getting an error when I try to refresh access token:

400 Bad Request

{error : "unauthorized_client"}

From the Google token URI:

{
  "error" : "invalid_request"
}

I read this answer here and the official Google documentation (which describes how a POST request should look) and I don't see any difference.

I captured my POST request (secrets removed):

POST /SHOWMERAWPOST HTTP/1.1
User-Agent: Google-HTTP-Java-Client/1.10.3-beta (gzip)
Pragma: no-cache
Host: requestb.in
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 175
Connection: keep-alive
Cache-Control: no-cache
Accept-Encoding: gzip
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2

grant_type=refresh_token&refresh_token=******&client_id=*******.apps.googleusercontent.com&client_secret=******

Java code which sends the request:

RefreshTokenRequest req = new RefreshTokenRequest(new NetHttpTransport(), new JacksonFactory(), new GenericUrl(
                    getSecrets().getDetails().getTokenUri()), REFRESH_TOKEN);

           req.set("client_id", getSecrets().getDetails().getClientId());
           req.set("client_secret", getSecrets().getDetails().getClientSecret());

           TokenResponse response = req.execute();

Is there anything wrong?

Community
  • 1
  • 1
Martin V.
  • 3,560
  • 6
  • 31
  • 47
  • did u find the answer, getting the same error (http://stackoverflow.com/questions/13878605/authorize-google-calendar-api-works-on-personal-account-but-not-on-google-domain) – Stephan Celis Dec 14 '12 at 12:27
  • I do not need to impersonate user, i am sending only client and secret id with refresh token. That`s different situation. – Martin V. Dec 16 '12 at 21:02

4 Answers4

45

PROBLEM EXPLANATION

With the hint @MartinV gave I was finally able to fix it! Because his answer doesn't explain very well how to solve it, I'm going to post it here.

The problem is because we all have generated the Refresh Token using Google's OAuth Playground, but when you click 'Authorize APIs' in the first step, it takes you to the concent screen using the Playground app. After that, all the tokens that you create can be used only by the Playground app, but of course you don't know either the Client ID or the Client Secret for that app.

SOLUTION

The solution is to make Playground to use your own Client ID and Secret. To do so, click on the Settings button:

Playground settings

And enter your Client ID and Secret. But, before you do that, as it says there, you need to go to the Developer's Console, find your OAuth 2.0 client IDs client, edit it and add https://developers.google.com/oauthplayground under Authorized redirect URIs. After you added that and saved the changes, go back to the playground and try to Authorize APIs. In my case it took like 15 minutes before the changes in the Authorized redirect URIs took effect.

Once you're done, don't forget to remove the Playground URI from the Developer Console!

EXTRA

Once I have done that, in Python I did this and it worked:

access_token = None 
client_id = 'xxxxxxxx.apps.googleusercontent.com'
client_secret = 'xxxxxxxxxxxx'
refresh_token = 'xxxxxxxxxxxx'
token_expiry = None
token_uri = "https://accounts.google.com/o/oauth2/token"
user_agent = 'YourAgent/1.0'

credentials = client.GoogleCredentials(access_token, client_id, client_secret, refresh_token, token_expiry, token_uri, user_agent)

http = credentials.authorize(httplib2.Http())
credentials.refresh(http)

service = build('drive', 'v3', http=http)
req = service.files().list()
resp = req.execute(http=http)
Diego Jancic
  • 7,280
  • 7
  • 52
  • 80
  • I have followed all your steps but after completion of whole process refresh_token is blank while I got access token in oauthplayground. – Bhavin May 19 '17 at 11:15
  • Not sure I understand. In Playground, you need to click _Exchange authorization code for tokens_ to get the Refresh Token. – Diego Jancic May 23 '17 at 18:14
  • i tried to test on localhost, and set my redirect uri to playground but i got: "redirect_uri_mismatch" in my localhost server – AnD Jul 06 '17 at 15:23
  • Not sure if it's using just the redirect parameter or the _referrer_. I don't remember. In any case, did you way a few minutes after adding the playground URL under in the Dev's console? – Diego Jancic Jul 06 '17 at 17:39
  • This answer should have been the correct thorough answer - you literally saved me! haha – Raven Sep 11 '17 at 17:08
  • Thank you! Google needs to clean up their act and put this info in their docs. – Captain Kirk Feb 07 '18 at 07:45
  • I am sure your answer solved the problem extremely difficult,I went to find it a long time.I love you @Diego Jancic – Touya Akira Sep 27 '18 at 01:41
  • 4
    OMG! THANK YOU!!!!....after reviewing tons of StackOverflow postings on the topic, reviewing multiple tutorials on the general topic and pulling out my hair for a week+ trying to figure out why my programmatic request to refresh tokens failed...THIS appears to have solved the problem. I agree with @user1701153 that Google needs better documentation...it's really horrific! Argh! – user2101068 Feb 15 '19 at 21:23
  • I agree with @Raven this should have been the CORRECT answer! – user2101068 Feb 15 '19 at 21:30
  • 3
    Oh Jesus! I was fighting with this problem for several days, until I found this post. Thank you man, this should be accepted answer!!! @google please update your docs. This is not normal! – Valor_ Jun 19 '19 at 16:33
15

I created access and refresh token in OAuth2 playground and then i copied them to my app. It`s not allowed to have different clients for autorization and for token refresh.

Martin V.
  • 3,560
  • 6
  • 31
  • 47
  • 3
    It`s not allowed to have different clients for autorization and for token refresh >> Martin can you explain it a bit more.. I am facing similar issue.. thank you. – Neo Jul 10 '13 at 12:16
  • 1
    Hi Martin can you please let me know what you did to solve this error. i am facing same problem. – Sunil Silumala Aug 14 '13 at 07:32
  • Please define what a client is... IP Address, User Agent, how does it know? – Ryan Williams Nov 28 '13 at 23:38
  • 13
    this problem happens when u are using the refresh token is generated by [clientid,screretid] which is different from [clientid,screredid] is used for authorizing process. – Trong Dinh Jan 24 '14 at 07:02
1

Another solution using the REST API to get an access_token and then use it to interact with the REST API (e.g. add a video to a private playlist) after creating the refresh_token as described above.

import requests
import json

# according to  https://stackoverflow.com/a/41556775/3774227
client_id = '<client_id>'
client_secret = '<client_secret>'
refresh_token = '<refresh_token>'

playlist_id = '<playlist>'
video_id = 'M7FIvfx5J10'


def get_access_token(client_id, client_secret, refresh_token):

    url = 'https://www.googleapis.com/oauth2/v4/token'

    data = {
        'client_id': client_id,
        'client_secret': client_secret,
        'refresh_token': refresh_token,
        'grant_type': 'refresh_token'
    }

    response = requests.post(
        url=url,
        data=data,
    )

    return response.json().get('access_token')


def add_video_to_playlist(playlist_id, video_id, access_token):

    url = 'https://www.googleapis.com/youtube/v3/playlistItems'

    params = {
        'part': 'snippet',
    }

    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer {}'.format(access_token)
    }

    data = {
        'snippet': {
            'playlistId': playlist_id,
            'resourceId': {
                'kind': 'youtube#video',
                'videoId': video_id
            },
        }
    }

    requests.post(
        url=url,
        params=params,
        headers=headers,
        data=json.dumps(data)
    )


if __name__ == '__main__':
    access_token = get_access_token(client_id, client_secret, refresh_token)
    add_video_to_playlist(playlist_id, video_id, access_token)
Livioso
  • 1,242
  • 1
  • 11
  • 21
0

I had the same problem. The solution was to use the same client when authorizing in the application and when updating the token on the server.

Can't refresh access token for Google Calendar API on server side