invalid_grant intentando obtener el token de oAuth de google


120

Sigo recibiendo un invalid_granterror al intentar obtener un token oAuth de Google para conectarme a la API de sus contactos. Toda la información es correcta y he comprobado esto por triplicado, de modo que me quedé perplejo.

¿Alguien sabe qué puede estar causando este problema? He intentado configurar una identificación de cliente diferente para él, pero obtengo el mismo resultado, he intentado conectar muchas formas diferentes, incluido intentar la autenticación forzada, pero sigue siendo el mismo resultado.


para mí, el problema estaba en la página de credenciales de Google ... creé otra ... y resolví el problema ...
costamatrix

Respuestas:


59

Me encontré con este problema cuando no solicité explícitamente acceso "sin conexión" al enviar al usuario a OAuth "¿Quieres darle permiso a esta aplicación para tocar tus cosas?" página.

Asegúrese de especificar access_type = offline en su solicitud.

Detalles aquí: https://developers.google.com/accounts/docs/OAuth2WebServer#offline

(Además: creo que Google agregó esta restricción a fines de 2011. Si tiene tokens antiguos de antes, deberá enviar a sus usuarios a la página de permisos para autorizar el uso sin conexión).


9
@Adders estoy de acuerdo. Me puse access_typeen offline, este error todavía ocurre.
slideshowp2

Esta no debería ser la respuesta aceptada.
Kishan Solanki

Revise esta documentación developers.google.com/android-publisher/authorization y lea todo para implementar
Kishan Solanki

70

Me encontré con este mismo problema a pesar de especificar "sin conexión" access_typeen mi solicitud según la respuesta de bonkydog. En pocas palabras, descubrí que la solución descrita aquí funcionó para mí:

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

En esencia, cuando agrega un cliente OAuth2 en la consola de su API de Google, Google le dará un "ID de cliente" y una "dirección de correo electrónico" (suponiendo que seleccione "aplicación web" como su tipo de cliente). Y a pesar de las convenciones de nomenclatura engañosas de Google, esperan que envíe la "Dirección de correo electrónico" como valor del client_idparámetro cuando acceda a sus API de OAuth2.

Esto se aplica al llamar a estas dos URL:

Tenga en cuenta que la llamada a la primera URL se realizará correctamente si la llama con su "ID de cliente" en lugar de su "Dirección de correo electrónico". Sin embargo, el uso del código devuelto por esa solicitud no funcionará al intentar obtener un token de portador de la segunda URL. En su lugar, obtendrá un 'Error 400' y un mensaje "invalid_grant".


66
Absolutamente ridículo: especialmente la parte donde funciona con el client_id si obtiene un token de actualización inicial. La API de Google y su documentación son un desastre.
Traubenfuchs

7
Estuve golpeándome la cabeza contra este tema durante tantas horas. Nunca esperé que 'client_id' no fuera lo que se esperaba para el campo 'client_id'. Excepto el momento ocasional en que obtiene un refresh_token y funciona. Estoy bastante seguro de que las palabras que tengo para Google en este momento no se pueden decir en SO.
Justin

6
Hola ... No puedo encontrar esa dirección de "correo electrónico" de la que están hablando. esto es lo que tengo en mi consola -> pbs.twimg.com/media/CVVcEBWUwAAIiCy.png:large
omarojo

4
¿Dónde está esa dirección de correo electrónico? Tengo el mismo problema
Isma Haro

4
Nunca confíe en la documentación de Google. La documentación y las API más malas provienen de Google, la empresa más valiosa del mundo. Tuve que pasar incontables horas para usar la API de Google. Hubo problemas después de problemas y luego sus propias bibliotecas .Net para diferentes API que no se compilaron juntas debido a diferentes problemas de dependencia y todo. El código ahora funciona bien para la mayoría de los usuarios, pero para algunos usuarios todavía obtengo invalid_grant, invalid_credentials, etc. sin ninguna razón en particular.
Allen King

56

Aunque esta es una pregunta antigua, parece que muchos todavía la encuentran: pasamos días y días rastreando esto nosotros mismos.

En la especificación OAuth2, "invalid_grant" es una especie de catch-all para todos los errores relacionados con tokens no válidos / caducados / revocados (token de autorización o actualización).

