我想从谷歌获取访问令牌。谷歌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,你需要在OAuth请求URL中包含access_type=offline。当一个用户第一次验证时,你会得到一个非空的refresh_token以及一个过期的access_token。

如果遇到这样的情况,用户可能会重新验证您已经拥有身份验证令牌的帐户(如上面提到的@SsjCosty),则需要从谷歌获取令牌用于哪个帐户的信息。要做到这一点,请将概要文件添加到作用域。使用OAuth2 Ruby宝石,你的最终请求可能看起来像这样:

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"
)

注意,作用域有两个空格分隔的条目,一个用于对谷歌Analytics的只读访问,另一个仅用于配置文件,这是OpenID Connect标准。

这将导致谷歌在get_token响应中提供一个名为id_token的附加属性。要从id_token中获取信息,请查看谷歌文档中的此页面。有一些google提供的库可以为您验证和“解码”这些代码(我使用了Ruby google-id-token gem)。解析后,子参数实际上是唯一的谷歌帐户ID。

值得注意的是,如果您更改了作用域,对于已经使用原始作用域进行身份验证的用户,您将再次获得一个刷新令牌。这很有用,如果你已经有一群用户,不想让他们都在谷歌中取消应用程序的认证。

哦,最后一点注意:您不需要prompt=select_account,但如果您的用户可能想要使用多个谷歌帐户进行身份验证(即,您不使用此帐户进行登录/身份验证),则它很有用。

其他回答

这给我带来了一些困惑,所以我想分享一下我艰难的经历:

当您使用access_type=offline和approval_prompt=force参数请求访问时,您应该收到一个访问令牌和一个刷新令牌。访问令牌在收到后不久就会过期,您需要刷新它。

您正确地发出了获取新访问令牌的请求,并收到了具有新访问令牌的响应。我还对没有获得新的刷新令牌感到困惑。然而,这就是它的意义所在,因为您可以反复使用相同的刷新令牌。

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

为了获得refresh_token,你需要在OAuth请求URL中包含access_type=offline。当一个用户第一次验证时,你会得到一个非空的refresh_token以及一个过期的access_token。

如果遇到这样的情况,用户可能会重新验证您已经拥有身份验证令牌的帐户(如上面提到的@SsjCosty),则需要从谷歌获取令牌用于哪个帐户的信息。要做到这一点,请将概要文件添加到作用域。使用OAuth2 Ruby宝石,你的最终请求可能看起来像这样:

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"
)

注意,作用域有两个空格分隔的条目,一个用于对谷歌Analytics的只读访问,另一个仅用于配置文件,这是OpenID Connect标准。

这将导致谷歌在get_token响应中提供一个名为id_token的附加属性。要从id_token中获取信息,请查看谷歌文档中的此页面。有一些google提供的库可以为您验证和“解码”这些代码(我使用了Ruby google-id-token gem)。解析后,子参数实际上是唯一的谷歌帐户ID。

值得注意的是,如果您更改了作用域,对于已经使用原始作用域进行身份验证的用户,您将再次获得一个刷新令牌。这很有用,如果你已经有一群用户,不想让他们都在谷歌中取消应用程序的认证。

哦,最后一点注意:您不需要prompt=select_account,但如果您的用户可能想要使用多个谷歌帐户进行身份验证(即,您不使用此帐户进行登录/身份验证),则它很有用。

我找了一个漫长的夜晚,这个很管用:

修改了admin-sdk中的user-example.php

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

然后在重定向url处获得代码 用代码进行身份验证并获得刷新令牌

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

你现在应该把它存储;)

当你的accesskey超时就做

$client->refreshToken($theRefreshTokenYouHadStored);
    #!/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

要使用postman获取刷新令牌,下面是一个配置示例

预期响应