web-dev-qa-db-ja.com

invalid_grantがGoogleからoAuthトークンを取得しようとしています

GoogleからoAuthトークンを取得して連絡先APIに接続しようとすると、invalid_grantエラーが発生し続けます。すべての情報が正しいので、これを3回チェックしました。

この問題の原因を誰もが知っていますか?別のクライアントIDを設定しようとしましたが、同じ結果が得られます。強制認証の試行を含む多くの異なる方法を接続しようとしましたが、それでも同じ結果です。

96
André Figueira

ユーザーをOAuthに送信するときに「オフライン」アクセスを明示的に要求しなかったときに、この問題に遭遇しました。「このアプリにあなたのものにアクセスする許可を与えますか?」ページ。

リクエストでは必ずaccess_type = offlineを指定してください。

詳細はこちら: https://developers.google.com/accounts/docs/OAuth2WebServer#offline

(また、2011年後半にGoogleがこの制限を追加したと思います。それ以前の古いトークンを持っている場合、オフラインでの使用を許可するためにユーザーを許可ページに送る必要があります。)

54
bonkydog

Bonkydogの回答に従って、要求に「オフライン」access_typeを指定しているにもかかわらず、この同じ問題に遭遇しました。長い話を簡単に言えば、ここで説明した解決策が私にとってはうまくいくことがわかりました。

https://groups.google.com/forum/#!topic/google-analytics-data-export-api/4uNaJtquxCs

基本的に、Google APIのコンソールにOAuth2クライアントを追加すると、Googleは「クライアントID」と「メールアドレス」を提供します(クライアントタイプとして「webapp」を選択した場合)。また、Googleの誤解を招く命名規則にもかかわらず、OAuth2 APIにアクセスするときにclient_idパラメーターの値として「メールアドレス」を送信することを期待しています。

これは、これらのURLの両方を呼び出すときに適用されます。

「メールアドレス」ではなく「クライアントID」で呼び出すと、最初のURLへの呼び出しは成功になることに注意してください。ただし、2番目のURLからベアラートークンを取得しようとすると、そのリクエストから返されたコードを使用しても機能しません。代わりに、「エラー400」と「invalid_grant」メッセージが表示されます。

58
aroth

これは古い質問ですが、多くの人がまだ遭遇しているようです。

OAuth2仕様では、「invalid_grant」は、無効/期限切れ/失効したトークン(認証付与または更新トークン)に関連するすべてのエラーに対する一種のキャッチオールです。

私たちにとって、問題は2つありました。

  1. ユーザーがアプリへのアクセスを積極的に取り消しました
    理にかなっていますが、これを取得します:取り消しの12時間後、Googleは応答でエラーメッセージの送信を停止します“error_description” : “Token has been revoked.”
    エラーメッセージは常に存在するものの、そうではないと仮定するため、かなり誤解を招きます。 apps permission page で、アプリにまだアクセス権があるかどうかを確認できます。

  2. ユーザーはGoogleパスワードをリセット/回復しました
    2015年12月、 Googleが変更 デフォルトの動作により、Google Apps以外のユーザーのパスワードリセットは、すべてのユーザーのアプリ更新トークンを自動的に取り消すようになりました。失効時には、エラーメッセージは前のケースと同じルールに従うため、最初の12時間以内に「error_description」のみを取得します。ユーザーが手動でアクセスを取り消したのか(意図的)、パスワードのリセットが原因で発生したのか(副作用)を知る方法はないようです。

これらとは別に、エラーを引き起こす可能性のある他の無数の潜在的な原因があります。

  1. サーバーのクロック/時間が同期していません
  2. オフラインアクセスは許可されていません
  3. Googleによる調整
  4. 期限切れの更新トークンの使用
  5. ユーザーは6か月間非アクティブでした
  6. クライアントIDの代わりにService Workerのメールを使用する
  7. 短時間で多すぎるアクセストークン
  8. クライアントSDKは古くなっている可能性があります
  9. 間違った/不完全なリフレッシュトークン

