invalid_grant essayant d'obtenir le jeton oAuth de Google

121

Je reçois une invalid_granterreur en essayant d'obtenir un jeton oAuth de Google pour me connecter à l'API de leurs contacts. Toutes les informations sont correctes et j'ai vérifié à trois reprises ce genre de perplexité.

Quelqu'un sait-il ce qui peut être à l'origine de ce problème? J'ai essayé de configurer un identifiant de client différent pour cela mais j'obtiens le même résultat, j'ai essayé de connecter de nombreuses manières différentes, y compris en essayant l'authentification forcée, mais toujours le même résultat.

André Figueira
la source
pour moi, le problème était sur la page des informations d'identification de Google ... j'en ai créé une autre ... et j'ai résolu le problème ....
costamatrix

Réponses:

59

J'ai rencontré ce problème lorsque je n'ai pas explicitement demandé l'accès "hors ligne" lors de l'envoi de l'utilisateur vers OAuth "Voulez-vous autoriser cette application à toucher vos contenus?" page.

Assurez-vous de spécifier access_type = offline dans votre demande.

Détails ici: https://developers.google.com/accounts/docs/OAuth2WebServer#offline

(Aussi: je pense que Google a ajouté cette restriction à la fin de 2011. Si vous avez d'anciens jetons d'avant, vous devrez envoyer vos utilisateurs sur la page d'autorisation pour autoriser l'utilisation hors connexion.)

bonkydog
la source
9
@Adders je suis d'accord. Je me suis mis access_typeà offline, cette erreur se produit toujours.
diaporamap2
Cela ne devrait pas être la réponse acceptée.
Kishan Solanki le
Parcourez cette documentation developer.google.com/android-publisher/authorization et lisez tout à mettre en œuvre
Kishan Solanki
70

J'ai rencontré ce même problème malgré la spécification du "hors ligne" access_typedans ma demande selon la réponse de bonkydog. En bref, j'ai trouvé que la solution décrite ici fonctionnait pour moi:

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

En substance, lorsque vous ajoutez un client OAuth2 dans la console de votre API Google, Google vous donne un "ID client" et une "adresse e-mail" (en supposant que vous sélectionnez "application Web" comme type de client). Et malgré les conventions de dénomination trompeuses de Google, ils s'attendent à ce que vous client_idenvoyiez «l'adresse e-mail» comme valeur du paramètre lorsque vous accédez à leurs API OAuth2.

Cela s'applique lors de l'appel de ces deux URL:

Notez que l'appel à la première URL réussira si vous l'appelez avec votre "ID client" au lieu de votre "Adresse e-mail". Cependant, l'utilisation du code renvoyé par cette demande ne fonctionnera pas lors de la tentative d'obtention d'un jeton de support à partir de la deuxième URL. Au lieu de cela, vous obtiendrez un message "Erreur 400" et "invalid_grant".

aroth
la source
66
Absolument ridicule: en particulier la partie où cela fonctionne avec le client_id si vous obtenez un jeton de rafraîchissement initial. L'API Googles et leur documentation sont en désordre.
Traubenfuchs
7
Je me cognais la tête contre ce problème pendant tant d'heures. Je ne m'attendais jamais à ce que «client_id» ne soit pas ce qui était attendu pour le champ «client_id». Sauf le temps occasionnel où vous obtenez un refresh_token et que cela fonctionne. À peu près sûr que les mots que j'ai pour google pour le moment ne peuvent pas être prononcés sur SO.
Justin
6
Salut, je n'arrive pas à trouver cette adresse "email" dont vous parlez. c'est ce que j'ai dans ma console -> pbs.twimg.com/media/CVVcEBWUwAAIiCy.png:large
omarojo
4
Où est cette adresse e-mail? J'ai le même problème
Isma Haro
4
Ne faites jamais confiance à la documentation Google. La documentation et les API les plus effrayantes proviennent de Google, la société la plus précieuse au monde. J'ai dû passer d'innombrables heures à utiliser l'API Google. Il y avait des problèmes après des problèmes, puis leurs propres bibliothèques .Net pour différentes API ne se compilaient pas ensemble en raison de problèmes de dépendance différents. Le code fonctionne maintenant bien pour la plupart des utilisateurs, mais pour certains utilisateurs, j'obtiens toujours invalid_grant, invalid_credentials, etc. sans raison particulière.
Allen King
56

