Не получает токен обновления 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
}

12 ответов


на refresh_token Это только при первой авторизации пользователя. Последующие авторизации, такие как те, которые вы делаете при тестировании интеграции OAuth2, не вернут refresh_token снова. :)

  1. перейдите на страницу с приложениями, имеющими доступ к вашей учетной записи: https://myaccount.google.com/u/0/permissions.
  2. в меню сторонних приложений выберите приложение.
  3. нажмите Удалить доступ и нажмите кнопку ОК подтвердить
  4. следующий запрос OAuth2, который вы сделаете, вернет refresh_token.

кроме того, вы можете добавить параметр запроса prompt=consent для перенаправления OAuth (см. Google OAuth 2.0 для приложений Web-сервера


чтобы получить токен обновления, вы должны добавить оба approval_prompt=force и access_type="offline" Если вы используете Java-клиент, предоставленный Google, он будет выглядеть так:

GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
            HTTP_TRANSPORT, JSON_FACTORY, getClientSecrets(), scopes)
            .build();

AuthorizationCodeRequestUrl authorizationUrl =
            flow.newAuthorizationUrl().setRedirectUri(callBackUrl)
                    .setApprovalPrompt("force")
                    .setAccessType("offline");

Я искал долгую ночь, и это делает трюк:

модифицированных пользователя-пример.php от admin-sdk

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

это вызвало у меня некоторую путаницу, поэтому я подумал, что поделюсь тем, что я пришел, чтобы узнать трудный путь:

при запросе доступа с помощью access_type=offline и approval_prompt=force параметры вы должны получить как открыть маркер и обновить маркер. The открыть токен истекает вскоре после его получения, и вам нужно будет его обновить.

вы правильно сделали запрос на получение нового открыть токен и получил ответ у этого есть ваш новый открыть маркер. Я также был смущен тем, что я не получил новый обновить маркер. Однако это так, как это должно быть, так как вы можете использовать то же самое обновить маркер снова и снова.

Я думаю, что некоторые из других ответов предположим, что вы хотите получить себе новый обновить токен по какой-то причине и предположил, что вы повторно авторизуете пользователя, но на самом деле вам это не нужно, так как обновить маркер будет работать до отмены пользователем.


ответ Рича Саттона наконец-то сработало для меня, после того, как я понял, что добавление access_type=offline делается на передний конец запрос клиента на код авторизации,не внутренний запрос, который обменивает этот код на access_token. Я добавил комментарий к его ответу и эта ссылка в Google для получения дополнительной информации об обновлении маркеров.

P. S. Если вы используете Satellizer, вот как добавить эту опцию в $authProvider.Google в в AngularJS.


установка этого приведет к тому, что токен обновления будет отправляться каждый раз:

$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 предоставлено Google. Через 1 час access_key тайм-аут, и есть перенаправление на страницу 401. Я пробовал все вышеперечисленные варианты, но они не сработали. Наконец, после проверки исходного кода 'AbstractAuthorizationCodeServlet', я мог видеть, что перенаправление будет отключено, если учетные данные присутствуют, но в идеале он должен был проверить refresh token!=null. Я добавил ниже код CalendarSampleServlet и это сработало после этого. Большое облегчение после стольких часов разочарования . слава Богу.

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

для получения refresh_token вам нужно включить access_type=offline в URL-адресе запроса OAuth. Когда пользователь аутентифицируется в первый раз, вы получите обратно не-ноль refresh_token и access_token, срок действия которого истекает.

если у вас есть ситуация, когда пользователь может повторно аутентифицировать учетную запись, для которой у вас уже есть токен аутентификации (например, @SsjCosty упоминает выше), вам нужно получить информацию от Google, для какой учетной записи предназначен токен. Для этого добавьте profile в свой масштабы. Используя рубиновый камень OAuth2, ваш окончательный запрос может выглядеть примерно так:

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, а другая просто profile, который является стандартом OpenId Connect.

это приведет к тому, что Google предоставит дополнительный атрибут под названием id_token на get_token ответ. Чтобы получить информацию из маркера, проверьте эту страницу в документах Google. Есть несколько библиотек, предоставляемых Google, которые будут проверять и" декодировать " это для вас (я использовал Ruby Google-id-token gem). Как только вы его проанализируете,sub параметр эффективно уникальный идентификатор учетной записи Google.

стоит отметить, если вы изменить область, вы получите маркер обновления снова для пользователей,которые уже прошли проверку подлинности с исходной областью. Это полезно, если, скажем, у вас уже есть куча пользователей и вы не хотите чтобы сделать их все un-auth приложение в Google.

да, и последнее замечание: вы не нужно prompt=select_account, но это полезно, если у вас есть ситуация, когда ваши пользователи могут захотеть аутентифицироваться с более чем одной учетной записью Google (т. е. вы не используете это для входа / аутентификации).


теперь google отказался от этих параметров в моем запросе (access_type, prompt)... : (и нет никакой кнопки "отозвать доступ" вообще. Я расстраиваюсь из-за возвращения моего refresh_token lol

обновление: Я нашел ответ здесь: D вы можете вернуть токен обновления по запросу https://developers.google.com/identity/protocols/OAuth2WebServer

curl-H "Content-type: 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 => ,
                         },
                    },
              },
              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

С помощью автономный доступ и запрос:согласие работал хорошо для меня:

   auth2 = gapi.auth2.init({
                    client_id: '{cliend_id}' 
   });

   auth2.grantOfflineAccess({prompt:'consent'}).then(signInCallback); 

мое решение было немного странно..я попробовал каждое решение я нашел в интернете и ничего. Surprisely это работает: удалить учетные данные.json, обновить, vinculate ваше приложение в вашем аккаунте снова. Новые полномочия.файл json будет иметь токен обновления. Сделайте где-нибудь резервную копию этого файла. Затем продолжайте использовать приложение, пока снова не появится ошибка маркера обновления. Удалите crendetials.файл json, который теперь только с сообщением об ошибке (это hapenned в моем случае), а затем вставьте старый файл учетных данных в папка, готово! Его была 1 неделя с тех пор, как я сделал это и больше не было проблем.