未收到Google OAuth刷新令牌

我想从Google获取访问令牌。 Google API说要获取访问令牌,将代码和其他参数发送到令牌生成页面,并且响应将是一个JSON对象,如:

{ "access_token" : "ya29.AHES6ZTtm7SuokEB-RGtbBty9IIlNiP9-eNMMQKtXdMP3sfjL1Fc", "token_type" : "Bearer", "expires_in" : 3600, "refresh_token" : "1/HKSmLFXzqP0leUihZp2xUt3-5wkU7Gmu2Os_eBnzw74" } 

但是,我没有收到刷新令牌。 我的这个回应是:

 { "access_token" : "ya29.sddsdsdsdsds_h9v_nF0IR7XcwDK8XFB2EbvtxmgvB-4oZ8oU", "token_type" : "Bearer", "expires_in" : 3600 } 

refresh_token只在来自用户的第一个授权上提供。 后续授权(例如您在testingOAuth2集成时所做的种类)将不会再返回refresh_token 。 🙂

  1. 转到您的帐户安全设置: https : //www.google.com/settings/u/1/security 。
  2. 点击“授权应用程序和网站”旁边的修改button。
  3. 然后点击应用旁边的“撤销访问”。
  4. 您所做的下一个OAuth2请求将返回一个refresh_token

或者,您可以将查询参数prompt=consent添加到OAuthredirect(请参阅Google的OAuth 2.0 for Web服务器应用程序页面)。

这将提示用户再次授权应用程序,并将始终返回一个refresh_token

为了获得刷新令牌,您必须同时添加approval_prompt=forceaccess_type="offline"如果您使用的是由Google提供的Java客户端,它将如下所示:

 GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder( HTTP_TRANSPORT, JSON_FACTORY, getClientSecrets(), scopes) .build(); AuthorizationCodeRequestUrl authorizationUrl = flow.newAuthorizationUrl().setRedirectUri(callBackUrl) .setApprovalPrompt("force") .setAccessType("offline"); 

我search了一个漫长的夜晚,这是在伎俩:

从admin-sdk修改user-example.php

 $client->setAccessType('offline'); $client->setApprovalPrompt('force'); $authUrl = $client->createAuthUrl(); echo "<a class='login' href='" . $authUrl . "'>Connect Me!</a>"; 

那么你得到的代码在redirecturl和authentication的代码,并获得刷新令牌

 $client()->authenticate($_GET['code']); echo $client()->getRefreshToken(); 

你应该现在存储;)

当您的访问键超时只是做

 $client->refreshToken($theRefreshTokenYouHadStored); 

这引起了我一些困惑,所以我想分享一下我刚刚学习的东西:

当您使用access_type=offlineapproval_prompt=force参数请求访问时,您应该同时接收访问令牌和刷新令牌。 访问令牌收到后很快就会过期,您需要刷新它。

您正确地请求获得一个新的访问令牌,并收到具有您的新的访问令牌的响应。 我也感到困惑的是,我没有得到一个新的更新令牌。 但是,这是它的意思,因为你可以一次又一次地使用相同的刷新标记。

我认为其他的一些答案假设你想得到一个新的刷新令牌出于某种原因,并build议你重新授权用户,但实际上,你不需要,因为你有刷新令牌将工作,直到被用户撤销。

富萨顿的答案终于为我工作,我意识到添加access_type=offline是在前端客户端的授权码请求, 而不是后端请求交换代码的access_token完成。 我已经在Google上添加了对他的回答和链接的评论,以获取有关令人耳目一新的令牌的更多信息。

PS如果您使用的是Satellizer, 那么可以将这个选项添加到AngularJS的$ authProvider.google中 。

设置这将导致刷新标记每次发送:

 $client->setApprovalPrompt('force'); 

下面给出一个例子(php):

 $client = new Google_Client(); $client->setClientId($client_id); $client->setClientSecret($client_secret); $client->setRedirectUri($redirect_uri); $client->addScope("email"); $client->addScope("profile"); $client->setAccessType('offline'); $client->setApprovalPrompt('force'); 

对我来说,我尝试了CalendarSampleServlet提供的CalendarSampleServlet 。 1小时后access_key超时,并有一个redirect到401页面。 我尝试了所有上述选项,但他们没有工作。 最后在检查“AbstractAuthorizationCodeServlet”的源代码时,我可以看到如果证书存在,redirect将被禁用,但理想情况下,它应该检查refresh token!=null 。 我将下面的代码添加到CalendarSampleServlet ,并在此之后。 经过这么多小时的挫折之后,我感到非常欣慰 感谢上帝。

 if (credential.getRefreshToken() == null) { AuthorizationCodeRequestUrl authorizationUrl = authFlow.newAuthorizationUrl(); authorizationUrl.setRedirectUri(getRedirectUri(req)); onAuthorization(req, resp, authorizationUrl); credential = null; } 

