Google에서 oAuth 토큰을 가져 오려는 invalid_grant


120

invalid_grant연락처 API에 연결하기 위해 Google에서 oAuth 토큰을 가져 오려고 할 때 계속 오류가 발생합니다. 모든 정보가 정확하고 나는 이것을 너무 어리둥절하게 확인했습니다.

이 문제의 원인이 무엇인지 아는 사람이 있습니까? 나는 그것에 대해 다른 클라이언트 ID를 설정하려고 시도했지만 동일한 결과를 얻었습니다. 강제 인증을 시도하는 것을 포함하여 여러 가지 방법을 연결했지만 여전히 동일한 결과를 얻었습니다.


나를 위해 문제는 내가 .... 다른 하나를 만들어 ... 그리고 문제를 해결 ... 구글 자격 증명 페이지에 있던
costamatrix

답변:


59

사용자를 OAuth로 보낼 때 "오프라인"액세스를 명시 적으로 요청하지 않았을 때이 문제가 발생했습니다. "이 앱에 항목을 터치 할 수있는 권한을 부여 하시겠습니까?" 페이지.

요청에 access_type = offline을 지정했는지 확인하십시오.

세부 정보 : https://developers.google.com/accounts/docs/OAuth2WebServer#offline

(또한 Google이 2011 년 후반에이 제한을 추가했다고 생각합니다. 이전 토큰이있는 경우 사용자를 권한 페이지로 보내 오프라인 사용을 승인해야합니다.)


9
@Adders 동의합니다. 내가 설정 access_typeoffline이 오류가 여전히 발생합니다.
slideshowp2

이것은 받아 들여지는 대답이 아니어야합니다.
Kishan Solanki

developers.google.com/android-publisher/authorization 문서를 살펴보고 구현할 모든 것을 읽어
보세요

70

access_typebonkydog의 답변에 따라 내 요청에 "오프라인" 을 지정 했음에도 불구하고 동일한 문제가 발생했습니다 . 간단히 말해서 여기에 설명 된 솔루션이 저에게 효과적이라는 것을 알았습니다.

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

본질적으로 Google API 콘솔에 OAuth2 클라이언트를 추가하면 Google은 "클라이언트 ID"와 "이메일 주소"를 제공합니다 (클라이언트 유형으로 "웹앱"을 선택한다고 가정). 그리고 Google의 오해의 소지가있는 이름 지정 규칙에도 불구하고 client_idOAuth2 API에 액세스 할 때 매개 변수 값으로 "이메일 주소"를 보낼 것으로 예상합니다 .

이는 다음 두 URL을 모두 호출 할 때 적용됩니다.

첫 번째 URL에 대한 호출은 "이메일 주소"대신 "클라이언트 ID"로 호출 하면 성공 합니다. 그러나 두 번째 URL에서 전달자 토큰을 가져 오려고 할 때 해당 요청에서 반환 된 코드를 사용하면 작동하지 않습니다. 대신 'Error 400'및 "invalid_grant"메시지가 표시됩니다.


66
절대적으로 우스꽝 스럽습니다. 특히 초기 새로 고침 토큰을 받으면 client_id와 함께 작동하는 부분입니다. Google API와 그 문서는 엉망입니다.
Traubenfuchs 2015 년

7
나는이 문제에 대해 너무 많은 시간 동안 머리를 두드렸다. 나는 'client_id'가 'client_id'필드에 대해 예상했던 것이 아니라는 것을 결코 예상하지 못했습니다. 가끔씩 refresh_token을 받고 작동하는 경우를 제외하고. 현재 Google에 대해 가지고있는 단어는 그렇게 말할 수 없습니다.
Justin

6
안녕하세요 .. 여러분이 말하는 "이메일"주소를 찾을 수 없습니다. > - 이것은 내가 내 콘솔에있는 것입니다 pbs.twimg.com/media/CVVcEBWUwAAIiCy.png:large
omarojo

4
그 이메일 주소는 어디에 있습니까? 동일한 문제가 있습니다
Isma Haro 2015

