invalid_grant пытается получить токен oAuth от Google


121

Я все время получаю сообщение invalid_grantоб ошибке при попытке получить токен oAuth от Google для подключения к их контактам api. Вся информация верна, и я трижды проверил это, так что в тупике.

Кто-нибудь знает, что может вызвать эту проблему? Я попытался настроить для него другой идентификатор клиента, но получил тот же результат, я пробовал подключить много разных способов, включая принудительную аутентификацию, но все равно результат тот же.


для меня проблема была на странице учетных данных google ... я создал еще одну ... и решил проблему ....
costamatrix

Ответы:


59

Я столкнулся с этой проблемой, когда я явно не запрашивал «автономный» доступ при отправке пользователя на OAuth: «Вы хотите дать этому приложению разрешение прикасаться к вашим материалам?» стр.

Убедитесь, что вы указали access_type = offline в своем запросе.

Подробности здесь: https://developers.google.com/accounts/docs/OAuth2WebServer#offline

(Также: я думаю, что Google добавил это ограничение в конце 2011 года. Если у вас есть старые токены, полученные ранее, вам нужно будет отправить своих пользователей на страницу разрешений, чтобы разрешить автономное использование.)


9
@Adders Я согласен. Я поставил access_typeв offline, эта ошибка все еще происходит.
slideshowp2

Это не должен быть общепринятым ответом.
Кишан Соланки,

Просмотрите эту документацию developers.google.com/android-publisher/authorization и прочтите все, чтобы реализовать
Кишан Соланки,

70

Я столкнулся с этой же проблемой, несмотря на то, что access_typeв моем запросе указывал "офлайн" в соответствии с ответом bonkydog. Короче говоря, я обнаружил, что описанное здесь решение сработало для меня:

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

По сути, когда вы добавляете клиент OAuth2 в консоль Google API, Google предоставит вам «идентификатор клиента» и «адрес электронной почты» (при условии, что вы выбрали «webapp» в качестве типа клиента). И, несмотря на вводящие в заблуждение соглашения об именах Google, они ожидают, что вы отправите «адрес электронной почты» в качестве значения client_idпараметра при доступе к их API OAuth2.

Это применимо при вызове обоих этих URL:

Обратите внимание, что вызов первого URL-адреса будет успешным, если вы вызовете его со своим «Идентификатором клиента» вместо своего «адреса электронной почты». Однако использование кода, возвращенного из этого запроса, не будет работать при попытке получить токен носителя со второго URL-адреса. Вместо этого вы получите сообщение «Ошибка 400» и «invalid_grant».


66
Абсолютно смешно: особенно та часть, где он работает с client_id, если вы получаете токен начального обновления. API Googles и их документация - это беспорядок.
Traubenfuchs,

7
Я столько часов бился головой об этот вопрос. Я никогда не ожидал, что client_id будет не таким, как ожидалось для поля client_id. За исключением случаев, когда вы получаете refresh_token, и он действительно работает. Уверен, что слова, которые у меня есть для Google, нельзя сказать на SO.
Джастин

6
Привет .. Я не могу найти тот "электронный" адрес, о котором вы, ребята, говорите. это то, что у меня в консоли -> pbs.twimg.com/media/CVVcEBWUwAAIiCy.png:large
omarojo

4
Где этот адрес электронной почты? У меня такая же проблема
Исма Харо

4
Никогда не доверяйте документации Google. Самая дурацкая документация и API-интерфейсы исходят от Google, самой дорогой компании в мире. Мне пришлось потратить бесчисленное количество часов, чтобы использовать Google API. Были проблемы после проблем, а затем их собственные библиотеки .Net для разных API не компилировались вместе из-за разных проблем с зависимостями и всего. Код теперь хорошо работает для большинства пользователей, но для некоторых пользователей я все еще получаю invalid_grant, invalid_credentials и т. Д. Без особых причин.
Аллен Кинг

56

Хотя это старый вопрос, похоже, что многие до сих пор с ним сталкиваются - мы потратили дни напролет, отслеживая его сами.

