invalid_grant che tenta di ottenere il token oAuth da Google


121

Continuo a ricevere un invalid_granterrore nel tentativo di ottenere un token oAuth da Google per connettersi all'API dei contatti. Tutte le informazioni sono corrette e ho controllato tre volte questo così perplesso.

Qualcuno sa cosa potrebbe causare questo problema? Ho provato a impostare un ID client diverso per esso, ma ottengo lo stesso risultato, ho provato a connettere molti modi diversi, incluso provare l'autenticazione forzata, ma sempre lo stesso risultato.


per me il problema era sulla pagina delle credenziali di google ... ne ho creato un altro ... e ho risolto il problema ....
costamatrix

Risposte:


59

Mi sono imbattuto in questo problema quando non ho richiesto esplicitamente l'accesso "offline" quando ho inviato l'utente a OAuth "Vuoi dare a questa app il permesso di toccare le tue cose?" pagina.

Assicurati di specificare access_type = offline nella tua richiesta.

Dettagli qui: https://developers.google.com/accounts/docs/OAuth2WebServer#offline

(Inoltre: penso che Google abbia aggiunto questa restrizione alla fine del 2011. Se hai vecchi token di prima di allora, dovrai inviare i tuoi utenti alla pagina di autorizzazione per autorizzare l'uso offline.)


9
@Adders sono d'accordo. Ho impostato access_typea offline, questo errore si verifica ancora.
slideshowp2

Questa non dovrebbe essere la risposta accettata.
Kishan Solanki

Consulta questa documentazione developers.google.com/android-publisher/authorization e leggi tutto per l'implementazione
Kishan Solanki

70

Mi sono imbattuto in questo stesso problema nonostante abbia specificato "offline" access_typenella mia richiesta come da risposta di bonkydog. Per farla breve, ho scoperto che la soluzione qui descritta ha funzionato per me:

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

In sostanza, quando aggiungi un client OAuth2 nella console dell'API di Google, Google ti fornirà un "ID client" e un "indirizzo email" (supponendo che tu selezioni "webapp" come tipo di client). E nonostante le convenzioni di denominazione fuorvianti di Google, si aspettano che invii "Indirizzo email" come valore del client_idparametro quando accedi alle loro API OAuth2.

Ciò si applica quando si chiamano entrambi questi URL:

Tieni presente che la chiamata al primo URL avrà esito positivo se lo chiami con il tuo "ID cliente" invece del tuo "indirizzo e-mail". Tuttavia, l'utilizzo del codice restituito da tale richiesta non funzionerà quando si tenta di ottenere un token di trasporto dal secondo URL. Riceverai invece un "Errore 400" e un messaggio "invalid_grant".


66
Assolutamente ridicolo: soprattutto la parte in cui funziona con client_id se ottieni un token di aggiornamento iniziale. L'API di Google e la loro documentazione sono un disastro.
Traubenfuchs

7
Ho sbattuto la testa contro questo problema per così tante ore. Non mi sarei mai aspettato che "client_id" non fosse quello previsto per il campo "client_id". Tranne il momento occasionale in cui ottieni un refresh_token e funziona. Sono abbastanza sicuro che le parole che ho per Google al momento non possono essere dette su SO.
Justin

6
Salve .. Non riesco a trovare quell'indirizzo "email" di cui state parlando. questo è quello che ho nella mia console -> pbs.twimg.com/media/CVVcEBWUwAAIiCy.png:large
omarojo

4
Dov'è quell'indirizzo email? Ho lo stesso problema
Isma Haro

4
Non fidarti mai della documentazione di Google. La documentazione e le API più elaborate provengono da Google, l'azienda più preziosa al mondo. Ho dovuto passare innumerevoli ore per utilizzare l'API di Google. C'erano problemi dopo problemi e quindi le loro librerie .Net per API diverse non venivano compilate insieme a causa di diversi problemi di dipendenza e tutto. Il codice ora funziona bene per la maggior parte degli utenti, ma per alcuni utenti continuo a ricevere invalid_grant, invalid_credentials ecc. Senza motivi particolari.
Allen King

56

Sebbene questa sia una vecchia domanda, sembra che molti la incontrino ancora: abbiamo passato giorni e giorni a rintracciarla da soli.

Nella specifica OAuth2, "invalid_grant" è una sorta di catch-all per tutti gli errori relativi a token non validi / scaduti / revocati (autorizzazione di autorizzazione o token di aggiornamento).

Per noi, il problema era duplice:

  1. L'utente ha revocato attivamente l'accesso alla nostra app Ha
    senso, ma ottieni questo: 12 ore dopo la revoca, Google smette di inviare il messaggio di errore nella sua risposta: “error_description” : “Token has been revoked.”
    è piuttosto fuorviante perché presumerai che il messaggio di errore sia sempre presente, il che non lo è il caso. Puoi verificare se la tua app ha ancora accesso alla pagina di autorizzazione delle app .

  2. L'utente ha reimpostato / recuperato la propria password Google
    A dicembre 2015, Google ha modificato il comportamento predefinito in modo che la reimpostazione della password per gli utenti non Google Apps revocherebbe automaticamente tutti i token di aggiornamento delle app dell'utente. In caso di revoca, il messaggio di errore segue la stessa regola del caso precedente, quindi riceverai solo "descrizione_errore" nelle prime 12 ore. Non sembra esserci alcun modo per sapere se l'utente ha revocato manualmente l'accesso (intenzionale) o è successo a causa di una reimpostazione della password (effetto collaterale).

Oltre a questi, ci sono una miriade di altre potenziali cause che potrebbero innescare l'errore:

  1. L'orologio / ora del server non è sincronizzato
  2. Non autorizzato per l'accesso offline
  3. Limitato da Google
  4. Utilizzo di token di aggiornamento scaduti
  5. L'utente è inattivo da 6 mesi
  6. Utilizza l'email del lavoratore dell'assistenza anziché l'ID cliente
  7. Troppi token di accesso in breve tempo
  8. Client SDK potrebbe essere obsoleto
  9. Token di aggiornamento errato / incompleto

Ho scritto un breve articolo che riassume ogni elemento con alcune linee guida per il debug per trovare il colpevole. Spero che sia d'aiuto.


1
Un altro scenario è se si tenta di ottenere i token più volte dallo stesso codice di autenticazione.
knownasilya

Questo era esattamente il mio problema, semplicemente revocare l'app per errore. Quindi ha dovuto rieseguire il refreshToken.php tramite il terminale per generare un altro codice di autorizzazione e quindi sostituire il refreshToken ovunque per questo clientID.
Robert Sinclair

7

Ho riscontrato lo stesso problema. Per me, ho risolto questo problema utilizzando l'indirizzo e-mail (la stringa che termina con ... @ developer.gserviceaccount.com) invece dell'ID client per il valore del parametro client_id. La denominazione impostata da Google è confusa qui.


6
Questa è la stessa risposta data da @aroth più di un anno prima
Bryan Ash il

3

Il mio problema era che ho utilizzato questo URL:

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

Quando avrei dovuto usare questo URL:

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

Si trattava di testare un account di servizio che desiderava l'accesso offline al motore di archiviazione .


2

Ho ricevuto lo stesso messaggio di errore "invalid_grant" ed è stato perché l' authResult ["codice"] inviato dal javascript lato client non è stato ricevuto correttamente sul server.

Prova a restituirlo dal server per vedere se è corretto e non una stringa vuota.


1

se stai usando la libreria scribe, imposta la modalità offline, come suggerito da bonkydog ecco il codice:

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

Utilizzando un client ID Android (no client_secret) stavo ottenendo la seguente risposta di errore:

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

Non riesco a trovare alcuna documentazione per il campo "code_verifier" ma ho scoperto che se lo imposti su valori uguali sia nelle richieste di autorizzazione che in quelle di token, questo errore verrà rimosso. Non sono sicuro di quale dovrebbe essere il valore previsto o se dovrebbe essere sicuro. Ha una lunghezza minima (16 caratteri?) Ma ho trovato che nullanche l' impostazione funziona.

Sto usando AppAuth per la richiesta di autorizzazione nel mio client Android che ha una setCodeVerifier()funzione.

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

Ecco un esempio di richiesta di token nel nodo:

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

Ho provato e funziona con entrambi https://www.googleapis.com/oauth2/v4/tokene https://accounts.google.com/o/oauth2/token.

Se invece stai usando GoogleAuthorizationCodeTokenRequest:

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


1

Potrebbe essere necessario rimuovere una risposta OAuth obsoleta / non valida.

Credito: il campione node.js google oauth2 ha smesso di funzionare invalid_grant

Nota : anche una risposta OAuth non sarà più valida se la password utilizzata nell'autorizzazione iniziale è stata modificata.

Se in un ambiente bash, puoi utilizzare quanto segue per rimuovere la risposta non aggiornata:

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


1

Esistono due ragioni principali per l' errore invalid_grant di cui è necessario prestare attenzione prima della richiesta POST per il token di aggiornamento e il token di accesso.

  1. L'intestazione della richiesta deve contenere "content-type: application / x-www-form-urlencoded"
  2. Il payload della tua richiesta deve essere Dati modulo codificati in URL, non inviare come oggetto JSON.

RFC 6749 OAuth 2.0 ha definito invalid_grant come: la concessione di autorizzazione fornita (ad es. Codice di autorizzazione, credenziali del proprietario della risorsa) o il token di aggiornamento non è valido, è scaduto, revocato, non corrisponde all'URI di reindirizzamento utilizzato nella richiesta di autorizzazione o è stato rilasciato a un altro client .

Ho trovato un altro buon articolo, qui troverai molti altri motivi per questo errore.

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


Volevi pubblicare due risposte quasi identiche? Potresti voler eliminare questo perché l'altro ha una riga aggiuntiva.
Blastfurnace

1

Se stai testando questo in Postman / insomnia e stai solo cercando di farlo funzionare, suggerimento: il codice di autorizzazione del server (parametro del codice) è valido solo una volta. Significa che se riempi uno qualsiasi degli altri parametri nella richiesta e ottieni un 400, dovrai utilizzare un nuovo codice di autorizzazione del server o riceverai solo un altro 400.



0

Dopo aver considerato e provato tutti gli altri modi qui, ecco come ho risolto il problema in nodejs con il googleapismodulo in combinazione con il requestmodulo, che ho usato per recuperare i token invece del getToken()metodo fornito :

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

Uso semplicemente requestper effettuare la richiesta API tramite HTTP come descritto qui: 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

Prova a cambiare l'URL per la richiesta in

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

0

Per i futuri ... ho letto molti articoli e blog ma ho avuto fortuna con la soluzione qui sotto ...

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

Questo blog descrive diversi casi in cui si verifica l'errore "invalid_grant".

Godere!!!


0

per me ho dovuto assicurarmi che redirect_uricorrispondesse esattamente a quello nella console per sviluppatori Authorised redirect URIs, che lo ha risolto per me, sono stato in grado di eseguire il debug e sapere qual era esattamente il problema dopo il passaggio da https://accounts.google.com/o/oauth2/tokenahttps://www.googleapis.com/oauth2/v4/token

Ho ricevuto un errore corretto:

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

0

Ho riscontrato questo problema dopo aver abilitato una nuova API di servizio sulla console di Google e provato a utilizzare le credenziali precedentemente create.

Per risolvere il problema, sono dovuto tornare alla pagina delle credenziali, fare clic sul nome della credenziale e fare nuovamente clic su "Salva" . Dopodiché, potrei autenticarmi senza problemi.


0

Nel mio caso, il problema era nel mio codice. Per errore ho provato ad avviare il client 2 volte con gli stessi token. Se nessuna delle risposte precedenti ti ha aiutato, assicurati di non generare 2 istanze del client.

Il mio codice prima della correzione:

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)