4
Google 문서를 신뢰하지 마십시오. 가장 믿을 수없는 문서와 API는 세계에서 가장 가치있는 회사 인 Google에서 제공합니다. Google API를 사용하려면 많은 시간을 투자해야했습니다. 문제 후 문제가 있었고 다른 종속성 문제 등으로 인해 서로 다른 API에 대한 자체 .Net 라이브러리가 함께 컴파일되지 않았습니다. 이제 코드는 대부분의 사용자에게 잘 작동하지만 일부 사용자의 경우 특별한 이유없이 invalid_grant, invalid_credentials 등을 여전히 얻습니다.
Allen King

56

이것은 오래된 질문이지만 여전히 많은 사람들이 직면하고있는 것 같습니다. 우리는 이것을 스스로 추적하는 데 며칠을 보냈습니다.

OAuth2 사양에서 "invalid_grant"는 유효하지 않은 / 만료 된 / 취소 된 토큰 (인증 부여 또는 새로 고침 토큰)과 관련된 모든 오류에 대한 일종의 포괄입니다.

우리에게 문제는 두 가지였습니다.

  1. 사용자가 앱에 대한 액세스를 적극적으로 취소했습니다.
    당연합니다. 취소 후 12 시간이 지나면 Google이 응답에서 오류 메시지 전송을 중지“error_description” : “Token has been revoked.”
    합니다. 오류 메시지가 항상 존재하지 않는다고 가정하기 때문에 오해의 소지가 있습니다. 경우. 앱 권한 페이지 에서 앱에 여전히 액세스 권한 이 있는지 확인할 수 있습니다 .

  2. 사용자가 Google 비밀번호를 재설정 / 복구 함
    2015 년 12 월, Google은 Google Apps 사용자가 아닌 사용자에 대한 비밀번호 재설정이 모든 사용자의 앱 새로 고침 토큰을 자동으로 취소하도록 기본 동작을 변경 했습니다. 해지시 오류 메시지는 이전 사례와 동일한 규칙을 따르므로 처음 12 시간 동안 만 "error_description"이 표시됩니다. 사용자가 수동으로 액세스를 취소했는지 (의도적) 또는 암호 재설정 (부작용)으로 인해 발생했는지 알 수있는 방법이없는 것 같습니다.

그 외에도 오류를 유발할 수있는 다른 수많은 잠재적 원인이 있습니다.

  1. 서버 시계 / 시간이 동기화되지 않았습니다.
  2. 오프라인 액세스 권한이 없습니다.
  3. Google에 의해 제한됨
  4. 만료 된 새로 고침 토큰 사용
  5. 사용자가 6 개월 동안 비활성 상태였습니다.
  6. 클라이언트 ID 대신 서비스 워커 이메일 사용
  7. 짧은 시간에 너무 많은 액세스 토큰
  8. 클라이언트 SDK가 오래되었을 수 있습니다.
  9. 잘못되었거나 불완전한 새로 고침 토큰

범인을 찾는 데 도움이되는 몇 가지 디버깅 지침과 함께 각 항목을 요약 한 짧은 기사를 작성했습니다 . 도움이 되었기를 바랍니다.


1
또 다른 시나리오는 동일한 인증 코드에서 토큰을 여러 번 얻으려고하는 경우입니다.
knownasilya

그것은 정확히 내 문제였습니다. 단순히 실수로 앱을 취소했습니다. 그런 다음 터미널을 통해 refreshToken.php를 다시 실행하여 다른 인증 코드를 생성 한 다음이 clientID의 모든 위치에서 refreshToken을 교체해야했습니다.
Robert Sinclair

7

같은 문제가 발생했습니다. 필자는 client_id 매개 변수 값에 Client ID 대신 Email Address (... @ developer.gserviceaccount.com으로 끝나는 문자열)를 사용하여이 문제를 해결했습니다. Google에서 설정 한 이름이 여기에서 혼란 스럽습니다.