私は短い記事を書きました 犯人を見つけるのを助けるためにいくつかのデバッグガイダンスで各アイテムを要約します。それが役に立てば幸い。

44
laander

同じ問題が発生しました。私にとっては、client_idパラメーター値のクライアントIDの代わりに、電子メールアドレス(... @ developer.gserviceaccount.comで終わる文字列)を使用してこれを修正しました。 Googleが設定したネーミングはここではわかりにくいです。

7
Tony Vu

私の問題は、このURLを使用したことです。

https://accounts.google.com/o/oauth2/token

このURLを使用すべきだった場合:

https://www.googleapis.com/oauth2/v4/token

これは、 ストレージエンジン へのオフラインアクセスを必要とするサービスアカウントをテストしていました。

2
Brad Lee

同じエラーメッセージ「invalid_grant」が発生しました。これは、クライアント側javascriptからのauthResult ['code'] sendがサーバーで正しく受信されなかったためです。

サーバーから出力して、空の文字列ではなく正しいかどうかを確認してください。

2
Mihai Crăiță

古い/無効なOAuth応答を削除する必要がある場合があります。

クレジット: node.js google oauth2サンプルはinvalid_grantの動作を停止しました

:OAuth応答も、初期認証で使用されるパスワードが変更された場合に無効になります。

Bash環境の場合、以下を使用して古い応答を削除できます。

rm /Users/<username>/.credentials/<authorization.json>

1
Lindauson

invalid_grantエラーには2つの主な理由があり、POSTリクエストの前に注意する必要があります更新トークンおよびアクセストークン用。

  1. 要求ヘッダーには、「content-type:application/x-www-form-urlencoded」が含まれている必要があります
  2. リクエストペイロードは、URLエンコードされたフォームデータである必要があります。jsonオブジェクトとして送信しないでください。

RFC 6749 OAuth 2. definedinvalid_grantas:提供された認可付与(例:承認コード、リソース所有者の資格情報)または更新トークンが無効、期限切れ、失効、承認リクエストで使用されたリダイレクトURIと一致しない、または別のクライアントに発行されました。

別の良い記事を見つけました here このエラーには他の多くの理由があります。

https://blog.timekit.io/google-oauth-invalid-grant-nightmare-and-how-to-fix-it-9f4efaf1da35

1
Hisham Javed

これは馬鹿げた答えですが、私にとっての問題は、保存に失敗したGoogleユーザーのアクティブなoAuthトークンが既に発行されていることに気付かなかったことです。この場合の解決策は、APIコンソールに移動して、クライアントシークレットをリセットすることです。

この効果に対するSOには、他にも多数の回答があります。たとえば、 クライアントシークレットOAuth2のリセット-クライアントはアクセスを再許可する必要がありますか?

1
paul

スクライブライブラリを使用している場合は、オフラインモードを設定するだけです。たとえば、bonkydogで提案されているのはコードです。

OAuthService service = new ServiceBuilder().provider(Google2Api.class).apiKey(clientId).apiSecret(apiSecret)
                .callback(callbackUrl).scope(SCOPE).offline(true)
                .build();

https://github.com/codolutions/scribe-Java/

1

Android clientId(client_secretなし)を使用すると、次のエラー応答が返されました。

{
 "error": "invalid_grant",
 "error_description": "Missing code verifier."
}

フィールド 'code_verifier'のドキュメントは見つかりませんが、認証要求とトークン要求の両方で同じ値に設定すると、このエラーが削除されることがわかりました。意図した値がどうあるべきか、または安全である必要があるかどうかはわかりません。最小の長さ(16文字)がありますが、nullへの設定も機能することがわかりました。

setCodeVerifier()関数を持つAndroidクライアントの承認リクエストにAppAuthを使用しています。

AuthorizationRequest authRequest = new AuthorizationRequest.Builder(
                                    serviceConfiguration,
                                    provider.getClientId(),
                                    ResponseTypeValues.CODE,
                                    provider.getRedirectUri()
                            )
                            .setScope(provider.getScope())
                            .setCodeVerifier(null)
                            .build();

ノード内のトークンリクエストの例を次に示します。