现在谷歌已经拒绝了我的请求(access_type,提示)中的这些参数… :(并没有“撤销访问”button在所有。我很郁闷,因为回到我的refresh_token大声笑

更新:我在这里find了答案:D您可以通过请求https://developers.google.com/identity/protocols/OAuth2WebServer取回刷新标记;

curl -H“内容types:application / x-www-form-urlencoded”\ https://accounts.google.com/o/oauth2/revoke?token= {token}

令牌可以是访问令牌或刷新令牌。 如果令牌是访问令牌并且具有相应的刷新令牌,则刷新令牌也将被撤销。

如果吊销被成功处理,则响应的状态码是200.对于错误条件,返回状态码400和错误码。

  #!/usr/bin/env perl use strict; use warnings; use 5.010_000; use utf8; binmode STDOUT, ":encoding(utf8)"; use Text::CSV_XS; use FindBin; use lib $FindBin::Bin . '/../lib'; use Net::Google::Spreadsheets::V4; use Net::Google::DataAPI::Auth::OAuth2; use lib 'lib'; use Term::Prompt; use Net::Google::DataAPI::Auth::OAuth2; use Net::Google::Spreadsheets; use Data::Printer ; my $oauth2 = Net::Google::DataAPI::Auth::OAuth2->new( client_id => $ENV{CLIENT_ID}, client_secret => $ENV{CLIENT_SECRET}, scope => ['https://www.googleapis.com/auth/spreadsheets'], ); my $url = $oauth2->authorize_url(); # system("open '$url'"); print "go to the following url with your browser \n" ; print "$url\n" ; my $code = prompt('x', 'paste code: ', '', ''); my $objToken = $oauth2->get_access_token($code); my $refresh_token = $objToken->refresh_token() ; print "my refresh token is : \n" ; # debug p($refresh_token ) ; p ( $objToken ) ; my $gs = Net::Google::Spreadsheets::V4->new( client_id => $ENV{CLIENT_ID} , client_secret => $ENV{CLIENT_SECRET} , refresh_token => $refresh_token , spreadsheet_id => '1hGNULaWpYwtnMDDPPkZT73zLGDUgv5blwJtK7hAiVIU' ); my($content, $res); my $title = 'My foobar sheet'; my $sheet = $gs->get_sheet(title => $title); # create a sheet if does not exit unless ($sheet) { ($content, $res) = $gs->request( POST => ':batchUpdate', { requests => [ { addSheet => { properties => { title => $title, index => 0, }, }, }, ], }, ); $sheet = $content->{replies}[0]{addSheet}; } my $sheet_prop = $sheet->{properties}; # clear all cells $gs->clear_sheet(sheet_id => $sheet_prop->{sheetId}); # import data my @requests = (); my $idx = 0; my @rows = ( [qw(name age favorite)], # header [qw(tarou 31 curry)], [qw(jirou 18 gyoza)], [qw(saburou 27 ramen)], ); for my $row (@rows) { push @requests, { pasteData => { coordinate => { sheetId => $sheet_prop->{sheetId}, rowIndex => $idx++, columnIndex => 0, }, data => $gs->to_csv(@$row), type => 'PASTE_NORMAL', delimiter => ',', }, }; } # format a header row push @requests, { repeatCell => { range => { sheetId => $sheet_prop->{sheetId}, startRowIndex => 0, endRowIndex => 1, }, cell => { userEnteredFormat => { backgroundColor => { red => 0.0, green => 0.0, blue => 0.0, }, horizontalAlignment => 'CENTER', textFormat => { foregroundColor => { red => 1.0, green => 1.0, blue => 1.0 }, bold => \1, }, }, }, fields => 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment)', }, }; ($content, $res) = $gs->request( POST => ':batchUpdate', { requests => \@requests, }, ); exit; #Google Sheets API, v4 # Scopes # https://www.googleapis.com/auth/drive View and manage the files in your Google D# # i# rive # https://www.googleapis.com/auth/drive.file View and manage Google Drive files and folders that you have opened or created with this app # https://www.googleapis.com/auth/drive.readonly View the files in your Google Drive # https://www.googleapis.com/auth/spreadsheets View and manage your spreadsheets in Google Drive # https://www.googleapis.com/auth/spreadsheets.readonly View your Google Spreadsheets 

为了获得refresh_token您需要在OAuth请求url中包含access_type=offline 。 当用户第一次validation时,你将得到一个非零的refresh_token以及一个到期的access_token

如果您有一种情况,用户可能会重新validation一个帐户,您已经有一个身份validation令牌(如上面提到的@SsjCosty),您需要从Google获取有关该令牌所用帐户的信息。 为此,请将profile添加到您的范围。 使用OAuth2 Ruby gem,您的最终请求可能如下所示:

 client = OAuth2::Client.new( ENV["GOOGLE_CLIENT_ID"], ENV["GOOGLE_CLIENT_SECRET"], authorize_url: "https://accounts.google.com/o/oauth2/auth", token_url: "https://accounts.google.com/o/oauth2/token" ) # Configure authorization url client.authorize_url( scope: "https://www.googleapis.com/auth/analytics.readonly profile", redirect_uri: callback_url, access_type: "offline", prompt: "select_account" ) 

请注意,作用域有两个空格分隔的条目,一个用于Google Analytics(分析)的只读访问权限,另一个只是OpenID Connect标准的profile

这将导致Google在get_token响应中提供一个名为id_token的附加属性。 要从id_token中获取信息, 请在Google文档中查看此页面 。 有一些Google提供的库可以为你validation和“解码”(我使用Ruby google-id-token gem )。 一旦你得到它的分析,这个sub参数实际上是唯一的Google帐户ID。

值得注意的是,如果您更改范围,则会再次为已经对原始范围进行身份validation的用户取回刷新令牌。 如果您已经有一大堆用户,并且不想让他们全部在Google中取消应用,那么这非常有用。

哦,还有一个最后的注意事项:你不需要 prompt=select_account ,但是如果你的用户可能想要使用多个Google帐户进行身份validation,这很有用(也就是说,你没有使用它来login/身份validation)。