Bien que ce soit une vieille question, il semble que beaucoup la rencontrent encore - nous avons passé des jours entiers à la rechercher nous-mêmes.

Dans la spécification OAuth2, "invalid_grant" est une sorte de fourre-tout pour toutes les erreurs liées aux jetons invalides / expirés / révoqués (octroi d'authentification ou jeton d'actualisation).

Pour nous, le problème était double:

  1. L'utilisateur a activement révoqué l'accès à notre application
    Cela a du sens, mais comprenez ceci: 12 heures après la révocation, Google arrête d'envoyer le message d'erreur dans sa réponse: “error_description” : “Token has been revoked.”
    c'est plutôt trompeur car vous supposerez que le message d'erreur est là à tout moment, ce qui n'est pas le cas l'affaire. Vous pouvez vérifier si votre application a toujours accès à la page d'autorisation des applications .

  2. L'utilisateur a réinitialisé / récupéré son mot de passe Google
    En décembre 2015, Google a modifié son comportement par défaut afin que la réinitialisation du mot de passe pour les utilisateurs non Google Apps révoque automatiquement tous les jetons d'actualisation des applications de l'utilisateur. Lors de la révocation, le message d'erreur suit la même règle que le cas précédent, de sorte que vous n'obtiendrez la "description_erreur" que dans les 12 premières heures. Il ne semble pas y avoir de moyen de savoir si l'utilisateur a manuellement révoqué l'accès (intentionnel) ou si cela s'est produit à cause d'une réinitialisation du mot de passe (effet secondaire).

En dehors de ceux-ci, il existe une myriade d'autres causes potentielles qui pourraient déclencher l'erreur:

  1. L'horloge / l'heure du serveur n'est pas synchronisée
  2. Non autorisé pour l'accès hors ligne
  3. Limité par Google
  4. Utilisation de jetons d'actualisation expirés
  5. L'utilisateur est inactif depuis 6 mois
  6. Utiliser l'adresse e-mail du technicien de service au lieu de l'ID client
  7. Trop de jetons d'accès en peu de temps
  8. Le SDK client est peut-être obsolète
  9. Jeton d'actualisation incorrect / incomplet

J'ai écrit un court article résumant chaque élément avec des conseils de débogage pour aider à trouver le coupable. J'espère que ça aide.

laander
la source
1
Un autre scénario est si vous essayez d'obtenir des jetons plusieurs fois à partir du même code d'authentification.
knownasilya
C'était exactement mon problème, révoquer simplement l'application par accident. Ensuite, il fallait réexécuter le refreshToken.php via le terminal pour générer un autre code d'autorisation, puis remplacer le refreshToken partout pour cet ID client.
Robert Sinclair
7

J'ai rencontré le même problème. Pour moi, j'ai corrigé cela en utilisant l'adresse e-mail (la chaîne qui se termine par ... @ developer.gserviceaccount.com) au lieu de l'ID client pour la valeur du paramètre client_id. La dénomination définie par Google prête à confusion ici.

Tony Vu
la source
6
C'est la même réponse donnée par @aroth plus d'un an plus tôt
Bryan Ash
3

Mon problème était que j'utilisais cette URL:

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

Quand j'aurais dû utiliser cette URL:

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

Il s'agissait de tester un compte de service qui souhaitait un accès hors ligne au moteur de stockage .

Brad Lee
la source
2

J'ai eu le même message d'erreur 'invalid_grant' et c'était parce que l' envoi de authResult ['code'] du côté client javascript n'a pas été reçu correctement sur le serveur.

Essayez de le renvoyer à partir du serveur pour voir s'il est correct et non une chaîne vide.

Mihai Crăiță
la source
1

si vous utilisez la bibliothèque de scribe, configurez simplement le mode hors ligne, comme bonkydog l'a suggéré, voici le code:

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

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