request.post(
  'https://www.googleapis.com/oauth2/v4/token',
  { form: {
    'code': '4/xxxxxxxxxxxxxxxxxxxx',
    'code_verifier': null,
    'client_id': 'xxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com',
    'client_secret': null,
    'redirect_uri': 'com.domain.app:/oauth2redirect',
    'grant_type': 'authorization_code'
  } },
  function (error, response, body) {
    if (!error && response.statusCode == 200) {
      console.log('Success!');
    } else {
      console.log(response.statusCode + ' ' + error);
    }

    console.log(body);
  }
);

テストしましたが、これはhttps://www.googleapis.com/oauth2/v4/tokenhttps://accounts.google.com/o/oauth2/tokenの両方で動作します。

代わりにGoogleAuthorizationCodeTokenRequestを使用している場合:

final GoogleAuthorizationCodeTokenRequest req = new GoogleAuthorizationCodeTokenRequest(
                    TRANSPORT,
                    JSON_FACTORY,
                    getClientId(),
                    getClientSecret(),
                    code,
                    redirectUrl
);
req.set("code_verifier", null);          
GoogleTokenResponse response = req.execute();
1
Justin Fiedler

リクエストのURLを変更してみてください

https://www.googleapis.com/oauth2/v4/token
0

私の場合、問題は私のコードにありました。誤って、同じトークンを使用してクライアントを2回開始しようとしました。上記の回答のいずれも役に立たない場合は、クライアントの2つのインスタンスを生成しないようにしてください。

修正前の私のコード:

def gc_service
      oauth_client = Signet::OAuth2::Client.new(client_options)
      oauth_client.code = params[:code]
      response = oauth_client.fetch_access_token!
      session[:authorization] = response
      oauth_client.update!(session[:authorization])

      gc_service = Google::Apis::CalendarV3::CalendarService.new
      gc_service.authorization = oauth_client

      gc_service
    end
primary_calendar_id = gc_service.list_calendar_lists.items.select(&:primary).first.id

gc_service.insert_acl(primary_calendar_id, acl_rule_object, send_notifications: false)

変更するとすぐに(1つのインスタンスのみを使用):

@gc_service = gc_service
primary_calendar_id = @gc_service.list_calendar_lists.items.select(&:primary).first.id

@gc_service.insert_acl(primary_calendar_id, acl_rule_object, send_notifications: false)

助成金タイプに関する私の問題を修正しました。

0
Kate Kasinskaya

このサイトで console.developers.google.com

このコンソールボードは、宣誓のURLを入力してプロジェクトを選択します。 oauthが成功すると、oauthコールバックURLがリダイレクトします

0
user5699596

Googleコンソールで新しいサービスAPIを有効にし、以前に作成した資格情報を使用しようとした後、この問題が発生しました。

問題を解決するには、資格情報ページに戻り、資格情報名をクリックして、「保存」をクリックしますagain。その後、私はうまく認証できました。

0
kimphamg

将来の人々のために...私は多くの記事とブログを読みましたが、以下の解決策で運がありました...

GoogleTokenResponse tokenResponse =
      new GoogleAuthorizationCodeTokenRequest(
          new NetHttpTransport(),
          JacksonFactory.getDefaultInstance(),
          "https://www.googleapis.com/oauth2/v4/token",
          clientId,
          clientSecret,
          authCode,
          "") //Redirect Url
     .setScopes(scopes)
     .setGrantType("authorization_code")
     .execute();

This ブログでは、「invalid_grant」エラーが発生するさまざまなケースを説明しています。

楽しい!!!

0
Kanishk Gupta

ここで他のすべての方法を検討して試してみた後、提供されたgetToken()メソッドの代わりにトークンを取得するために使用したgoogleapisモジュールと一緒にrequestモジュールを使用してnodejsの問題を解決した方法は次のとおりです:

const request = require('request');

//SETUP GOOGLE AUTH
var google = require('googleapis');
const oAuthConfigs = rootRequire('config/oAuthConfig')
const googleOAuthConfigs = oAuthConfigs.google

