invalid_grant намагається отримати маркер oAuth від google


120

Я постійно отримую 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, ця помилка все ще відбувається.
слайдшоу2

Це не повинно бути прийнятою відповіддю.
Кішань Соланкі


70

Я зіткнувся з цією самою проблемою, незважаючи access_typeна те, що вказав "офлайн" у своєму запиті відповідно до відповіді bonkydog Довгий короткий опис я виявив, що описане тут рішення працювало на мене:

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

По суті, коли ви додасте клієнт OAuth2 в консоль API Google, 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 на даний момент, не можна сказати на ТАК.
Джастін

6
Привіт. Не можу знайти ту адресу електронної пошти, про яку ви говорите. це те, що у мене в консолі -> pbs.twimg.com/media/CVVcEBWUwAAIiCy.png:large
omarojo

4
Де ця адреса електронної пошти? У мене те саме питання
Ісма Харо

4
Ніколи не довіряйте документації Google. Найяскравіша документація та API надходять від Google, найціннішої компанії в світі. Мені довелося витратити незліченну кількість годин, щоб користуватися API Google. Виникли проблеми після випусків, а потім власні бібліотеки .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, автоматично відкликало б усі маркери оновлення додатків користувачів. Після відкликання повідомлення про помилку дотримується того самого правила, що і раніше, тому "опис помилки" ви отримаєте лише протягом перших 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 ["коду"] з боку клієнта 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 та скинути клієнтську таємницю.

На цю тему існує багато інших відповідей, наприклад Reset Client Secret OAuth2 - Чи потрібно клієнтам повторно надавати доступ?


1

Можливо, вам доведеться видалити несвіжу / недійсну відповідь OAuth.

Кредит: зразок node.js google oauth2 перестав працювати invalid_grant

Примітка : Відповідь OAuth також стане недійсною, якщо пароль, використаний у початковій авторизації, був змінений.

Якщо ви знаходитесь в баш-середовищі, ви можете використовувати наступне, щоб видалити несвіжу реакцію:

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


Ви мали на увазі опублікувати дві майже однакові відповіді? Ви можете видалити цю, оскільки в другому є додатковий рядок.
Доменна піч


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 рази тими ж лексемами. Якщо жоден з вищезазначених відповідей не допомог переконатися, що ви не генеруєте два екземпляри клієнта.

Мій код перед виправленням:

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". Там ви також знайдете свій ідентифікатор клієнта та клієнт-секрет.


0

Існує незадокументований час очікування між тим, коли ви вперше перенаправляєте користувача на сторінку аутентифікації google (і повертаєте код назад), і коли ви берете повернутий код і розміщуєте його в URL-адресі маркера. Для мене це добре працює з фактично наданим google client_id на відміну від "недокументованої адреси електронної пошти". Мені просто потрібно було запустити процес заново.


0

Якщо ви тестуєте це на листоноші / безсонні і просто намагаєтесь його запрацювати, підкажіть: код автентифікації сервера (параметр коду) лише один раз корисний Це означає, що якщо ви заповнюєте будь-який з інших параметрів у запиті та отримуєте 400, вам потрібно буде використовувати новий код автентифікації сервера або просто ви отримаєте ще 400.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.