Не получает маркер обновления Google OAuth


Я хочу получить маркер доступа от Google. API Google говорит чтобы получить токен доступа, отправьте код и другие параметры на страницу генерации токенов, и ответ будет объектом 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 210

12 ответов:

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

  1. перейдите на страницу, где отображаются приложения с доступом к вашей учетной записи: https://myaccount.google.com/u/0/permissions.
  2. в меню сторонних приложений выберите свое приложение.
  3. Нажмите кнопку Удалить доступ, а затем нажмите кнопку ОК, чтобы подтвердите
  4. следующий запрос OAuth2, который вы сделаете, вернет a 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();

вы должны хранить его теперь ;)

когда ваш ключ доступа тайм-аут просто сделать

$client->refreshToken($theRefreshTokenYouHadStored);

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

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

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

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

ответ Рича Саттона наконец, работал для меня, после того, как я понял, что добавлять 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. Когда пользователь аутентифицируется в первый раз, вы получите обратно не-nil refresh_token и access_token, срок действия которого истекает.

если у вас есть ситуация, когда пользователь может повторно аутентифицировать учетную запись, для которой у вас уже есть маркер аутентификации (например, @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, а другая-просто 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); 

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