6
이 @aroth 더 이상의 전년 동기에 의해 주어진 같은 대답이다
브라이언 애쉬

3

내 문제는 다음 URL을 사용했다는 것입니다.

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

이 URL을 사용해야 할 때 :

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

이것은 저장소 엔진에 대한 오프라인 액세스를 원하는 서비스 계정을 테스트하는 것 입니다.


2

동일한 오류 메시지 'invalid_grant'가 나타 났는데 이는 클라이언트 측 javascript에서 보낸 authResult [ 'code'] 가 서버에서 올바르게 수신되지 않았기 때문입니다.

서버에서 다시 출력하여 빈 문자열이 아닌 올바른지 확인하십시오.


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도 작동합니다.

setCodeVerifier()기능 이있는 Android 클라이언트의 인증 요청에 AppAuth를 사용하고 있습니다.

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/tokenhttps://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

invalid_grant 오류 에는 두 가지 주요 이유 가 있으며 새로 고침 토큰 및 액세스 토큰에 대한 POST 요청 전에주의해야합니다.

  1. 요청 헤더에는 "content-type : application / x-www-form-urlencoded"가 포함되어야합니다.
  2. 요청 페이로드는 URL로 인코딩 된 양식 데이터 여야하며 json 객체로 보내지 마십시오.

RFC 6749 OAuth 2.0invalid_grant 를 다음과 같이 정의 했습니다. 제공된 권한 부여 (예 : 권한 부여 코드, 리소스 소유자 자격 증명) 또는 새로 고침 토큰이 유효하지 않거나 만료되었거나 취소되었거나 권한 요청에 사용 된 리디렉션 URI와 일치하지 않거나 다른 클라이언트에 발급되었습니다. .

다른 좋은 기사를 찾았습니다. 여기 에서이 오류에 대한 다른 많은 이유를 찾을 수 있습니다.

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


거의 동일한 두 개의 답변을 게시하려고 하셨나요? 다른 하나에는 추가 줄이 있으므로이 항목을 삭제할 수 있습니다.
Blastfurnace


0

여기에서 다른 모든 방법을 고려하고 시도한 후 제공된 방법 대신 토큰을 가져 오는 데 사용한 모듈 googleapis과 함께 모듈을 사용하여 nodejs에서 문제를 해결 한 방법은 다음과 같습니다.requestgetToken()

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

https://developers.google.com/identity/protocols/OAuth2WebServer#offline에request 설명 된대로 HTTP를 통해 API 요청을 만드는 데 사용 합니다.

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

요청 URL을 다음으로 변경하십시오.

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

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

Google 콘솔에서 새 서비스 API를 활성화하고 이전에 만든 자격 증명을 사용하려고 시도한 후이 문제가 발생했습니다.

문제를 해결하려면 자격 증명 페이지로 돌아가 자격 증명 이름을 클릭 한 다음 "저장"을 다시 클릭 해야했습니다 . 그 후에는 잘 인증 할 수있었습니다.


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;

Google 콘솔의 도메인에 redirect_uri 도메인을 추가해야합니다. 'OAuth 2.0-Client-IDs'탭에서 https://console.cloud.google.com/apis/credentials . 거기에서 Client-ID와 Client-Secret도 찾을 수 있습니다.


0

사용자를 Google 인증 페이지로 처음 리디렉션하고 코드를 반환 할 때와 반환 된 코드를 가져와 토큰 URL에 게시 할 때 사이에 문서화되지 않은 시간 제한이 있습니다. "문서화되지 않은 이메일 주소"가 아니라 실제 Google에서 제공 한 client_id로 잘 작동합니다. 프로세스를 다시 시작해야했습니다.


0

우편 배달부 / 불면증에서 이것을 테스트하고 작동을 시도하는 경우 힌트 : 서버 인증 코드 (코드 매개 변수)는 한 번만 좋습니다. 즉, 요청의 다른 매개 변수를 채우고 400을 반환하면 새 서버 인증 코드를 사용해야합니다. 그렇지 않으면 400 만 더 받게됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.