Oleksii Kyslytsyn
la source
1

En utilisant un clientId Android (pas de client_secret), j'obtenais la réponse d'erreur suivante:

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

Je ne trouve aucune documentation pour le champ 'code_verifier', mais j'ai découvert que si vous le définissez sur des valeurs égales dans les demandes d'autorisation et de jeton, cela supprimera cette erreur. Je ne sais pas quelle devrait être la valeur prévue ou si elle devrait être sécurisée. Il a une longueur minimale (16? Caractères) mais j'ai trouvé que le réglage nullfonctionne également.

J'utilise AppAuth pour la demande d'autorisation dans mon client Android qui a une setCodeVerifier()fonction.

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

Voici un exemple de demande de jeton dans le nœud:

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

J'ai testé et cela fonctionne avec https://www.googleapis.com/oauth2/v4/tokenet https://accounts.google.com/o/oauth2/token.

Si vous utilisez à la GoogleAuthorizationCodeTokenRequestplace:

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

Vous devrez peut-être supprimer une réponse OAuth périmée / non valide.

Crédit: node.js google oauth2 sample a cessé de fonctionner invalid_grant

Remarque : Une réponse OAuth deviendra également invalide si le mot de passe utilisé dans l'autorisation initiale a été modifié.

Si vous vous trouvez dans un environnement bash, vous pouvez utiliser ce qui suit pour supprimer la réponse obsolète:

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

Lindauson
la source
1

Il y a deux raisons principales pour l' erreur invalid_grant que vous devez prendre en compte avant la demande POST pour le jeton d'actualisation et le jeton d'accès.

  1. L'en-tête de la requête doit contenir "content-type: application / x-www-form-urlencoded"
  2. Votre charge utile de demande doit être des données de formulaire encodées en URL, ne pas envoyer en tant qu'objet json.

RFC 6749 OAuth 2.0 a défini invalid_grant comme: l' octroi d'autorisation fourni (par exemple, le code d'autorisation, les informations d'identification du propriétaire de la ressource) ou le jeton d'actualisation est invalide, expiré, révoqué, ne correspond pas à l'URI de redirection utilisé dans la demande d'autorisation ou a été émis à un autre client .

J'ai trouvé un autre bon article, ici vous trouverez beaucoup d' autres raisons de cette erreur.

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

Hisham Javed
la source
Vouliez-vous publier deux réponses presque identiques? Vous souhaiterez peut-être supprimer celui-ci car l'autre a une ligne supplémentaire.
Blastfurnace
1

Si vous testez cela dans postman / insomnia et que vous essayez simplement de le faire fonctionner, un indice: le code d'authentification du serveur (paramètre de code) n'est valable qu'une seule fois. Cela signifie que si vous remplissez l'un des autres paramètres de la requête et que vous récupérez un 400, vous devrez utiliser un nouveau code d'authentification du serveur ou vous obtiendrez simplement 400 autres.

Jason Sultana
la source
0

dans ce site console.developers.google.com

ce panneau de console sélectionnez votre projet entrez l'url de serment. l'URL de rappel oauth sera redirigée lorsque le succès oauth

user5699596
la source
0

Après avoir examiné et essayé toutes les autres façons ici, voici comment j'ai résolu le problème dans nodejs avec le googleapismodule en conjonction avec le requestmodule, que j'ai utilisé pour récupérer les jetons au lieu de la getToken()méthode fournie :

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

J'utilise simplement requestpour faire la demande d'API via HTTP comme décrit ici: 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
lwdthe1
la source
0

Essayez de changer votre URL pour demander à

https://www.googleapis.com/oauth2/v4/token
Sergiy Voytovych
la source
0

Pour les futurs gens ... j'ai lu de nombreux articles et blogs mais j'ai eu de la chance avec la solution ci-dessous ...

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

Ce blog décrit différents cas dans lesquels l'erreur "invalid_grant" survient.

Prendre plaisir!!!

Kanishk Gupta
la source
0