//for google OAuth: https://github.com/google/google-api-nodejs-client
var OAuth2 = google.auth.OAuth2;
var googleOAuth2Client = new OAuth2(
    process.env.GOOGLE_OAUTH_CLIENT_ID || googleOAuthConfigs.clientId, 
    process.env.GOOGLE_OAUTH_CLIENT_SECRET || googleOAuthConfigs.clientSecret, 
    process.env.GOOGLE_OAUTH_CLIENT_REDIRECT_URL || googleOAuthConfigs.callbackUrl);

/* generate a url that asks permissions for Google+ and Google Calendar scopes
https://developers.google.com/identity/protocols/googlescopes#monitoringv3*/
var googleOAuth2ClientScopes = [
    'https://www.googleapis.com/auth/plus.me',
    'https://www.googleapis.com/auth/userinfo.email'
];

var googleOAuth2ClientRedirectURL = process.env.GOOGLE_OAUTH_CLIENT_REDIRECT_URL || googleOAuthConfigs.callbackUrl; 

var googleOAuth2ClientAuthUrl = googleOAuth2Client.generateAuthUrl({
  access_type: 'offline', // 'online' (default) or 'offline' (gets refresh_token)
  scope: googleOAuth2ClientScopes // If you only need one scope you can pass it as string
});

//AFTER SETUP, THE FOLLOWING IS FOR OBTAINING TOKENS FROM THE AUTHCODE


        const ci = process.env.GOOGLE_OAUTH_CLIENT_ID || googleOAuthConfigs.clientId
        const cs = process.env.GOOGLE_OAUTH_CLIENT_SECRET || googleOAuthConfigs.clientSecret
        const ru = process.env.GOOGLE_OAUTH_CLIENT_REDIRECT_URL || googleOAuthConfigs.callbackUrl
        var oauth2Client = new OAuth2(ci, cs, ru);

        var hostUrl = "https://www.googleapis.com";
        hostUrl += '/oauth2/v4/token?code=' + authCode + '&client_id=' + ci + '&client_secret=' + cs + '&redirect_uri=' + ru + '&grant_type=authorization_code',
        request.post({url: hostUrl}, function optionalCallback(err, httpResponse, data) {
            // Now tokens contains an access_token and an optional refresh_token. Save them.
            if(!err) {
                //SUCCESS! We got the tokens
                const tokens = JSON.parse(data)
                oauth2Client.setCredentials(tokens);

                //AUTHENTICATED PROCEED AS DESIRED.
                googlePlus.people.get({ userId: 'me', auth: oauth2Client }, function(err, response) {
                // handle err and response
                    if(!err) {
                        res.status(200).json(response);
                    } else {
                        console.error("/google/exchange 1", err.message);
                        handleError(res, err.message, "Failed to retrieve google person");
                    }
                });
            } else {
                console.log("/google/exchange 2", err.message);
                handleError(res, err.message, "Failed to get access tokens", err.code);
            }
        });

ここで説明するように、requestを使用してHTTP経由でapiリクエストを行うだけです。 https://developers.google.com/identity/protocols/OAuth2WebServer#offline

POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=8819981768.apps.googleusercontent.com&
client_secret={client_secret}&
redirect_uri=https://oauth2.example.com/code&
grant_type=authorization_code
0
lwdthe1

私にとっては、redirect_uriが開発者コンソールAuthorised redirect URIsにあるものと完全に一致することを確認する必要がありました。 https://accounts.google.com/o/oauth2/tokenからhttps://www.googleapis.com/oauth2/v4/tokenへの切り替え

適切なエラーが発生しました:

{"error": "redirect_uri_mismatch",  "error_description": "Bad Request"}
0
Waqleh

私の問題は、プロジェクトに複数のクライアントがあり、これが完全に大丈夫だと確信していますが、そのプロジェクトのすべてのクライアントを削除し、新しいクライアントを作成し、すべてが私のために働き始めました(WP_SMTPプラグインヘルプからこのアイデアを得ましたサポートフォーラム)参照のためにそのリンクを見つけることができません

0
Subrata Fouzdar