invalid_grant
연락처 API에 연결하기 위해 Google에서 oAuth 토큰을 가져 오려고 할 때 계속 오류가 발생합니다. 모든 정보가 정확하고 나는 이것을 너무 어리둥절하게 확인했습니다.
이 문제의 원인이 무엇인지 아는 사람이 있습니까? 나는 그것에 대해 다른 클라이언트 ID를 설정하려고 시도했지만 동일한 결과를 얻었습니다. 강제 인증을 시도하는 것을 포함하여 여러 가지 방법을 연결했지만 여전히 동일한 결과를 얻었습니다.
invalid_grant
연락처 API에 연결하기 위해 Google에서 oAuth 토큰을 가져 오려고 할 때 계속 오류가 발생합니다. 모든 정보가 정확하고 나는 이것을 너무 어리둥절하게 확인했습니다.
이 문제의 원인이 무엇인지 아는 사람이 있습니까? 나는 그것에 대해 다른 클라이언트 ID를 설정하려고 시도했지만 동일한 결과를 얻었습니다. 강제 인증을 시도하는 것을 포함하여 여러 가지 방법을 연결했지만 여전히 동일한 결과를 얻었습니다.
답변:
사용자를 OAuth로 보낼 때 "오프라인"액세스를 명시 적으로 요청하지 않았을 때이 문제가 발생했습니다. "이 앱에 항목을 터치 할 수있는 권한을 부여 하시겠습니까?" 페이지.
요청에 access_type = offline을 지정했는지 확인하십시오.
세부 정보 : https://developers.google.com/accounts/docs/OAuth2WebServer#offline
(또한 Google이 2011 년 후반에이 제한을 추가했다고 생각합니다. 이전 토큰이있는 경우 사용자를 권한 페이지로 보내 오프라인 사용을 승인해야합니다.)
access_type
에 offline
이 오류가 여전히 발생합니다.
access_type
bonkydog의 답변에 따라 내 요청에 "오프라인" 을 지정 했음에도 불구하고 동일한 문제가 발생했습니다 . 간단히 말해서 여기에 설명 된 솔루션이 저에게 효과적이라는 것을 알았습니다.
https://groups.google.com/forum/#!topic/google-analytics-data-export-api/4uNaJtquxCs
본질적으로 Google API 콘솔에 OAuth2 클라이언트를 추가하면 Google은 "클라이언트 ID"와 "이메일 주소"를 제공합니다 (클라이언트 유형으로 "웹앱"을 선택한다고 가정). 그리고 Google의 오해의 소지가있는 이름 지정 규칙에도 불구하고 client_id
OAuth2 API에 액세스 할 때 매개 변수 값으로 "이메일 주소"를 보낼 것으로 예상합니다 .
이는 다음 두 URL을 모두 호출 할 때 적용됩니다.
첫 번째 URL에 대한 호출은 "이메일 주소"대신 "클라이언트 ID"로 호출 하면 성공 합니다. 그러나 두 번째 URL에서 전달자 토큰을 가져 오려고 할 때 해당 요청에서 반환 된 코드를 사용하면 작동하지 않습니다. 대신 'Error 400'및 "invalid_grant"메시지가 표시됩니다.
이것은 오래된 질문이지만 여전히 많은 사람들이 직면하고있는 것 같습니다. 우리는 이것을 스스로 추적하는 데 며칠을 보냈습니다.
OAuth2 사양에서 "invalid_grant"는 유효하지 않은 / 만료 된 / 취소 된 토큰 (인증 부여 또는 새로 고침 토큰)과 관련된 모든 오류에 대한 일종의 포괄입니다.
우리에게 문제는 두 가지였습니다.
사용자가 앱에 대한 액세스를 적극적으로 취소했습니다.
당연합니다. 취소 후 12 시간이 지나면 Google이 응답에서 오류 메시지
전송을 중지“error_description” : “Token has been revoked.”
합니다. 오류 메시지가 항상 존재하지 않는다고 가정하기 때문에 오해의 소지가 있습니다. 경우. 앱 권한 페이지 에서 앱에 여전히 액세스 권한 이 있는지 확인할 수 있습니다 .
사용자가 Google 비밀번호를 재설정 / 복구 함
2015 년 12 월, Google은 Google Apps 사용자가 아닌 사용자에 대한 비밀번호 재설정이 모든 사용자의 앱 새로 고침 토큰을 자동으로 취소하도록 기본 동작을 변경 했습니다. 해지시 오류 메시지는 이전 사례와 동일한 규칙을 따르므로 처음 12 시간 동안 만 "error_description"이 표시됩니다. 사용자가 수동으로 액세스를 취소했는지 (의도적) 또는 암호 재설정 (부작용)으로 인해 발생했는지 알 수있는 방법이없는 것 같습니다.
그 외에도 오류를 유발할 수있는 다른 수많은 잠재적 원인이 있습니다.
범인을 찾는 데 도움이되는 몇 가지 디버깅 지침과 함께 각 항목을 요약 한 짧은 기사를 작성했습니다 . 도움이 되었기를 바랍니다.
동일한 오류 메시지 'invalid_grant'가 나타 났는데 이는 클라이언트 측 javascript에서 보낸 authResult [ 'code'] 가 서버에서 올바르게 수신되지 않았기 때문입니다.
서버에서 다시 출력하여 빈 문자열이 아닌 올바른지 확인하십시오.
스크라이브 라이브러리를 사용하는 경우 여기에 제안 된 bonkydog과 같이 오프라인 모드를 설정하면됩니다.
OAuthService service = new ServiceBuilder().provider(Google2Api.class).apiKey(clientId).apiSecret(apiSecret)
.callback(callbackUrl).scope(SCOPE).offline(true)
.build();
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/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();
이것은 어리석은 대답이지만 문제는 내가 저장하지 못한 내 Google 사용자에 대해 활성 oAuth 토큰을 이미 발급 받았다는 사실을 깨닫지 못했다는 것입니다. 이 경우 솔루션은 api 콘솔로 이동하여 클라이언트 암호를 재설정하는 것입니다.
부실 / 잘못된 OAuth 응답을 제거해야 할 수 있습니다.
크레딧 : node.js google oauth2 샘플이 invalid_grant 작동을 중지했습니다.
참고 : 초기 인증에 사용 된 비밀번호가 변경된 경우에도 OAuth 응답이 무효화됩니다.
bash 환경의 경우 다음을 사용하여 부실 응답을 제거 할 수 있습니다.
rm /Users/<username>/.credentials/<authorization.json>
invalid_grant 오류 에는 두 가지 주요 이유 가 있으며 새로 고침 토큰 및 액세스 토큰에 대한 POST 요청 전에주의해야합니다.
RFC 6749 OAuth 2.0 은 invalid_grant 를 다음과 같이 정의 했습니다. 제공된 권한 부여 (예 : 권한 부여 코드, 리소스 소유자 자격 증명) 또는 새로 고침 토큰이 유효하지 않거나 만료되었거나 취소되었거나 권한 요청에 사용 된 리디렉션 URI와 일치하지 않거나 다른 클라이언트에 발급되었습니다. .
다른 좋은 기사를 찾았습니다. 여기 에서이 오류에 대한 다른 많은 이유를 찾을 수 있습니다.
https://blog.timekit.io/google-oauth-invalid-grant-nightmare-and-how-to-fix-it-9f4efaf1da35
이 사이트 console.developers.google.com
이 콘솔 보드는 프로젝트 입력 선서 URL을 선택합니다. oauth 콜백 URL은 oauth 성공시 리디렉션됩니다.
여기에서 다른 모든 방법을 고려하고 시도한 후 제공된 방법 대신 토큰을 가져 오는 데 사용한 모듈 googleapis
과 함께 모듈을 사용하여 nodejs에서 문제를 해결 한 방법은 다음과 같습니다.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);
}
});
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
요청 URL을 다음으로 변경하십시오.
https://www.googleapis.com/oauth2/v4/token
미래의 사람들을 위해 ... 많은 기사와 블로그를 읽었지만 아래의 해결책에 행운이있었습니다 ...
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"오류가 발생하는 다양한 사례를 설명합니다.
즐겨!!!
나를 위해 나는 그것이 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"}
제 경우에는 문제가 제 코드에있었습니다. 실수로 동일한 토큰으로 클라이언트를 두 번 시작하려고했습니다. 위의 답변 중 어느 것도 도움이되지 않으면 클라이언트의 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)
보조금 유형에 대한 내 문제를 해결했습니다.
나에게 문제는 내 프로젝트에 여러 클라이언트가 있었고 이것이 완벽하게 괜찮다고 확신하지만 해당 프로젝트의 모든 클라이언트를 삭제하고 새 클라이언트를 만들고 모두 나를 위해 일하기 시작했습니다 (WP_SMTP 플러그인 도움말 에서이 아이디어를 얻었습니다. 지원 포럼) 참조 용 링크를 찾을 수 없습니다.
사용자 입력을 삭제하는 경우 (예 : $_GET["code"]
php) 실수로 코드에서 내용을 바꾸지 않도록하십시오.
내가 사용하는 정규식은 지금 /[^A-Za-z0-9\/-]/
먼저 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도 찾을 수 있습니다.