pour moi, je devais m'assurer que le redirect_uricorrespondait exactement à celui de la console du développeur Authorised redirect URIs, qui l'a corrigé pour moi, j'ai pu déboguer et savoir quel était exactement le problème après le passage de https://accounts.google.com/o/oauth2/tokenàhttps://www.googleapis.com/oauth2/v4/token

J'ai une erreur correcte:

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

J'ai eu ce problème après avoir activé une nouvelle API de service sur la console Google et essayé d'utiliser les informations d'identification précédemment créées.

Pour résoudre le problème, j'ai dû retourner à la page d'informations d'identification, en cliquant sur le nom des informations d'identification, puis en cliquant à nouveau sur "Enregistrer" . Après cela, je pourrais très bien m'authentifier.

Kimphamg
la source
0

Dans mon cas, le problème était dans mon code. Par erreur, j'ai essayé d'initier le client 2 fois avec les mêmes jetons. Si aucune des réponses ci-dessus n'a aidé, assurez-vous de ne pas générer 2 instances du client.

Mon code avant le correctif:

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)

dès que je le change en (n'utiliser qu'une seule instance):

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

il a résolu mes problèmes avec le type de subvention.

Kate Kasinskaya
la source
0

Pour moi, le problème était que j'avais plusieurs clients dans mon projet et je suis presque sûr que tout va bien, mais j'ai supprimé tous les clients de ce projet et en ai créé un nouveau et tout a commencé à travailler pour moi (J'ai cette idée de l'aide du plugin WP_SMTP forum d'assistance) Je ne suis pas en mesure de trouver ce lien pour référence

Subrata Fouzdar
la source
0

Si vous nettoyez l'entrée utilisateur (par exemple, $_GET["code"]en php) Assurez-vous de ne pas remplacer accidentellement quelque chose dans le code.

Le regex que j'utilise est maintenant /[^A-Za-z0-9\/-]/

nathanfranke
la source
0

Regardez ceci https://dev.to/risafj/beginner-s-guide-to-oauth-understanding-access-tokens-and-authorization-codes-2988

Vous avez d'abord besoin d'un access_token:

$code = $_GET['code'];

$clientid = "xxxxxxx.apps.googleusercontent.com";
$clientsecret = "xxxxxxxxxxxxxxxxxxxxx";

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://www.googleapis.com/oauth2/v4/token");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "client_id=".urlencode($clientid)."&client_secret=".urlencode($clientsecret)."&code=".urlencode($code)."&grant_type=authorization_code&redirect_uri=". urlencode("https://yourdomain.com"));
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($ch);
curl_close ($ch);

$server_output = json_decode($server_output);
$access_token = $server_output->access_token;
$refresh_token = $server_output->refresh_token;
$expires_in = $server_output->expires_in;

Sécurisez le jeton d'accès, le jeton d'actualisation et expire_in dans une base de données. Le jeton d'accès expire après $ expires_in secondes. Ensuite, vous devez récupérer un nouveau jeton d'accès (et le mettre en sécurité dans la base de données) avec la demande suivante:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://www.googleapis.com/oauth2/v4/token");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "client_id=".urlencode($clientid)."&client_secret=".urlencode($clientsecret)."&refresh_token=".urlencode($refresh_token)."&grant_type=refresh_token");
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($ch);
curl_close ($ch);

$server_output = json_decode($server_output);
$access_token = $server_output->access_token;
$expires_in = $server_output->expires_in;

N'oubliez pas d'ajouter le domaine redirect_uri à vos domaines dans votre console Google: https://console.cloud.google.com/apis/credentials dans l'onglet "OAuth 2.0-Client-IDs". Vous y trouverez également votre identifiant client et votre secret client.

Konstantin XFlash Stratigenas
la source
0

Il y a un délai d'attente non documenté entre le moment où vous redirigez pour la première fois l'utilisateur vers la page d'authentification Google (et récupérez un code) et lorsque vous prenez le code retourné et le publiez dans l'URL du jeton. Cela fonctionne bien pour moi avec le client_id fourni par Google, par opposition à une "adresse e-mail non documentée". J'avais juste besoin de recommencer le processus.

Sparkyspider
la source