개요
Google API를 호출하는 사용자별 액세스 토큰을 가져오기 위해 Google에서는 다음과 같은 여러 JavaScript 라이브러리를 제공합니다.
이 가이드에서는 이러한 라이브러리에서 Google ID 서비스 라이브러리로 이전하는 방법을 안내합니다.
이 가이드를 따르면 다음 작업을 할 수 있습니다.
- 지원 중단된 플랫폼 라이브러리를 ID 서비스 라이브러리로 교체하고
- API 클라이언트 라이브러리를 사용하는 경우 지원 중단된
gapi.auth2
모듈과 메서드, 객체를 삭제하고 이에 상응하는 ID 서비스 모듈로 바꿉니다.
Identity Services JavaScript 라이브러리의 변경사항에 관한 설명은 개요 및 사용자 승인 작동 방식을 참고하여 주요 용어와 개념을 검토하세요.
사용자 가입 및 로그인 인증을 확인하려면 Google 로그인에서 이전을 참고하세요.
승인 흐름 확인
사용자 승인 흐름에는 암시적 승인과 승인 코드의 두 가지가 있습니다.
웹 앱을 검토하여 현재 사용 중인 승인 흐름의 유형을 식별합니다.
웹 앱에서 암시적 흐름을 사용하고 있음을 나타내는 표시:
- 웹 앱이 백엔드 플랫폼 없이 순수하게 브라우저 기반입니다.
- 사용자가 있어야 Google API를 호출할 수 있고 앱은 액세스 토큰만 사용하며 갱신 토큰이 필요하지 않습니다.
- 웹 앱이
apis.google.com/js/api.js
를 로드합니다. - 구현은 클라이언트 측 웹 애플리케이션용 OAuth 2.0을 기반으로 합니다.
- 앱이 JavaScript용 Google API 클라이언트 라이브러리에 있는
gapi.client
또는gapi.auth2
모듈을 사용합니다.
웹 앱에서 승인 코드 흐름을 사용하고 있음을 나타내는 표시:
구현은 다음을 기반으로 합니다.
앱은 사용자의 브라우저와 백엔드 플랫폼에서 모두 실행됩니다.
백엔드 플랫폼에서 승인 코드 엔드포인트를 호스팅합니다.
백엔드 플랫폼은 사용자가 로그인하지 않아도 사용자를 대신하여 Google API를 호출합니다(오프라인 모드라고도 함).
갱신 토큰은 백엔드 플랫폼에서 관리하고 저장합니다.
경우에 따라 코드베이스가 두 흐름을 모두 지원할 수 있습니다.
승인 흐름 선택
이전을 시작하기 전에 기존 흐름을 계속할지 아니면 다른 흐름을 채택하는 것이 요구사항에 가장 적합한지 결정해야 합니다.
승인 흐름 선택을 검토하여 두 흐름 간의 주요 차이점과 장단점을 알아보세요.
대부분의 경우 최고 수준의 사용자 보안을 제공하는 승인 코드 흐름을 사용하는 것이 좋습니다. 이 흐름을 구현하면 플랫폼에서 업데이트를 가져와 사용자에게 캘린더, 사진, 구독 등의 주요 변경사항을 알리는 등의 새로운 오프라인 기능을 더 쉽게 추가할 수 있습니다.
아래 선택기를 사용하여 승인 흐름을 선택합니다.
암시적 흐름
사용자가 있는 동안 브라우저 내에서 사용할 액세스 토큰을 가져옵니다.
암시적 흐름 예는 Identity Services로 이전하기 전과 후의 웹 앱을 보여줍니다.
승인 코드 흐름
Google에서 발급한 사용자별 승인 코드는 백엔드 플랫폼으로 전송된 후 액세스 토큰 및 갱신 토큰으로 교환됩니다.
승인 코드 흐름 예는 Identity Services로 이전하기 전과 후의 웹 앱을 보여줍니다.
이 가이드에서는 기존 기능 추가, 삭제, 업데이트 또는 교체에 굵게 표시된 안내를 따릅니다.
브라우저 내 웹 앱 변경사항
이 섹션에서는 Google ID 서비스 JavaScript 라이브러리로 이전할 때 브라우저 내 웹 앱에 적용할 변경사항을 검토합니다.
영향을 받는 코드 식별 및 테스트
디버그 쿠키를 사용하면 영향을 받는 코드를 찾고 지원 중단 후 동작을 테스트하는 데 도움이 됩니다.
크거나 복잡한 앱에서는 gapi.auth2
모듈 지원 중단의 영향을 받는 모든 코드를 찾기가 어려울 수 있습니다. 지원 중단 예정인 기능의 기존 사용을 콘솔에 기록하려면 G_AUTH2_MIGRATION
쿠키의 값을 informational
로 설정합니다. 원하는 경우 콜론 뒤에 키 값을 추가하여 세션 저장소에도 로깅합니다. 로그인하고 사용자 인증 정보를 수신한 후에는 수집된 로그를 검토하거나 나중에 분석할 수 있도록 백엔드로 전송합니다. 예를 들어 informational:showauth2use
는 출처와 URL을 showauth2use
라는 세션 저장소 키에 저장합니다.
gapi.auth2
모듈이 더 이상 로드되지 않을 때 앱 동작을 확인하려면 G_AUTH2_MIGRATION
쿠키의 값을 enforced
로 설정합니다. 이를 통해 시행일 전에 지원 중단 후 동작을 테스트할 수 있습니다.
가능한 G_AUTH2_MIGRATION
쿠키 값은 다음과 같습니다.
enforced
gapi.auth2
모듈을 로드하지 않습니다.informational
지원 중단된 기능 사용을 JS 콘솔에 로깅합니다. 선택적 키 이름이 설정된 경우(informational:key-name
) 세션 저장소에도 로깅합니다.
사용자에게 미치는 영향을 최소화하려면 프로덕션 환경에서 사용하기 전에 개발 및 테스트 중에 먼저 이 쿠키를 로컬에서 설정하는 것이 좋습니다.
라이브러리 및 모듈
gapi.auth2
모듈은 로그인의 사용자 인증과 승인의 암시적 흐름을 관리합니다. 지원 중단된 이 모듈과 그 객체 및 메서드를 Google ID 서비스 라이브러리로 대체하세요.
문서에 포함하여 웹 앱에 Identity Services 라이브러리를 추가합니다.
<script src="https://accounts.google.com/gsi/client" async defer></script>
gapi.load('auth2',
function)
를 사용하여 auth2
모듈을 로드하는 인스턴스를 삭제합니다.
Google ID 서비스 라이브러리는 gapi.auth2
모듈의 사용을 대체합니다.
JavaScript용 Google API 클라이언트 라이브러리의 gapi.client
모듈을 계속 안전하게 사용하고 검색 문서에서 호출 가능한 JS 메서드를 자동으로 생성하고, 여러 API 호출을 일괄 처리하고, CORS 관리 기능을 활용할 수 있습니다.
쿠키
사용자 인증에는 쿠키 사용이 필요하지 않습니다.
사용자 인증에서 쿠키를 사용하는 방법에 관한 자세한 내용은 Google 로그인에서 이전을, 다른 Google 제품 및 서비스에서 쿠키를 사용하는 방법은 Google에서 쿠키를 사용하는 방법을 참고하세요.
사용자 인증 정보
Google ID 서비스는 사용자 인증 및 승인을 두 개의 개별 작업으로 분리하며, 사용자 인증 정보는 별개입니다. 사용자를 식별하는 데 사용되는 ID 토큰은 승인에 사용되는 액세스 토큰과 별도로 반환됩니다.
이러한 변경사항을 보려면 사용자 인증 정보 예시를 참고하세요.
암시적 흐름
승인 흐름에서 사용자 프로필 처리를 삭제하여 사용자 인증과 승인을 분리합니다.
다음 Google 로그인 JavaScript 클라이언트 참조를 삭제합니다.
메서드
GoogleUser.getBasicProfile()
GoogleUser.getId()
승인 코드 흐름
ID 서비스는 브라우저 내 사용자 인증 정보를 ID 토큰과 액세스 토큰으로 분리합니다. 이 변경사항은 백엔드 플랫폼에서 Google OAuth 2.0 엔드포인트를 직접 호출하거나 Google API Node.js 클라이언트와 같이 플랫폼의 보안 서버에서 실행되는 라이브러리를 통해 얻은 사용자 인증 정보에는 적용되지 않습니다.
세션 상태
이전에는 Google 로그인을 사용하여 다음을 통해 사용자 로그인 상태를 관리할 수 있었습니다.
- 사용자의 세션 상태 모니터링을 위한 콜백 핸들러입니다.
- 사용자 Google 계정의 로그인 상태 변경 및 이벤트와 관련된 리스너
개발자는 웹 앱의 로그인 상태와 사용자 세션을 관리해야 합니다.
다음 Google 로그인 JavaScript 클라이언트 참조를 삭제합니다.
객체:
gapi.auth2.SignInOptions
메서드
GoogleAuth.attachClickHandler()
GoogleAuth.isSignedIn()
GoogleAuth.isSignedIn.get()
GoogleAuth.isSignedIn.listen()
GoogleAuth.signIn()
GoogleAuth.signOut()
GoogleAuth.currentUser.get()
GoogleAuth.currentUser.listen()
GoogleUser.isSignedIn()
클라이언트 구성
암시적 또는 승인 코드 흐름의 토큰 클라이언트를 초기화하도록 웹 앱을 업데이트합니다.
다음 Google 로그인 JavaScript 클라이언트 참조를 삭제합니다.
객체:
gapi.auth2.ClientConfig
gapi.auth2.OfflineAccessOptions
메서드
gapi.auth2.getAuthInstance()
GoogleUser.grant()
암시적 흐름
토큰 클라이언트 초기화의 예시에 따라 TokenClientConfig
객체와 initTokenClient()
호출을 추가하여 웹 앱을 구성합니다.
Google 로그인 JavaScript 클라이언트 참조를 Google ID 서비스로 대체합니다.
객체:
TokenClientConfig
에서gapi.auth2.AuthorizeConfig
메서드
google.accounts.oauth2.initTokenClient()
에서gapi.auth2.init()
매개변수:
gapi.auth2.AuthorizeConfig.login_hint
(TokenClientConfig.login_hint
포함)TokenClientConfig.hd
로gapi.auth2.GoogleUser.getHostedDomain()
호출
승인 코드 흐름
Code 클라이언트 초기화의 예에 따라 CodeClientConfig
객체와 initCodeClient()
호출을 추가하여 웹 앱을 구성합니다.
암시적 흐름에서 승인 코드 흐름으로 전환하는 경우:
Google 로그인 JavaScript 클라이언트 참조를 삭제합니다.
객체:
gapi.auth2.AuthorizeConfig
메서드
gapi.auth2.init()
매개변수:
gapi.auth2.AuthorizeConfig.login_hint
gapi.auth2.GoogleUser.getHostedDomain()
토큰 요청
버튼 클릭과 같은 사용자 동작은 암시적 흐름을 통해 사용자의 브라우저에 직접 액세스 토큰이 반환되거나, 사용자별 승인 코드를 액세스 토큰 및 갱신 토큰과 교환한 후 백엔드 플랫폼으로 반환되는 요청을 생성합니다.
암시적 흐름
액세스 토큰은 사용자가 로그인되어 있고 Google과 활성 세션이 있는 동안 브라우저에서 가져와 사용할 수 있습니다. 암시적 모드의 경우 이전에 요청이 있었더라도 액세스 토큰을 요청하려면 사용자 동작이 필요합니다.
Google 로그인 JavaScript 클라이언트 참조를 Google ID 서비스로 대체합니다.
메서드
TokenClient.requestAccessToken()
에서gapi.auth2.authorize()
GoogleUser.reloadAuthResponse()
(TokenClient.requestAccessToken()
포함)
requestAccessToken()
를 호출하여 팝업 UX 흐름을 시작하여 액세스 토큰을 요청하거나 기존 토큰이 만료될 때 새 토큰을 가져오도록 링크 또는 버튼을 추가합니다.
코드베이스를 다음으로 업데이트합니다.
requestAccessToken()
를 사용하여 OAuth 2.0 토큰 흐름을 트리거합니다.requestAccessToken
및OverridableTokenClientConfig
를 사용하여 여러 범위에 대한 하나의 요청을 여러 개의 작은 요청으로 분리하여 증분 승인을 지원합니다.- 기존 토큰이 만료되거나 취소되면 새 토큰을 요청합니다.
여러 범위를 사용하는 경우 한 번에 전부가 아닌 필요한 경우에만 범위에 대한 액세스를 요청하기 위해 코드베이스를 구조적으로 변경해야 할 수 있으며 이를 증분 승인이라고 합니다. 각 요청은 최대한 적은 수의 범위, 가능하면 단일 범위를 포함해야 합니다. 증분 승인을 위해 앱을 업데이트하는 방법에 관한 자세한 내용은 사용자 동의 처리 방법을 참고하세요.
액세스 토큰이 만료되면 gapi.auth2
모듈이 웹 앱의 유효한 새 액세스 토큰을 자동으로 가져옵니다. 사용자 보안을 강화하기 위해 이 자동 토큰 새로고침 프로세스는 Google ID 서비스 라이브러리에서 지원되지 않습니다. 만료된 액세스 토큰을 감지하고 새 토큰을 요청하도록 웹 앱을 업데이트해야 합니다. 자세한 내용은 아래의 토큰 처리 섹션을 참고하세요.
승인 코드 흐름
링크 또는 버튼을 추가하여 requestCode()
를 호출하여 Google에 승인 코드를 요청합니다. 예를 보려면 OAuth 2.0 코드 흐름 트리거를 참고하세요.
만료되거나 취소된 액세스 토큰에 응답하는 방법에 관한 자세한 내용은 아래의 토큰 처리 섹션을 참고하세요.
토큰 처리
만료되거나 취소된 액세스 토큰이 사용될 때 실패한 Google API 호출을 감지하고 유효한 새 액세스 토큰을 요청하는 오류 처리를 추가합니다.
만료되었거나 취소된 액세스 토큰이 사용되면 Google API에서 HTTP 상태 코드 401 Unauthorized
및 invalid_token
오류 메시지를 반환합니다. 예시는 잘못된 토큰 응답을 참고하세요.
만료된 토큰
액세스 토큰은 유효 기간이 짧으며 몇 분 동안만 유효한 경우가 많습니다.
토큰 취소
Google 계정 소유자는 언제든지 이전에 부여한 동의를 취소할 수 있습니다. 그렇게 하면 기존 액세스 토큰과 갱신 토큰이 무효화됩니다. 취소는 revoke()
를 사용하거나 Google 계정을 통해 플랫폼에서 트리거될 수 있습니다.
Google 로그인 JavaScript 클라이언트 참조를 Google ID 서비스로 바꿉니다.
메서드
google.accounts.oauth2.revoke()
에서getAuthInstance().disconnect()
google.accounts.oauth2.revoke()
에서GoogleUser.disconnect()
사용자가 플랫폼에서 계정을 삭제하거나 앱과 데이터를 공유하기 위한 동의를 삭제하려는 경우 revoke
를 호출합니다.
사용자 동의 메시지
웹 앱 또는 백엔드 플랫폼이 액세스 토큰을 요청하면 Google은 사용자에게 동의 대화상자를 표시합니다. Google에서 사용자에게 표시하는 동의 대화상자의 예를 참고하세요.
앱에 액세스 토큰을 발급하기 전에 기존의 활성 Google 세션에서 사용자 동의를 요청하고 결과를 기록해야 합니다. 기존 세션이 아직 설정되지 않은 경우 사용자는 Google 계정에 로그인해야 할 수 있습니다.
사용자 로그인
사용자는 별도의 브라우저 탭에서 Google 계정에 로그인하거나 브라우저 또는 운영체제를 통해 기본적으로 로그인할 수 있습니다. 사용자가 앱을 처음 열 때 Google 계정과 브라우저 간에 활성 세션을 설정하려면 사이트에 Google 계정으로 로그인을 추가하는 것이 좋습니다. 이렇게 하면 다음과 같은 이점이 있습니다.
- 사용자가 로그인해야 하는 횟수를 최소화합니다. 활성 세션이 아직 없는 경우 액세스 토큰을 요청하면 Google 계정 로그인 프로세스가 시작됩니다.
- JWT ID 토큰 사용자 인증 정보
email
필드를CodeClientConfig
또는TokenClientConfig
객체의login_hint
매개변수 값으로 직접 사용합니다. 이는 플랫폼에서 사용자 계정 관리 시스템을 유지하지 않는 경우에 특히 유용합니다. - 플랫폼에서 Google 계정을 조회하고 기존 로컬 사용자 계정과 연결하여 플랫폼에서 중복 계정을 최소화합니다.
- 새 로컬 계정이 생성되면 가입 대화상자와 흐름을 사용자 인증 대화상자와 흐름에서 명확하게 구분할 수 있으므로 필요한 단계 수를 줄이고 이탈률을 개선할 수 있습니다.
로그인 후 액세스 토큰이 발급되기 전에 사용자는 요청된 범위에 대한 애플리케이션에 동의해야 합니다.
토큰 및 동의 응답
동의하면 사용자가 승인하거나 거부한 범위 목록과 함께 액세스 토큰이 반환됩니다.
세분화된 권한을 사용하면 사용자가 개별 범위를 승인하거나 거부할 수 있습니다. 여러 범위에 대한 액세스를 요청할 때 각 범위는 다른 범위와는 별개로 부여되거나 거부됩니다. 앱은 사용자 선택에 따라 개별 범위에 종속되는 기능을 선택적으로 사용 설정합니다.
암시적 흐름
Google 로그인 JavaScript 클라이언트 참조를 Google ID 서비스로 대체합니다.
객체:
TokenClient.TokenResponse
에서gapi.auth2.AuthorizeResponse
TokenClient.TokenResponse
에서gapi.auth2.AuthResponse
메서드
GoogleUser.hasGrantedScopes()
(google.accounts.oauth2.hasGrantedAllScopes()
포함)GoogleUser.getGrantedScopes()
(google.accounts.oauth2.hasGrantedAllScopes()
포함)
Google 로그인 JavaScript 클라이언트 참조를 삭제합니다.
메서드
GoogleUser.getAuthResponse()
이 세부 권한 예시에 따라 hasGrantedAllScopes()
및 hasGrantedAnyScope()
로 웹 앱을 업데이트합니다.
승인 코드 흐름
인증 코드 처리의 안내에 따라 승인 코드 엔드포인트를 백엔드 플랫폼에 업데이트 또는 추가합니다.
코드 모델 사용 가이드에 설명된 단계에 따라 요청을 확인하고 액세스 토큰과 새로고침 토큰을 가져오도록 플랫폼을 업데이트합니다.
증분 승인 안내 및 사용자가 부여한 액세스 범위 검사 안내에 따라 사용자가 승인한 개별 범위를 기반으로 기능을 선택적으로 사용 설정하거나 중지하도록 플랫폼을 업데이트합니다.
암시적 흐름의 예
기존 방식
GAPI 클라이언트 라이브러리
사용자 동의를 위한 팝업 대화상자를 사용하여 브라우저에서 실행되는 자바스크립트용 Google API 클라이언트 라이브러리의 예입니다.
gapi.auth2
모듈은 gapi.client.init()
에 의해 자동으로 로드되고 사용되므로 숨겨집니다.
<!DOCTYPE html>
<html>
<head>
<script src="https://apis.google.com/js/api.js"></script>
<script>
function start() {
gapi.client.init({
'apiKey': 'YOUR_API_KEY',
'clientId': 'YOUR_CLIENT_ID',
'scope': 'https://www.googleapis.com/auth/cloud-translation',
'discoveryDocs': ['https://www.googleapis.com/discovery/v1/apis/translate/v2/rest'],
}).then(function() {
// Execute an API request which is returned as a Promise.
// The method name language.translations.list comes from the API discovery.
return gapi.client.language.translations.list({
q: 'hello world',
source: 'en',
target: 'de',
});
}).then(function(response) {
console.log(response.result.data.translations[0].translatedText);
}, function(reason) {
console.log('Error: ' + reason.result.error.message);
});
};
// Load the JavaScript client library and invoke start afterwards.
gapi.load('client', start);
</script>
</head>
<body>
<div id="results"></div>
</body>
</html>
JS 클라이언트 라이브러리
클라이언트 측 웹 애플리케이션용 OAuth 2.0: 사용자 동의를 위한 팝업 대화상자를 사용하여 브라우저에서 실행됩니다.
gapi.auth2
모듈은 수동으로 로드됩니다.
<!DOCTYPE html>
<html><head></head><body>
<script>
var GoogleAuth;
var SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly';
function handleClientLoad() {
// Load the API's client and auth2 modules.
// Call the initClient function after the modules load.
gapi.load('client:auth2', initClient);
}
function initClient() {
// In practice, your app can retrieve one or more discovery documents.
var discoveryUrl = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest';
// Initialize the gapi.client object, which app uses to make API requests.
// Get API key and client ID from API Console.
// 'scope' field specifies space-delimited list of access scopes.
gapi.client.init({
'apiKey': 'YOUR_API_KEY',
'clientId': 'YOUR_CLIENT_ID',
'discoveryDocs': [discoveryUrl],
'scope': SCOPE
}).then(function () {
GoogleAuth = gapi.auth2.getAuthInstance();
// Listen for sign-in state changes.
GoogleAuth.isSignedIn.listen(updateSigninStatus);
// Handle initial sign-in state. (Determine if user is already signed in.)
var user = GoogleAuth.currentUser.get();
setSigninStatus();
// Call handleAuthClick function when user clicks on
// "Sign In/Authorize" button.
$('#sign-in-or-out-button').click(function() {
handleAuthClick();
});
$('#revoke-access-button').click(function() {
revokeAccess();
});
});
}
function handleAuthClick() {
if (GoogleAuth.isSignedIn.get()) {
// User is authorized and has clicked "Sign out" button.
GoogleAuth.signOut();
} else {
// User is not signed in. Start Google auth flow.
GoogleAuth.signIn();
}
}
function revokeAccess() {
GoogleAuth.disconnect();
}
function setSigninStatus() {
var user = GoogleAuth.currentUser.get();
var isAuthorized = user.hasGrantedScopes(SCOPE);
if (isAuthorized) {
$('#sign-in-or-out-button').html('Sign out');
$('#revoke-access-button').css('display', 'inline-block');
$('#auth-status').html('You are currently signed in and have granted ' +
'access to this app.');
} else {
$('#sign-in-or-out-button').html('Sign In/Authorize');
$('#revoke-access-button').css('display', 'none');
$('#auth-status').html('You have not authorized this app or you are ' +
'signed out.');
}
}
function updateSigninStatus() {
setSigninStatus();
}
</script>
<button id="sign-in-or-out-button"
style="margin-left: 25px">Sign In/Authorize</button>
<button id="revoke-access-button"
style="display: none; margin-left: 25px">Revoke access</button>
<div id="auth-status" style="display: inline; padding-left: 25px"></div><hr>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script async defer src="https://apis.google.com/js/api.js"
onload="this.onload=function(){};handleClientLoad()"
onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
</body></html>
OAuth 2.0 엔드포인트
사용자 동의를 위해 Google로 리디렉션을 사용하여 브라우저에서 실행되는 클라이언트 측 웹 애플리케이션용 OAuth 2.0
이 예에서는 사용자 브라우저에서 Google의 OAuth 2.0 엔드포인트를 직접 호출하며 gapi.auth2
모듈 또는 JavaScript 라이브러리를 사용하지 않습니다.
<!DOCTYPE html>
<html><head></head><body>
<script>
var YOUR_CLIENT_ID = 'REPLACE_THIS_VALUE';
var YOUR_REDIRECT_URI = 'REPLACE_THIS_VALUE';
var fragmentString = location.hash.substring(1);
// Parse query string to see if page request is coming from OAuth 2.0 server.
var params = {};
var regex = /([^&=]+)=([^&]*)/g, m;
while (m = regex.exec(fragmentString)) {
params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
}
if (Object.keys(params).length > 0) {
localStorage.setItem('oauth2-test-params', JSON.stringify(params) );
if (params['state'] && params['state'] == 'try_sample_request') {
trySampleRequest();
}
}
// If there's an access token, try an API request.
// Otherwise, start OAuth 2.0 flow.
function trySampleRequest() {
var params = JSON.parse(localStorage.getItem('oauth2-test-params'));
if (params && params['access_token']) {
var xhr = new XMLHttpRequest();
xhr.open('GET',
'https://www.googleapis.com/drive/v3/about?fields=user&' +
'access_token=' + params['access_token']);
xhr.onreadystatechange = function (e) {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.response);
} else if (xhr.readyState === 4 && xhr.status === 401) {
// Token invalid, so prompt for user permission.
oauth2SignIn();
}
};
xhr.send(null);
} else {
oauth2SignIn();
}
}
/*
* Create form to request access token from Google's OAuth 2.0 server.
*/
function oauth2SignIn() {
// Google's OAuth 2.0 endpoint for requesting an access token
var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
// Create element to open OAuth 2.0 endpoint in new window.
var form = document.createElement('form');
form.setAttribute('method', 'GET'); // Send as a GET request.
form.setAttribute('action', oauth2Endpoint);
// Parameters to pass to OAuth 2.0 endpoint.
var params = {'client_id': YOUR_CLIENT_ID,
'redirect_uri': YOUR_REDIRECT_URI,
'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly',
'state': 'try_sample_request',
'include_granted_scopes': 'true',
'response_type': 'token'};
// Add form parameters as hidden input values.
for (var p in params) {
var input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', p);
input.setAttribute('value', params[p]);
form.appendChild(input);
}
// Add form to page and submit it to open the OAuth 2.0 endpoint.
document.body.appendChild(form);
form.submit();
}
</script>
<button onclick="trySampleRequest();">Try sample request</button>
</body></html>
새로운 방식
GIS만 해당
이 예에서는 토큰 모델을 사용하는 Google ID 서비스 JavaScript 라이브러리와 사용자 동의 팝업 대화상자만 보여줍니다. 클라이언트를 구성하고, 액세스 토큰을 요청 및 가져오고, Google API를 호출하는 데 필요한 최소한의 단계를 보여주기 위해 제공됩니다.
<!DOCTYPE html>
<html>
<head>
<script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
</head>
<body>
<script>
var client;
var access_token;
function initClient() {
client = google.accounts.oauth2.initTokenClient({
client_id: 'YOUR_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/calendar.readonly \
https://www.googleapis.com/auth/contacts.readonly',
callback: (tokenResponse) => {
access_token = tokenResponse.access_token;
},
});
}
function getToken() {
client.requestAccessToken();
}
function revokeToken() {
google.accounts.oauth2.revoke(access_token, () => {console.log('access token revoked')});
}
function loadCalendar() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.googleapis.com/calendar/v3/calendars/primary/events');
xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
xhr.send();
}
</script>
<h1>Google Identity Services Authorization Token model</h1>
<button onclick="getToken();">Get access token</button><br><br>
<button onclick="loadCalendar();">Load Calendar</button><br><br>
<button onclick="revokeToken();">Revoke token</button>
</body>
</html>
GAPI 비동기/대기
이 예에서는 토큰 모델을 사용하여 Google ID 서비스 라이브러리를 추가하고, gapi.auth2
모듈을 삭제하고, JavaScript용 Google API 클라이언트 라이브러리를 사용하여 API를 호출하는 방법을 보여줍니다.
프라미스, async, await는 라이브러리 로드 순서를 적용하고 승인 오류를 포착하여 재시도하는 데 사용됩니다. 유효한 액세스 토큰을 사용할 수 있어야 API가 호출됩니다.
페이지가 처음 로드될 때 액세스 토큰이 없거나 나중에 액세스 토큰이 만료된 경우 사용자는 'Calendar 표시' 버튼을 눌러야 합니다.
<!DOCTYPE html>
<html>
<head></head>
<body>
<h1>GAPI with GIS async/await</h1>
<button id="showEventsBtn" onclick="showEvents();">Show Calendar</button><br><br>
<button id="revokeBtn" onclick="revokeToken();">Revoke access token</button>
<script>
const gapiLoadPromise = new Promise((resolve, reject) => {
gapiLoadOkay = resolve;
gapiLoadFail = reject;
});
const gisLoadPromise = new Promise((resolve, reject) => {
gisLoadOkay = resolve;
gisLoadFail = reject;
});
var tokenClient;
(async () => {
document.getElementById("showEventsBtn").style.visibility="hidden";
document.getElementById("revokeBtn").style.visibility="hidden";
// First, load and initialize the gapi.client
await gapiLoadPromise;
await new Promise((resolve, reject) => {
// NOTE: the 'auth2' module is no longer loaded.
gapi.load('client', {callback: resolve, onerror: reject});
});
await gapi.client.init({
// NOTE: OAuth2 'scope' and 'client_id' parameters have moved to initTokenClient().
})
.then(function() { // Load the Calendar API discovery document.
gapi.client.load('https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest');
});
// Now load the GIS client
await gisLoadPromise;
await new Promise((resolve, reject) => {
try {
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: 'YOUR_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/calendar.readonly',
prompt: 'consent',
callback: '', // defined at request time in await/promise scope.
});
resolve();
} catch (err) {
reject(err);
}
});
document.getElementById("showEventsBtn").style.visibility="visible";
document.getElementById("revokeBtn").style.visibility="visible";
})();
async function getToken(err) {
if (err.result.error.code == 401 || (err.result.error.code == 403) &&
(err.result.error.status == "PERMISSION_DENIED")) {
// The access token is missing, invalid, or expired, prompt for user consent to obtain one.
await new Promise((resolve, reject) => {
try {
// Settle this promise in the response callback for requestAccessToken()
tokenClient.callback = (resp) => {
if (resp.error !== undefined) {
reject(resp);
}
// GIS has automatically updated gapi.client with the newly issued access token.
console.log('gapi.client access token: ' + JSON.stringify(gapi.client.getToken()));
resolve(resp);
};
tokenClient.requestAccessToken();
} catch (err) {
console.log(err)
}
});
} else {
// Errors unrelated to authorization: server errors, exceeding quota, bad requests, and so on.
throw new Error(err);
}
}
function showEvents() {
// Try to fetch a list of Calendar events. If a valid access token is needed,
// prompt to obtain one and then retry the original request.
gapi.client.calendar.events.list({ 'calendarId': 'primary' })
.then(calendarAPIResponse => console.log(JSON.stringify(calendarAPIResponse)))
.catch(err => getToken(err)) // for authorization errors obtain an access token
.then(retry => gapi.client.calendar.events.list({ 'calendarId': 'primary' }))
.then(calendarAPIResponse => console.log(JSON.stringify(calendarAPIResponse)))
.catch(err => console.log(err)); // cancelled by user, timeout, etc.
}
function revokeToken() {
let cred = gapi.client.getToken();
if (cred !== null) {
google.accounts.oauth2.revoke(cred.access_token, () => {console.log('Revoked: ' + cred.access_token)});
gapi.client.setToken('');
}
}
</script>
<script async defer src="https://apis.google.com/js/api.js" onload="gapiLoadOkay()" onerror="gapiLoadFail(event)"></script>
<script async defer src="https://accounts.google.com/gsi/client" onload="gisLoadOkay()" onerror="gisLoadFail(event)"></script>
</body>
</html>
GAPI 콜백
이 예시에서는 토큰 모델을 사용하여 Google ID 서비스 라이브러리를 추가하고, gapi.auth2
모듈을 삭제하고, JavaScript용 Google API 클라이언트 라이브러리를 사용하여 API를 호출하는 방법을 보여줍니다.
변수는 라이브러리 로드 순서를 적용하는 데 사용됩니다. 유효한 액세스 토큰이 반환된 후 콜백 내에서 GAPI가 호출됩니다.
사용자는 페이지가 처음 로드될 때 '캘린더 표시' 버튼을 누르고 캘린더 정보를 새로고침하려면 다시 누릅니다.
<!DOCTYPE html>
<html>
<head>
<script async defer src="https://apis.google.com/js/api.js" onload="gapiLoad()"></script>
<script async defer src="https://accounts.google.com/gsi/client" onload="gisInit()"></script>
</head>
<body>
<h1>GAPI with GIS callbacks</h1>
<button id="showEventsBtn" onclick="showEvents();">Show Calendar</button><br><br>
<button id="revokeBtn" onclick="revokeToken();">Revoke access token</button>
<script>
let tokenClient;
let gapiInited;
let gisInited;
document.getElementById("showEventsBtn").style.visibility="hidden";
document.getElementById("revokeBtn").style.visibility="hidden";
function checkBeforeStart() {
if (gapiInited && gisInited){
// Start only when both gapi and gis are initialized.
document.getElementById("showEventsBtn").style.visibility="visible";
document.getElementById("revokeBtn").style.visibility="visible";
}
}
function gapiInit() {
gapi.client.init({
// NOTE: OAuth2 'scope' and 'client_id' parameters have moved to initTokenClient().
})
.then(function() { // Load the Calendar API discovery document.
gapi.client.load('https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest');
gapiInited = true;
checkBeforeStart();
});
}
function gapiLoad() {
gapi.load('client', gapiInit)
}
function gisInit() {
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: 'YOUR_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/calendar.readonly',
callback: '', // defined at request time
});
gisInited = true;
checkBeforeStart();
}
function showEvents() {
tokenClient.callback = (resp) => {
if (resp.error !== undefined) {
throw(resp);
}
// GIS has automatically updated gapi.client with the newly issued access token.
console.log('gapi.client access token: ' + JSON.stringify(gapi.client.getToken()));
gapi.client.calendar.events.list({ 'calendarId': 'primary' })
.then(calendarAPIResponse => console.log(JSON.stringify(calendarAPIResponse)))
.catch(err => console.log(err));
document.getElementById("showEventsBtn").innerText = "Refresh Calendar";
}
// Conditionally ask users to select the Google Account they'd like to use,
// and explicitly obtain their consent to fetch their Calendar.
// NOTE: To request an access token a user gesture is necessary.
if (gapi.client.getToken() === null) {
// Prompt the user to select a Google Account and asked for consent to share their data
// when establishing a new session.
tokenClient.requestAccessToken({prompt: 'consent'});
} else {
// Skip display of account chooser and consent dialog for an existing session.
tokenClient.requestAccessToken({prompt: ''});
}
}
function revokeToken() {
let cred = gapi.client.getToken();
if (cred !== null) {
google.accounts.oauth2.revoke(cred.access_token, () => {console.log('Revoked: ' + cred.access_token)});
gapi.client.setToken('');
document.getElementById("showEventsBtn").innerText = "Show Calendar";
}
}
</script>
</body>
</html>
승인 코드 흐름 예시
Google Identity Service 라이브러리 팝업 UX는 URL 리디렉션을 사용하여 승인 코드를 백엔드 토큰 엔드포인트로 직접 반환하거나 사용자의 브라우저에서 실행되고 플랫폼에 응답을 프록시하는 JavaScript 콜백 핸들러를 사용할 수 있습니다. 두 경우 모두 백엔드 플랫폼은 OAuth 2.0 흐름을 완료하여 유효한 갱신 및 액세스 토큰을 얻습니다.
기존 방식
서버 측 웹 앱
사용자 동의를 위해 Google로 리디렉션을 사용하는 백엔드 플랫폼에서 실행되는 서버 측 앱용 Google 로그인
<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="https://apis.google.com/js/client:platform.js?onload=start" async defer></script>
<script>
function start() {
gapi.load('auth2', function() {
auth2 = gapi.auth2.init({
client_id: 'YOUR_CLIENT_ID',
api_key: 'YOUR_API_KEY',
discovery_docs: ['https://www.googleapis.com/discovery/v1/apis/translate/v2/rest'],
// Scopes to request in addition to 'profile' and 'email'
scope: 'https://www.googleapis.com/auth/cloud-translation',
});
});
}
function signInCallback(authResult) {
if (authResult['code']) {
console.log("sending AJAX request");
// Send authorization code obtained from Google to backend platform
$.ajax({
type: 'POST',
url: 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URL',
// Always include an X-Requested-With header to protect against CSRF attacks.
headers: {
'X-Requested-With': 'XMLHttpRequest'
},
contentType: 'application/octet-stream; charset=utf-8',
success: function(result) {
console.log(result);
},
processData: false,
data: authResult['code']
});
} else {
console.log('error: failed to obtain authorization code')
}
}
</script>
</head>
<body>
<button id="signinButton">Sign In With Google</button>
<script>
$('#signinButton').click(function() {
// Obtain an authorization code from Google
auth2.grantOfflineAccess().then(signInCallback);
});
</script>
</body>
</html>
리디렉션을 사용하는 HTTP/REST
웹 서버 애플리케이션용 OAuth 2.0 사용을 사용하여 사용자의 브라우저에서 백엔드 플랫폼으로 승인 코드를 전송합니다. 사용자 동의는 사용자의 브라우저를 Google로 리디렉션하여 처리됩니다.
/\*
\* Create form to request access token from Google's OAuth 2.0 server.
\*/
function oauthSignIn() {
// Google's OAuth 2.0 endpoint for requesting an access token
var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
// Create <form> element to submit parameters to OAuth 2.0 endpoint.
var form = document.createElement('form');
form.setAttribute('method', 'GET'); // Send as a GET request.
form.setAttribute('action', oauth2Endpoint);
// Parameters to pass to OAuth 2.0 endpoint.
var params = {'client\_id': 'YOUR_CLIENT_ID',
'redirect\_uri': 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URL',
'response\_type': 'token',
'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly',
'include\_granted\_scopes': 'true',
'state': 'pass-through value'};
// Add form parameters as hidden input values.
for (var p in params) {
var input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', p);
input.setAttribute('value', params[p]);
form.appendChild(input);
}
// Add form to page and submit it to open the OAuth 2.0 endpoint.
document.body.appendChild(form);
form.submit();
}
새로운 방식
GIS 팝업 UX
이 예에서는 승인 코드 모델을 사용하는 Google ID 서비스 JavaScript 라이브러리, 사용자 동의 팝업 대화상자, Google에서 승인 코드를 수신하는 콜백 핸들러만 보여줍니다. 이 그림은 클라이언트를 구성하고, 동의를 얻고, 백엔드 플랫폼으로 승인 코드를 전송하는 데 필요한 최소 단계를 설명하기 위해 제공됩니다.
<!DOCTYPE html>
<html>
<head>
<script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
</head>
<body>
<script>
var client;
function initClient() {
client = google.accounts.oauth2.initCodeClient({
client_id: 'YOUR_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/calendar.readonly',
ux_mode: 'popup',
callback: (response) => {
var code_receiver_uri = 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URI',
// Send auth code to your backend platform
const xhr = new XMLHttpRequest();
xhr.open('POST', code_receiver_uri, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onload = function() {
console.log('Signed in as: ' + xhr.responseText);
};
xhr.send('code=' + response.code);
// After receipt, the code is exchanged for an access token and
// refresh token, and the platform then updates this web app
// running in user's browser with the requested calendar info.
},
});
}
function getAuthCode() {
// Request authorization code and obtain user consent
client.requestCode();
}
</script>
<button onclick="getAuthCode();">Load Your Calendar</button>
</body>
</html>
GIS 리디렉션 UX
승인 코드 모델은 팝업 및 리디렉션 UX 모드를 지원하여 사용자별 승인 코드를 플랫폼에서 호스팅하는 엔드포인트로 전송합니다. 리디렉션 UX 모드는 다음과 같습니다.
<!DOCTYPE html>
<html>
<head>
<script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
</head>
<body>
<script>
var client;
function initClient() {
client = google.accounts.oauth2.initCodeClient({
client_id: 'YOUR_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/calendar.readonly \
https://www.googleapis.com/auth/photoslibrary.readonly',
ux_mode: 'redirect',
redirect_uri: 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URI'
});
}
// Request an access token
function getAuthCode() {
// Request authorization code and obtain user consent
client.requestCode();
}
</script>
<button onclick="getAuthCode();">Load Your Calendar</button>
</body>
</html>
JavaScript 라이브러리
Google ID 서비스는 사용자 인증 및 승인에 사용되는 단일 JavaScript 라이브러리로, 여러 라이브러리와 모듈에 있는 기능을 통합하고 대체합니다.
ID 서비스로 이전할 때 취해야 할 조치:
기존 JS 라이브러리 | 새 JS 라이브러리 | 참고 |
---|---|---|
apis.google.com/js/api.js |
accounts.google.com/gsi/client |
새 라이브러리를 추가하고 암시적 흐름을 따릅니다. |
apis.google.com/js/client.js |
accounts.google.com/gsi/client |
새 라이브러리 및 승인 코드 흐름을 추가합니다. |
라이브러리 빠른 참조
기존 Google 로그인 JavaScript 클라이언트 라이브러리와 신규 Google ID 서비스 라이브러리의 객체 및 메서드 비교와 이전 중에 취해야 할 추가 정보 및 조치가 포함된 참고사항
변경 전 | 신규 | 참고 |
---|---|---|
GoogleAuth 객체 및 관련 메서드: | ||
GoogleAuth.attachClickHandler() | 삭제 | |
GoogleAuth.currentUser.get() | 삭제 | |
GoogleAuth.currentUser.listen() | 삭제 | |
GoogleAuth.disconnect() | google.accounts.oauth2.revoke | 오래된 것을 새 것으로 교체합니다. https://myaccount.google.com/permissions에서도 권한을 취소할 수 있습니다. |
GoogleAuth.grantOfflineAccess() | 삭제: 승인 코드 흐름을 따릅니다. | |
GoogleAuth.isSignedIn.get() | 삭제 | |
GoogleAuth.isSignedIn.listen() | 삭제 | |
GoogleAuth.signIn() | 삭제 | |
GoogleAuth.signOut() | 삭제 | |
GoogleAuth.then() | 삭제 | |
GoogleUser 객체 및 관련 메서드: | ||
GoogleUser.disconnect() | google.accounts.id.revoke | 오래된 것을 새 것으로 교체합니다. https://myaccount.google.com/permissions에서도 권한을 취소할 수 있습니다. |
GoogleUser.getAuthResponse() | requestCode() or requestAccessToken() | 기존을 새 것으로 대체 |
GoogleUser.getBasicProfile() | 삭제를 탭합니다. 대신 ID 토큰을 사용하세요. Google 로그인에서 이전을 참고하세요. | |
GoogleUser.getGrantedScopes() | hasGrantedAnyScope() | 기존을 새 것으로 대체 |
GoogleUser.getHostedDomain() | 삭제 | |
GoogleUser.getId() | 삭제 | |
GoogleUser.grantOfflineAccess() | 삭제: 승인 코드 흐름을 따릅니다. | |
GoogleUser.grant() | 삭제 | |
GoogleUser.hasGrantedScopes() | hasGrantedAnyScope() | 기존을 새 것으로 대체 |
GoogleUser.isSignedIn() | 삭제 | |
GoogleUser.reloadAuthResponse() | requestAccessToken() | 이전 토큰을 삭제하고 만료되거나 취소된 액세스 토큰을 대체하기 위해 새 토큰을 호출합니다. |
gapi.auth2 객체 및 관련 메서드: | ||
gapi.auth2.AuthorizeConfig 객체 | TokenClientConfig 또는 CodeClientConfig | 기존을 새 것으로 대체 |
gapi.auth2.AuthorizeResponse 객체 | 삭제 | |
gapi.auth2.AuthResponse 객체 | 삭제 | |
gapi.auth2.authorize() | requestCode() or requestAccessToken() | 기존을 새 것으로 대체 |
gapi.auth2.ClientConfig() | TokenClientConfig 또는 CodeClientConfig | 기존을 새 것으로 대체 |
gapi.auth2.getAuthInstance() | 삭제 | |
gapi.auth2.init() | initTokenClient() or initCodeClient() | 기존을 새 것으로 대체 |
gapi.auth2.OfflineAccessOptions 객체 | 삭제 | |
gapi.auth2.SignInOptions 객체 | 삭제 | |
gapi.signin2 객체 및 관련 메서드: | ||
gapi.signin2.render() | 삭제를 탭합니다. g_id_signin 요소의 HTML DOM 로드 또는 google.accounts.id.renderButton에 대한 JS 호출은 Google 계정에 대한 사용자 로그인을 트리거합니다. |
사용자 인증 정보 예시
기존 사용자 인증 정보
Google 로그인 플랫폼 라이브러리, JavaScript용 Google API 클라이언트 라이브러리 또는 Google Auth 2.0 엔드포인트의 직접 호출은 단일 응답으로 OAuth 2.0 액세스 토큰과 OpenID Connect ID 토큰을 모두 반환합니다.
access_token
및 id_token
가 모두 포함된 응답 예시:
{
"token_type": "Bearer",
"access_token": "ya29.A0ARrdaM-SmArZaCIh68qXsZSzyeU-8mxhQERHrP2EXtxpUuZ-3oW8IW7a6D2J6lRnZrRj8S6-ZcIl5XVEqnqxq5fuMeDDH_6MZgQ5dgP7moY-yTiKR5kdPm-LkuPM-mOtUsylWPd1wpRmvw_AGOZ1UUCa6UD5Hg",
"scope": "https://www.googleapis.com/auth/calendar.readonly",
"login_hint": "AJDLj6I2d1RH77cgpe__DdEree1zxHjZJr4Q7yOisoumTZUmo5W2ZmVFHyAomUYzLkrluG-hqt4RnNxrPhArd5y6p8kzO0t8xIfMAe6yhztt6v2E-_Bb4Ec3GLFKikHSXNh5bI-gPrsI",
"expires_in": 3599,
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjkzNDFhYmM0MDkyYjZmYzAzOGU0MDNjOTEwMjJkZDNlNDQ1MzliNTYiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiYXpwIjoiNTM4MzQ0NjUzMjU1LTc1OGM1aDVpc2M0NXZnazI3ZDhoOGRlYWJvdnBnNnRvLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiYXVkIjoiNTM4MzQ0NjUzMjU1LTc1OGM1aDVpc2M0NXZnazI3ZDhoOGRlYWJvdnBnNnRvLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTE3NzI2NDMxNjUxOTQzNjk4NjAwIiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJkYWJyaWFuQGdvb2dsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6IkJBSW55TjN2MS1ZejNLQnJUMVo0ckEiLCJuYW1lIjoiQnJpYW4gRGF1Z2hlcnR5IiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hLS9BT2gxNEdnenAyTXNGRGZvbVdMX3VDemRYUWNzeVM3ZGtxTE5ybk90S0QzVXNRPXM5Ni1jIiwiZ2l2ZW5fbmFtZSI6IkJyaWFuIiwiZmFtaWx5X25hbWUiOiJEYXVnaGVydHkiLCJsb2NhbGUiOiJlbiIsImlhdCI6MTYzODk5MTYzOCwiZXhwIjoxNjM4OTk1MjM4LCJqdGkiOiI5YmRkZjE1YWFiNzE2ZDhjYmJmNDYwMmM1YWM3YzViN2VhMDQ5OTA5In0.K3EA-3Adw5HA7O8nJVCsX1HmGWxWzYk3P7ViVBb4H4BoT2-HIgxKlx1mi6jSxIUJGEekjw9MC-nL1B9Asgv1vXTMgoGaNna0UoEHYitySI23E5jaMkExkTSLtxI-ih2tJrA2ggfA9Ekj-JFiMc6MuJnwcfBTlsYWRcZOYVw3QpdTZ_VYfhUu-yERAElZCjaAyEXLtVQegRe-ymScra3r9S92TA33ylMb3WDTlfmDpWL0CDdDzby2asXYpl6GQ7SdSj64s49Yw6mdGELZn5WoJqG7Zr2KwIGXJuSxEo-wGbzxNK-mKAiABcFpYP4KHPEUgYyz3n9Vqn2Tfrgp-g65BQ",
"session_state": {
"extraQueryParams": {
"authuser": "0"
}
},
"first_issued_at": 1638991637982,
"expires_at": 1638995236982,
"idpId": "google"
}
Google ID 서비스 사용자 인증 정보
Google ID 서비스 라이브러리는 다음을 반환합니다.
승인에 사용되는 액세스 토큰:
{ "access_token": "ya29.A0ARrdaM_LWSO-uckLj7IJVNSfnUityT0Xj-UCCrGxFQdxmLiWuAosnAKMVQ2Z0LLqeZdeJii3TgULp6hR_PJxnInBOl8UoUwWoqsrGQ7-swxgy97E8_hnzfhrOWyQBmH6zs0_sUCzwzhEr_FAVqf92sZZHphr0g", "token_type": "Bearer", "expires_in": 3599, "scope": "https://www.googleapis.com/auth/calendar.readonly" }
또는 인증에 사용되는 ID 토큰:
{ "clientId": "538344653255-758c5h5isc45vgk27d8h8deabovpg6to.apps.googleusercontent.com", "credential": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxODkyZWI0OWQ3ZWY5YWRmOGIyZTE0YzA1Y2EwZDAzMjcxNGEyMzciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJuYmYiOjE2MzkxNTcyNjQsImF1ZCI6IjUzODM0NDY1MzI1NS03NThjNWg1aXNjNDV2Z2syN2Q4aDhkZWFib3ZwZzZ0by5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjExNzcyNjQzMTY1MTk0MzY5ODYwMCIsIm5vbmNlIjoiZm9vYmFyIiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJkYWJyaWFuQGdvb2dsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXpwIjoiNTM4MzQ0NjUzMjU1LTc1OGM1aDVpc2M0NXZnazI3ZDhoOGRlYWJvdnBnNnRvLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwibmFtZSI6IkJyaWFuIERhdWdoZXJ0eSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQU9oMTRHZ3pwMk1zRkRmb21XTF91Q3pkWFFjc3lTN2RrcUxOcm5PdEtEM1VzUT1zOTYtYyIsImdpdmVuX25hbWUiOiJCcmlhbiIsImZhbWlseV9uYW1lIjoiRGF1Z2hlcnR5IiwiaWF0IjoxNjM5MTU3NTY0LCJleHAiOjE2MzkxNjExNjQsImp0aSI6IjRiOTVkYjAyZjU4NDczMmUxZGJkOTY2NWJiMWYzY2VhYzgyMmI0NjUifQ.Cr-AgMsLFeLurnqyGpw0hSomjOCU4S3cU669Hyi4VsbqnAV11zc_z73o6ahe9Nqc26kPVCNRGSqYrDZPfRyTnV6g1PIgc4Zvl-JBuy6O9HhClAK1HhMwh1FpgeYwXqrng1tifmuotuLQnZAiQJM73Gl-J_6s86Buo_1AIx5YAKCucYDUYYdXBIHLxrbALsA5W6pZCqqkMbqpTWteix-G5Q5T8LNsfqIu_uMBUGceqZWFJALhS9ieaDqoxhIqpx_89QAr1YlGu_UO6R6FYl0wDT-nzjyeF5tonSs3FHN0iNIiR3AMOHZu7KUwZaUdHg4eYkU-sQ01QNY_11keHROCRQ", "select_by": "user" }
잘못된 토큰 응답
만료되거나 취소되었거나 잘못된 액세스 토큰을 사용하여 API 요청을 시도할 때 Google에서 반환하는 응답의 예는 다음과 같습니다.
HTTP 응답 헤더
www-authenticate: Bearer realm="https://accounts.google.com/", error="invalid_token"
응답 본문
{
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"errors": [
{
"message": "Invalid Credentials",
"domain": "global",
"reason": "authError",
"location": "Authorization",
"locationType": "header"
}
],
"status": "UNAUTHENTICATED"
}
}