В спецификации OAuth2 «invalid_grant» является своего рода универсальным средством для всех ошибок, связанных с недействительными / просроченными / отозванными токенами (разрешение авторизации или токен обновления).

Для нас проблема была двоякой:

  1. Пользователь активно отозвал доступ к нашему приложению.
    Имеет смысл, но поймите следующее: через 12 часов после отзыва Google перестает отправлять сообщение об ошибке в своем ответе: “error_description” : “Token has been revoked.”
    это вводит в заблуждение, потому что вы предполагаете, что сообщение об ошибке присутствует постоянно, а это не дело. Вы можете проверить, есть ли у вашего приложения доступ, на странице разрешений приложений .

  2. Пользователь сбросил / восстановил свой пароль Google.
    В декабре 2015 года Google изменил свое поведение по умолчанию, так что при сбросе пароля для пользователей, не использующих Google Apps, автоматически отменяются все токены обновления приложений пользователя. При отзыве сообщение об ошибке подчиняется тому же правилу, что и предыдущий случай, поэтому вы получите "error_description" только в первые 12 часов. Кажется, нет никакого способа узнать, отозвал ли пользователь вручную доступ (намеренно) или это произошло из-за сброса пароля (побочный эффект).

Помимо этого, существует множество других потенциальных причин, которые могут вызвать ошибку:

  1. Серверные часы / время не синхронизированы
  2. Не авторизован для офлайн-доступа
  3. Дросселируется Google
  4. Использование просроченных токенов обновления
  5. Пользователь был неактивен 6 месяцев
  6. Используйте электронную почту сервисного работника вместо идентификатора клиента
  7. Слишком много токенов доступа за короткое время
  8. Клиентский SDK может быть устаревшим
  9. Неверный / неполный токен обновления

Я написал небольшую статью, в которой резюмирует каждый элемент с некоторыми рекомендациями по отладке, чтобы помочь найти виновника. Надеюсь, поможет.


1
Другой сценарий: вы пытаетесь получить токены несколько раз из одного и того же кода авторизации.
knownasilya

Это была моя проблема, просто случайно отозвав приложение. Затем пришлось повторно запустить refreshToken.php через терминал, чтобы сгенерировать другой код авторизации, а затем заменить refreshToken везде для этого clientID.
Роберт Синклер,

7

Я столкнулся с той же проблемой. Для меня я исправил это, используя адрес электронной почты (строка, которая заканчивается на ... @ developer.gserviceaccount.com) вместо идентификатора клиента для значения параметра client_id. Названия, установленные Google, сбивают с толку.


6
Это тот же ответ, который дал @aroth более года назад
Брайан Эш,

3

Моя проблема заключалась в том, что я использовал этот URL:

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

Когда мне следовало использовать этот URL:

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

Это было тестирование учетной записи службы, которая хотела автономный доступ к ядру хранилища .


2

У меня было такое же сообщение об ошибке «invalid_grant», потому что authResult ['code'], отправленный с клиентской стороны javascript, не был правильно получен на сервере.

Попробуйте вывести его обратно с сервера, чтобы убедиться, что он правильный, а не пустая строка.


1

если вы используете библиотеку писцов, просто настройте автономный режим, например, 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также работает.

Я использую AppAuth для запроса авторизации в своем Android-клиенте, который имеет setCodeVerifier()функцию.

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/tokenи https://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

Это глупый ответ, но проблема для меня заключалась в том, что я не понял, что мне уже был выдан активный токен oAuth для моего пользователя Google, который мне не удалось сохранить. Решение в этом случае - перейти в консоль api и сбросить секрет клиента.

На этот счет есть множество других ответов на SO, например, Reset Client Secret OAuth2 - Нужно ли клиентам повторно предоставлять доступ?


1

Возможно, вам придется удалить устаревший / недействительный ответ OAuth.

Кредит: образец node.js google oauth2 перестал работать invalid_grant

Примечание . Ответ OAuth также станет недействительным, если пароль, использованный при первоначальной авторизации, был изменен.