Para nosotros, el problema era doble:

  1. El usuario ha revocado activamente el acceso a nuestra aplicación
    Tiene sentido, pero obtén esto: 12 horas después de la revocación, Google deja de enviar el mensaje de error en su respuesta: “error_description” : “Token has been revoked.”
    Es bastante engañoso porque asumirá que el mensaje de error está ahí en todo momento, lo que no es el caso. Puede verificar si su aplicación todavía tiene acceso en la página de permisos de aplicaciones .

  2. El usuario restableció / recuperó su contraseña de Google
    En diciembre de 2015, Google cambió su comportamiento predeterminado para que los restablecimientos de contraseña para usuarios que no son de Google Apps revoquen automáticamente todos los tokens de actualización de aplicaciones del usuario. En la revocación, el mensaje de error sigue la misma regla que en el caso anterior, por lo que solo obtendrá la "descripción_error" en las primeras 12 horas. No parece haber ninguna forma de saber si el usuario revocó el acceso manualmente (intencionalmente) o si sucedió debido a un restablecimiento de contraseña (efecto secundario).

Aparte de eso, hay una gran cantidad de otras causas potenciales que podrían desencadenar el error:

  1. El reloj / hora del servidor no está sincronizado
  2. No autorizado para el acceso sin conexión
  3. Acelerado por Google
  4. Usar tokens de actualización caducados
  5. El usuario ha estado inactivo durante 6 meses
  6. Utilice el correo electrónico del trabajador del servicio en lugar del ID de cliente
  7. Demasiados tokens de acceso en poco tiempo
  8. El SDK de cliente puede estar desactualizado
  9. Token de actualización incorrecto / incompleto

He escrito un artículo breve que resume cada elemento con algunas pautas de depuración para ayudar a encontrar al culpable. Espero eso ayude.


1
Otro escenario es si intenta obtener tokens varias veces del mismo código de autenticación.
knownasilya

Ese era exactamente mi problema, simplemente revocar la aplicación por accidente. Luego tuve que volver a ejecutar refreshToken.php a través de la terminal para generar otro código de autorización y luego reemplazar el refreshToken en todas partes para este ID de cliente.
Robert Sinclair

7

Encontré el mismo problema. Para mí, arreglé esto usando la dirección de correo electrónico (la cadena que termina con ... @ developer.gserviceaccount.com) en lugar de la ID de cliente para el valor del parámetro client_id. El nombre establecido por Google es confuso aquí.


6
Esta es la misma respuesta dada por @aroth más de un año antes
Bryan Ash

3

Mi problema fue que usé esta URL:

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

Cuando debería haber usado esta URL:

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

Se estaba probando una cuenta de servicio que quería acceso sin conexión al motor de almacenamiento .


2

Tuve el mismo mensaje de error 'invalid_grant' y fue porque el authResult ['code'] enviado desde el lado del cliente javascript no se recibió correctamente en el servidor.

Intente devolverlo desde el servidor para ver si es correcto y no una cadena vacía.


1

si está utilizando la biblioteca de escritura, simplemente configure el modo fuera de línea, como bonkydog sugirió aquí está el código:

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

Usando un clientId de Android (sin client_secret) recibía la siguiente respuesta de error:

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

No puedo encontrar ninguna documentación para el campo 'code_verifier' pero descubrí que si lo configura en valores iguales tanto en la autorización como en las solicitudes de token, eliminará este error. No estoy seguro de cuál debería ser el valor deseado o si debería ser seguro. Tiene una longitud mínima (16? Caracteres) pero encontré que la configuración nulltambién funciona.

Estoy usando AppAuth para la solicitud de autorización en mi cliente de Android que tiene una setCodeVerifier()función.

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

Aquí hay una solicitud de token de ejemplo en el 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);
  }
);

Probé y esto funciona con ambos https://www.googleapis.com/oauth2/v4/tokenyhttps://accounts.google.com/o/oauth2/token .

Si está usando en su GoogleAuthorizationCodeTokenRequestlugar:

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


1

Es posible que deba eliminar una respuesta de OAuth obsoleta o no válida.

Crédito: la muestra de node.js google oauth2 dejó de funcionar invalid_grant

Nota : Una respuesta de OAuth también dejará de ser válida si se cambia la contraseña utilizada en la autorización inicial.

Si está en un entorno bash, puede utilizar lo siguiente para eliminar la respuesta obsoleta:

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


1

