无法刷新访问令牌:响应为“未经授权的客户端”

27

在尝试刷新访问令牌时,我遇到了一个错误:

400 Bad Request

{error : "unauthorized_client"}

来自Google令牌URI:

{
  "error" : "invalid_request"
}

我阅读了这个回答(链接)和官方的Google文档(描述了POST请求应该如何看起来),但我没有看到任何区别。

我捕获了我的POST请求(已删除机密信息):

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代码:

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();

有什么问题吗?


你找到答案了吗?还是遇到同样的错误?(http://stackoverflow.com/questions/13878605/authorize-google-calendar-api-works-on-personal-account-but-not-on-google-domain) - Stephan Celis
我不需要冒充用户,我只发送客户端和密钥ID以及刷新令牌。这是不同的情况。 - Martin V.
4个回答

45

问题说明

在 @MartinV 给出的提示下,我终于把它修复了!因为他的答案没有很好地解释如何解决,所以我要在这里发布它。

问题是因为我们都使用了Google的OAuth Playground生成了刷新令牌,但是当你在第一步中点击“授权API”时,它会使用Playground应用程序将您带到同意屏幕。之后,您创建的所有令牌只能由Playground应用程序使用,但是您当然不知道该应用程序的客户端ID或客户端密钥。

解决方案

解决方案是使Playground使用您自己的客户端ID和密钥。要这样做,请单击设置按钮:

Playground settings

输入您的客户端ID和密钥。但是,在您执行此操作之前,如其所述,您需要进入 开发者中心,找到您的OAuth 2.0客户端ID客户端,编辑它并在授权重定向URI下添加 https://developers.google.com/oauthplayground 。添加完成并保存更改后,返回Playground并尝试授权API。在我的情况下,在授权重定向URI中的更改生效之前大约需要15分钟。

完成后,请不要忘记从开发人员控制台中删除Playground URI!

附加信息

一旦我完成了这些步骤,在Python中我做了以下操作,并且它起作用了:

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)

我按照您的所有步骤进行了操作,但在整个过程完成后,refresh_token为空,而我在oauthplayground中获得了访问令牌。 - Bhavin
我不确定我理解了。在 Playground 中,您需要点击“交换授权代码以获取令牌”来获取刷新令牌。 - Diego Jancic
我尝试在本地主机上进行测试,并将我的重定向URI设置为playground,但是在我的本地服务器上出现了“redirect_uri_mismatch”的错误。 - AnD
4
天哪!谢谢你!!!……在查看了大量有关该主题的 StackOverflow 帖子、查看了多个关于该主题的教程并且挠头一周以上试图弄清楚为什么我的程序性请求刷新令牌失败之后,这似乎已经解决了问题。我同意 @user1701153 的观点,即 Google 需要更好的文档...真的太可怕了!噫! - user2101068
3
哦,天啊!我为这个问题奋斗了好几天,直到我找到了这篇文章。谢谢你,这应该是被接受的答案!!!@Google请更新你的文档。这不是正常的! - Valor_
显示剩余5条评论

15

我在OAuth2 playground中创建了访问令牌和刷新令牌,然后将它们复制到我的应用程序中。 不允许为授权和令牌刷新使用不同的客户端。


3
不允许使用不同的客户端进行授权和令牌刷新。马丁,你能否再解释一下?我遇到了类似的问题。谢谢。 - Neo
1
嗨,马丁,你能告诉我你是怎么解决这个错误的吗?我也遇到了同样的问题。 - Sunil Silumala
请定义客户端是什么...IP地址、用户代理,它是如何知道的? - Ryan Williams
13
当您使用由[clientid,screretid]生成的刷新令牌进行授权时,如果与用于授权过程的[clientid,screredid]不同,则会出现此问题。 - Trong Dinh

1
使用REST API获取access_token,然后在创建refresh_token上文所述后使用它与REST API交互(例如将视频添加到私人播放列表)的另一种解决方案。保留html标签。
import requests
import json

# according to  https://dev59.com/VWYr5IYBdhLWcg3wM3j6#41556775
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)

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接