web-dev-qa-db-ja.com

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
}
238
Muhammad Usman

refresh_tokenは、ユーザーからの最初の承認時にのみ提供されます。 OAuth2統合のテスト中に行う種類などの後続の承認は、refresh_tokenを再度返しません。 :)

  1. アカウントにアクセスできるアプリを表示するページに移動します: https://myaccount.google.com/u/0/permissions
  2. [サードパーティアプリ]メニューで、アプリを選択します。
  3. [アクセス権を削除]をクリックし、Okをクリックして確認します
  4. 次のOAuth2リクエストはrefresh_tokenを返します。

または、クエリパラメーターPrompt=consentをOAuthリダイレクトに追加できます(Googleの Webサーバーアプリケーション用のOAuth 2. ページを参照)。

これにより、ユーザーにアプリケーションを再度承認するよう求められ、常にrefresh_tokenが返されます。

597
Rich Sutton

リフレッシュトークンを取得するには、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");
56
Gal Morad

私は長い夜を捜しました、そして、これはトリックをしています:

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

今すぐ保存する必要があります;)

アクセスキーがタイムアウトしたら

$client->refreshToken($theRefreshTokenYouHadStored);
27
Norbert

これにより混乱が生じたので、難しい方法を学ぶために私が来たものを共有すると思いました。

access_type=offlineおよびapproval_Prompt=forceパラメーターを使用してアクセスを要求すると、accessトークンとrefreshトークンの両方を受け取る必要があります。 accessトークンは、受け取った後すぐに期限切れになり、更新する必要があります。

新しいaccessトークンを取得する要求を正しく行い、新しいaccessトークンを含む応答を受け取りました。また、新しいrefreshトークンを取得しなかったという事実にも混乱しました。ただし、これは、同じrefreshトークンを何度も使用できるためです。

他の答えのいくつかは、何らかの理由で新しいrefreshトークンを取得したいと考えており、ユーザーを再承認することを示唆していると思いますが、実際には、 refreshトークンはユーザーによって取り消されるまで機能します。

14
jeteon

Rich Suttonの答えaccess_type=offlineの追加がフロントエンドクライアントの認証要求で行われたことに気付いた後、ようやく機能しましたコード、notそのコードをaccess_tokenに交換するバックエンド要求。私は彼の答えにコメントを追加しました Googleのこのリンク トークンの更新の詳細について。

追伸Satellizerを使用している場合は、 AngularJSの$ authProvider.googleにそのオプションを追加する方法はこちら です。

7
Zack Morris

これを設定すると、更新トークンが毎回送信されます。

$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');
2
apadana

1。 'refresh_token'を取得するには?

Solution:authURLを生成するときは、access_type = 'offline'オプションを使用する必要があります。 source: WebサーバーアプリケーションにOAuth 2.0を使用

2。しかし、「access_type = offline」を使用しても、「refresh_token」は取得されませんか?

解決策:最初のリクエストでのみ取得できることに注意してください。前回の有効期限が切れた後に新しいaccess_tokenを取得する際のコード、およびこの値を上書きしないようにしてください。

Google Auth Docから:(この値= access_type)

この値は、アプリケーションがトークンの認証コードを初めて交換するときに、更新トークンとアクセストークンを返すようにGoogle認証サーバーに指示します。

再度「refresh_token」が必要な場合は、 Rich Sutton's answer に記載されている手順に従って、アプリへのアクセスを削除する必要があります。

2
Mazahir Haroon

refresh_tokenを取得するには、OAuthリクエストURLにaccess_type=offlineを含める必要があります。ユーザーが初めて認証されると、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"
)

スコープには2つのスペース区切りのエントリがあり、1つはGoogleアナリティクスへの読み取り専用アクセス用で、もう1つはprofileであり、これはOpenID Connect標準です。

これにより、Googleはid_token応答でget_tokenという追加の属性を提供します。 id_tokenから情報を取得するには、Googleドキュメントで このページをチェックアウト します。これを検証して「デコード」するGoogle提供のライブラリがいくつかあります(Ruby google-id-token gem を使用しました)。解析されると、subパラメーターは実質的に一意のGoogleアカウントIDになります。

注目に値するのは、スコープをchangeすると、元のスコープで既に認証されているユーザーの更新トークンを再度取得することです。これは、たとえば、既に多数のユーザーがいて、すべてのユーザーがGoogleでアプリを認証しないようにしたくない場合に便利です。

ああ、最後の注意点:needPrompt=select_accountは必要ありませんが、ユーザーがもっと多くで認証したい状況がある場合に便利です1つのGoogleアカウント(つまり、サインイン/認証にこれを使用していない)。

2
coreyward

私にとっては、Googleが提供するCalendarSampleServletを試していました。 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;
}
1
Anoop Isaac
    #!/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
0
Yordan Georgiev

私の解決策は少し奇妙でした。インターネットで見つけたすべての解決策を試しましたが、何もしませんでした。驚いたことにこれは機能しました。credentials.jsonを削除し、更新し、アカウントでアプリを再度認証します。新しいcredentials.jsonファイルには更新トークンが含まれます。このファイルをどこかにバックアップします。その後、トークンの更新エラーが再び発生するまでアプリを使用し続けます。エラーメッセージのみが表示されたcrendetials.jsonファイルを削除し(これは私の場合はこのようになっています)、古い資格情報ファイルをフォルダーに貼り付けます。完了です! iveがこれを行ってから1週間が経過し、問題はありませんでした。

0
Guilherme Canoa

現在、Googleは私のリクエストでこれらのパラメータを拒否しました(access_type、Prompt)... :(そして「Revoke Access」ボタンはまったくありません。refresh_tokenlolを取り戻すためにイライラしています

更新:私はここで答えを見つけました: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が返されます。

0
Tran Tien Duc

オフラインアクセスおよびPrompt:consentを使用するとうまくいきました。

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

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

認証のたびに新しいrefresh_tokenを取得するには、ダッシュボードで作成されるOAuth 2.0資格情報のタイプが「その他」である必要があります。また、前述のように、authURLを生成するときにaccess_type = 'offline'オプションを使用する必要があります。

「Webアプリケーション」タイプの資格情報を使用する場合、Prompt/approval_Prompt変数の組み合わせは機能しません。最初の要求でのみrefresh_tokenを取得できます。

0
Martin Kask