Hay dos razones principales para el error invalid_grant que debe tener en cuenta antes de la solicitud POST de Refresh Token y Access Token.

  1. El encabezado de la solicitud debe contener "content-type: application / x-www-form-urlencoded"
  2. La carga útil de su solicitud debe tener datos de formulario codificados en URL, no enviar como objeto json.

RFC 6749 OAuth 2.0 definió invalid_grant como: La concesión de autorización proporcionada (por ejemplo, el código de autorización, las credenciales del propietario del recurso) o el token de actualización no es válido, ha caducado, se ha revocado, no coincide con el URI de redireccionamiento utilizado en la solicitud de autorización o se envió a otro cliente. .

Encontré otro buen artículo, aquí encontrarás muchas otras razones para este error.

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


¿Querías publicar dos respuestas casi idénticas? Es posible que desee eliminar este porque el otro tiene una línea adicional.
Blastfurnace


0

Después de considerar y probar todas las otras formas aquí, así es como resolví el problema en nodejs con el googleapismódulo junto con el requestmódulo, que usé para buscar los tokens en lugar del getToken()método proporcionado :

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

Simplemente uso requestpara hacer la solicitud de API a través de HTTP como se describe aquí: 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

Para futuras personas ... Leí muchos artículos y blogs, pero tuve suerte con la solución a continuación ...

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

Este blog describe diferentes casos en los que aparece el error "invalid_grant".

¡¡¡Disfrutar!!!


0

para mí, tuve que asegurarme de que redirect_uricoincida exactamente con el de la consola del desarrollador Authorised redirect URIs, eso lo solucionó para mí, pude depurar y saber cuál era exactamente el problema después de cambiar de https://accounts.google.com/o/oauth2/tokenahttps://www.googleapis.com/oauth2/v4/token

Tengo un error adecuado:

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

0

Tuve este problema después de habilitar una nueva API de servicio en la consola de Google e intentar usar las credenciales creadas anteriormente.

Para solucionar el problema, tuve que volver a la página de credenciales, hacer clic en el nombre de la credencial y hacer clic en "Guardar" nuevamente . Después de eso, pude autenticarme sin problemas.


0

En mi caso, el problema estaba en mi código. Por error, intenté iniciar el cliente 2 veces con los mismos tokens. Si ninguna de las respuestas anteriores ayudó, asegúrese de no generar 2 instancias del cliente.

Mi código antes de la corrección:

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)

tan pronto como lo cambie a (use solo una instancia):

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

solucionó mis problemas con el tipo de subvención.


0

Para mí, el problema fue que tenía varios clientes en mi proyecto y estoy bastante seguro de que esto está perfectamente bien, pero eliminé todos los clientes para ese proyecto y creé uno nuevo y todos comenzaron a trabajar para mí (obtuve esta idea para la ayuda del complemento WP_SMTP foro de soporte) No puedo encontrar ese enlace como referencia


0

Si está desinfectando la entrada del usuario (por ejemplo, $_GET["code"]en php), asegúrese de no reemplazar accidentalmente algo en el código.

La expresión regular que estoy usando es ahora /[^A-Za-z0-9\/-]/


0

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

Primero necesitas 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;

Guarde el token de acceso, el token de actualización y el expire_in en una base de datos. El token de acceso expira después de $ expires_in segundos. De lo que necesita obtener un nuevo token de acceso (y guardarlo en la base de datos) con la siguiente solicitud:

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

Recuerde agregar el dominio redirect_uri a sus dominios en su consola de Google: https://console.cloud.google.com/apis/credentials en la pestaña "OAuth 2.0-Client-IDs". Allí también encontrará su Client-ID y Client-Secret.


0

Existe un tiempo de espera no documentado entre la primera vez que redirige al usuario a la página de autenticación de Google (y obtiene un código) y cuando toma el código devuelto y lo publica en la URL del token. Funciona bien para mí con el client_id proporcionado por Google real en lugar de una "dirección de correo electrónico no documentada". Solo necesitaba comenzar el proceso nuevamente.


0

Si está probando esto en cartero / insomnio y solo está tratando de que funcione, pista: el código de autenticación del servidor (parámetro de código) solo es bueno una vez. Lo que significa que si rellena cualquiera de los otros parámetros en la solicitud y obtiene un 400, deberá usar un nuevo código de autenticación del servidor o simplemente obtendrá otros 400.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.