non appena lo cambio in (usa solo un'istanza):

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

ha risolto i miei problemi con il tipo di concessione.


0

Per me il problema era che avevo più clienti nel mio progetto e sono abbastanza sicuro che va perfettamente bene, ma ho cancellato tutto il client per quel progetto e ne ho creato uno nuovo e tutto ha iniziato a funzionare per me (Ho avuto questa idea per l'aiuto del plugin WP_SMTP forum di supporto) Non sono in grado di trovare quel collegamento come riferimento


0

Se stai disinfettando l'input dell'utente (ad esempio, $_GET["code"]in php) Assicurati di non sostituire accidentalmente qualcosa nel codice.

La regex che sto usando è adesso /[^A-Za-z0-9\/-]/


0

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

Per prima cosa hai bisogno di 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;

Salva il token di accesso e il token di aggiornamento e expire_in, in un database. Il token di accesso scade dopo $ expires_in secondi. Quindi devi prendere un nuovo token di accesso (e salvarlo nel database) con la seguente richiesta:

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

Ricorda di aggiungere il dominio redirect_uri ai tuoi domini nella tua console Google: https://console.cloud.google.com/apis/credentials nella scheda "OAuth 2.0-Client-IDs". Lì troverai anche il tuo ID cliente e Segreto client.


0

C'è un timeout non documentato tra la prima volta che reindirizzi l'utente alla pagina di autenticazione di Google (e ottieni un codice) e quando prendi il codice restituito e lo pubblichi nell'URL del token. Funziona bene per me con l'effettivo client_id fornito da Google invece di un "indirizzo email non documentato". Avevo solo bisogno di ricominciare il processo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.