В среде bash для удаления устаревшего ответа можно использовать следующее:

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


1

Есть две основные причины ошибки invalid_grant, о которых вы должны позаботиться до запроса POST для токена обновления и токена доступа.

  1. Заголовок запроса должен содержать «content-type: application / x-www-form-urlencoded».
  2. Полезные данные вашего запроса должны быть закодированы в URL-адресе данных формы, а не отправляться как объект json.

RFC 6749 OAuth 2.0 определил invalid_grant как: предоставленное разрешение авторизации (например, код авторизации, учетные данные владельца ресурса) или токен обновления недействителен, истек, отозван, не соответствует URI перенаправления, используемому в запросе авторизации, или был выдан другому клиенту ,

Я нашел еще одну хорошую статью, здесь вы найдете много других причин этой ошибки.

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


Вы хотели опубликовать два почти идентичных ответа? Вы можете удалить этот, потому что в другом есть дополнительная строка.
Blastfurnace

1

Если вы тестируете это в почтальоне / бессоннице и просто пытаетесь заставить его работать, подсказка: код аутентификации сервера (параметр кода) годен только один раз. Это означает, что если вы добавите какие-либо другие параметры в запрос и вернете 400, вам нужно будет использовать новый код аутентификации сервера, иначе вы просто получите еще 400.


0

на этом сайте console.developers.google.com

эта консольная плата выберите ваш проект, введите адрес клятвы. URL-адрес обратного вызова oauth будет перенаправлен при успехе oauth


0

Рассмотрев и опробовав все остальные способы, вот как я решил проблему в nodejs с помощью googleapisмодуля в сочетании с requestмодулем, который я использовал для получения токенов вместо предоставленного getToken()метода:

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для выполнения запроса api через HTTP, как описано здесь: 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

Для будущих людей ... Я читал много статей и блогов, но мне повезло с решением ниже ...

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

это блоге описаны различные случаи возникновения ошибки «invalid_grant».

Наслаждаться!!!


0

для меня я должен был убедиться, что 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

У меня возникла эта проблема после включения нового API службы в консоли Google и попытки использовать ранее созданные учетные данные.

Чтобы решить эту проблему, мне пришлось вернуться на страницу учетных данных, щелкнув имя учетных данных и снова нажав «Сохранить» . После этого я мог нормально пройти аутентификацию.


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)

как только я изменю его на (используйте только один экземпляр):

@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

Для меня проблема заключалась в том, что у меня было несколько клиентов в моем проекте, и я почти уверен, что это нормально, но я удалил всех клиентов для этого проекта и создал новый, и все начало работать на меня (эта идея появилась из справки по плагину WP_SMTP форум поддержки) Я не могу найти эту ссылку для справки


0

Если вы дезинфицируете пользовательский ввод (например, $_GET["code"]в php), убедитесь, что вы случайно не заменили что-то в коде.

Регулярное выражение, которое я использую, сейчас /[^A-Za-z0-9\/-]/


0

Посмотрите на это https://dev.to/risafj/beginner-s-guide-to-oauth-understanding-access-tokens-and-authorization-codes-2988

Сначала вам нужен 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;

Сохраните токен доступа, токен обновления и expire_in в базе данных. Срок действия токена доступа истекает через $ expires_in секунд. Затем вам нужно получить новый токен доступа (и сохранить его в базе данных) со следующим запросом:

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

Не забывайте добавить домен redirect_uri к своим доменам в консоли Google: https://console.cloud.google.com/apis/credentials на вкладке «OAuth 2.0-Client-IDs». Там вы также найдете свой Client-ID и Client-Secret.


0

Существует недокументированный тайм-аут между тем, когда вы впервые перенаправляете пользователя на страницу аутентификации Google (и получаете обратно код), и когда вы берете возвращенный код и отправляете его по URL-адресу токена. Он отлично работает для меня с фактическим предоставленным Google client_id, а не с «недокументированным адресом электронной почты». Мне просто нужно было начать процесс заново.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.