Google ID 서비스로 이전

개요

Google API를 호출하기 위한 사용자별 액세스 토큰을 얻을 수 있도록 Google에서는 다음과 같은 여러 JavaScript 라이브러리를 제공합니다.

이 가이드에서는 이러한 라이브러리에서 Google Identity Services 라이브러리로 이전하는 방법을 안내합니다.

이 가이드를 따르면 다음과 같은 작업을 수행할 수 있습니다.

  • 지원 중단된 플랫폼 라이브러리를 Identity Services 라이브러리로 대체
  • API 클라이언트 라이브러리를 사용하는 경우 지원 중단된 gapi.auth2 모듈, 해당 메서드, 객체를 삭제하고 이에 상응하는 ID 서비스 항목으로 바꿉니다.

ID 서비스 자바스크립트 라이브러리의 변경사항에 대한 설명은 개요사용자 승인 작동 방식을 읽고 주요 용어와 개념을 검토하세요.

사용자 가입 및 로그인을 위한 인증을 찾는 경우 대신 Google 로그인에서 이전을 참조하세요.

승인 흐름 확인

사용자 승인 흐름에는 암시적 코드와 승인 코드, 두 가지가 있습니다.

웹 앱을 검토하여 현재 사용 중인 승인 흐름의 유형을 확인합니다.

웹 앱이 암시적 흐름을 사용하고 있음을 나타냅니다.

웹 앱에서 승인 코드 흐름을 사용하고 있음을 나타냅니다.

  • 구현은 다음을 기반으로 합니다.

  • 앱은 사용자의 브라우저와 백엔드 플랫폼 모두에서 실행됩니다.

  • 백엔드 플랫폼은 승인 코드 엔드포인트를 호스팅합니다.

  • 백엔드 플랫폼은 사용자 대신 사용자 대신 Google API를 호출합니다(오프라인 모드라고도 함).

  • 갱신 토큰은 백엔드 플랫폼에서 관리하고 저장합니다.

경우에 따라 코드베이스가 두 흐름을 모두 지원할 수도 있습니다.

승인 흐름 선택

마이그레이션을 시작하기 전에 기존 흐름을 계속할지 아니면 다른 흐름을 채택하는 것이 니즈에 가장 적합한지 확인해야 합니다.

승인 흐름 선택을 검토하여 두 흐름의 주요 차이점과 장단점을 이해합니다.

대부분의 경우 가장 높은 수준의 사용자 보안을 제공하므로 승인 코드 흐름이 권장됩니다. 또한 이 흐름을 구현하면 업데이트를 가져와 사용자에게 캘린더, 사진, 정기 결제 등의 중요한 변경사항을 알리는 등의 새로운 오프라인 기능을 플랫폼에서 더 쉽게 추가할 수 있습니다.

아래의 선택기를 사용하여 승인 과정을 선택하세요.

암시적 흐름

사용자가 있는 동안 브라우저 내에서 사용할 액세스 토큰을 가져옵니다.

암시적 흐름 예는 ID 서비스로 마이그레이션하기 전후의 웹 앱을 보여줍니다.

승인 코드 흐름

Google에서 발급한 사용자별 승인 코드가 백엔드 플랫폼으로 전달되고, 여기에서 액세스 토큰 및 갱신 토큰으로 교환됩니다.

승인 코드 흐름 예시는 ID 서비스로 이전하기 전후의 웹 앱을 보여줍니다.

이 가이드에서는 굵게 표시된 안내에 따라 기존 기능 추가, 삭제, 업데이트 또는 교체를 수행합니다.

브라우저 내 웹 앱의 변경사항

이 섹션에서는 Google ID 서비스 JavaScript 라이브러리로 이전할 때 브라우저 내 웹 앱에 적용하는 변경사항을 검토합니다.

영향을 받은 코드 식별 및 테스트

디버그 쿠키를 사용하면 영향을 받은 코드를 찾고 지원 중단 후 동작을 테스트할 수 있습니다.

크거나 복잡한 앱에서는 gapi.auth2 모듈 지원 중단의 영향을 받는 모든 코드를 찾기 어려울 수 있습니다. 곧 지원 중단될 기능의 기존 사용을 콘솔에 로깅하려면 G_AUTH2_MIGRATION 쿠키 값을 informational로 설정합니다. 선택적으로 콜론 다음에 키 값을 추가하여 세션 저장소에도 로깅합니다. 로그인하고 사용자 인증 정보를 수신한 후에는 수집된 로그를 검토하거나 나중에 분석할 수 있도록 백엔드로 전송합니다. 예를 들어 informational:showauth2useshowauth2use라는 세션 스토리지 키에 출처와 URL을 저장합니다.

gapi.auth2 모듈이 더 이상 로드되지 않을 때 앱 동작을 확인하려면 G_AUTH2_MIGRATION 쿠키 값을 enforced로 설정합니다. 이를 통해 시행일 전에 지원 중단 이후의 동작을 테스트할 수 있습니다.

가능한 G_AUTH2_MIGRATION 쿠키 값은 다음과 같습니다.

  • enforced gapi.auth2 모듈을 로드하지 않습니다.
  • informational 지원 중단된 기능 사용을 JS 콘솔에 로깅합니다. 또한 선택적 키 이름 informational:key-name가 설정된 경우 세션 저장소에 로깅합니다.

사용자에게 미치는 영향을 최소화하려면 프로덕션 환경에서 사용하기 전에 먼저 개발 및 테스트 중에 이 쿠키를 로컬에서 설정하는 것이 좋습니다.

라이브러리 및 모듈

gapi.auth2 모듈은 로그인에 대한 사용자 인증과 승인을 위한 암시적 흐름을 관리하며, 지원 중단된 이 모듈과 모듈의 객체와 메서드를 Google ID 서비스 라이브러리로 바꿉니다.

ID 서비스 라이브러리를 문서에 포함하여 웹 앱에 추가합니다.

<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 로그인 자바스크립트 클라이언트 참조삭제합니다.

방법

  • GoogleUser.getBasicProfile()
  • GoogleUser.getId()

승인 코드 흐름

ID 서비스는 브라우저 내 사용자 인증 정보를 ID 토큰과 액세스 토큰으로 구분합니다. 이 변경사항은 백엔드 플랫폼에서 Google OAuth 2.0 엔드포인트를 직접 호출하거나 Google API Node.js 클라이언트와 같이 플랫폼의 보안 서버에서 실행되는 라이브러리를 통해 얻은 사용자 인증 정보에 적용되지 않습니다.

세션 상태

이전에는 Google 로그인을 통해 다음을 사용하여 사용자 로그인 상태를 관리할 수 있었습니다.

웹 앱의 로그인 상태와 사용자 세션을 관리해야 합니다.

다음의 Google 로그인 자바스크립트 클라이언트 참조삭제합니다.

객체:

  • 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 로그인 자바스크립트 클라이언트 참조삭제합니다.

객체:

  • gapi.auth2.ClientConfig
  • gapi.auth2.OfflineAccessOptions

메서드

  • gapi.auth2.getAuthInstance()
  • GoogleUser.grant()

암시적 흐름

토큰 클라이언트 초기화의 예에 따라 TokenClientConfig 객체와 initTokenClient() 호출을 추가하여 웹 앱을 구성합니다.

Google 로그인 자바스크립트 클라이언트 참조Google Identity Services대체합니다.

객체:

  • gapi.auth2.AuthorizeConfig, TokenClientConfig 공동작업

메서드

  • gapi.auth2.init(), google.accounts.oauth2.initTokenClient() 공동작업

매개변수:

  • gapi.auth2.AuthorizeConfig.login_hintTokenClientConfig.login_hint로 대체합니다.
  • TokenClientConfig.hdgapi.auth2.GoogleUser.getHostedDomain() 호출

승인 코드 흐름

코드 클라이언트 초기화의 예에 따라 CodeClientConfig 객체와 initCodeClient() 호출을 추가하여 웹 앱을 구성합니다.

암시적 흐름에서 승인 코드 흐름으로 전환할 때:

Google 로그인 자바스크립트 클라이언트 참조 삭제

객체:

  • gapi.auth2.AuthorizeConfig

메서드

  • gapi.auth2.init()

매개변수:

  • gapi.auth2.AuthorizeConfig.login_hint
  • gapi.auth2.GoogleUser.getHostedDomain()

토큰 요청

버튼 클릭과 같은 사용자 동작은 요청을 생성하여 액세스 토큰이 사용자별 승인 코드를 액세스 토큰 및 갱신 토큰으로 교환한 후 암시적 흐름을 통해 사용자의 브라우저에 직접 반환되거나 백엔드 플랫폼으로 반환됩니다.

암시적 흐름

사용자가 로그인되어 있고 Google과의 활성 세션이 있는 동안 브라우저에서 액세스 토큰을 가져와 사용할 수 있습니다. 암시적 모드에서는 이전 요청이 있었더라도 액세스 토큰을 요청하려면 사용자 동작이 필요합니다.

Google 로그인 자바스크립트 클라이언트 참조Google Identity Services대체합니다.

메서드

  • gapi.auth2.authorize(), TokenClient.requestAccessToken() 공동작업
  • GoogleUser.reloadAuthResponse()(TokenClient.requestAccessToken() 포함)

링크나 버튼을 추가하여 requestAccessToken()를 호출하여 액세스 토큰을 요청하거나 기존 토큰이 만료될 때 새 토큰을 얻기 위한 팝업 UX 흐름을 시작합니다.

코드베이스를 다음과 같이 업데이트합니다.

  • requestAccessToken()를 사용하여 OAuth 2.0 토큰 흐름을 트리거합니다.
  • requestAccessTokenOverridableTokenClientConfig를 사용하여 여러 범위의 요청을 여러 개의 작은 요청으로 분리하여 증분 승인을 지원합니다.
  • 기존 토큰이 만료되거나 취소되면 새 토큰을 요청합니다.

여러 범위로 작업하려면 한 번에 모든 범위가 아니라 필요할 때만 범위 액세스를 요청하기 위해 코드베이스의 구조를 변경해야 할 수 있습니다. 이를 점진적 승인이라고 합니다. 각 요청에는 가능한 한 적은 범위를 포함해야 하며, 이상적으로는 단일 범위를 포함해야 합니다. 점진적 승인을 위해 앱을 업데이트하는 방법에 관한 자세한 내용은 사용자 동의 처리 방법을 참고하세요.

액세스 토큰이 만료되면 gapi.auth2 모듈이 웹 앱의 유효한 새 액세스 토큰을 자동으로 가져옵니다. 사용자 보안을 강화하기 위해 자동 토큰 갱신 프로세스는 Google ID 서비스 라이브러리에서 지원되지 않습니다. 만료된 액세스 토큰을 감지하고 새 액세스 토큰을 요청하도록 웹 앱을 업데이트해야 합니다. 자세한 내용은 아래의 토큰 처리 섹션을 참고하세요.

승인 코드 흐름

requestCode()를 호출하여 Google에 승인 코드를 요청하는 링크 또는 버튼을 추가합니다. 예시를 보려면 OAuth 2.0 코드 흐름 트리거를 참고하세요.

만료되거나 취소된 액세스 토큰에 응답하는 방법은 아래의 토큰 처리 섹션을 참조하세요.

토큰 처리

만료되거나 취소된 액세스 토큰이 사용되었을 때 실패한 Google API 호출을 감지하고 유효한 새 액세스 토큰을 요청하는 오류 처리를 추가합니다.

만료되거나 취소된 액세스 토큰이 사용되면 Google API에서 401 Unauthorizedinvalid_token의 HTTP 상태 코드 오류 메시지를 반환합니다. 예를 보려면 잘못된 토큰 응답을 참조하세요.

만료된 토큰

액세스 토큰은 수명이 짧으며 보통 몇 분 동안만 유효합니다.

토큰 취소

Google 계정 소유자는 언제든지 이전에 부여된 동의를 철회할 수 있습니다. 이렇게 하면 기존 액세스 토큰과 갱신 토큰이 무효화됩니다. 취소는 revoke()를 사용하거나 Google 계정을 통해 플랫폼에서 트리거될 수 있습니다.

Google 로그인 자바스크립트 클라이언트 참조Google Identity Services대체합니다.

메서드

  • getAuthInstance().disconnect(), google.accounts.oauth2.revoke() 공동작업
  • GoogleUser.disconnect(), google.accounts.oauth2.revoke() 공동작업

사용자가 플랫폼에서 계정을 삭제하거나 앱과 데이터를 공유하는 데 대한 동의를 삭제하려고 하면 revoke를 호출합니다.

웹 앱 또는 백엔드 플랫폼에서 액세스 토큰을 요청하면 Google은 사용자에게 동의 대화상자를 표시합니다. Google이 사용자에게 표시하는 동의 대화상자의 예를 참고하세요.

앱에 액세스 토큰을 발급하기 전에 사용자 동의 메시지를 표시하고 결과를 기록하려면 기존 활성 Google 세션이 필요합니다. 기존 세션이 아직 설정되지 않은 경우 사용자가 Google 계정에 로그인해야 할 수 있습니다.

사용자 로그인

사용자는 별도의 브라우저 탭에서 또는 기본적으로 브라우저나 운영체제를 통해 Google 계정에 로그인할 수 있습니다. 사이트에 Google 계정으로 로그인을 추가하여 사용자가 앱을 처음 열 때 Google 계정과 브라우저 간에 활성 세션을 설정하는 것이 좋습니다. 이렇게 하면 다음과 같은 이점이 있습니다.

  • 사용자가 로그인해야 하는 횟수를 최소화합니다. 활성 세션이 아직 없는 경우 액세스 토큰을 요청하면 Google 계정 로그인 프로세스가 시작됩니다.
  • JWT ID 토큰 사용자 인증 정보 email 필드를 CodeClientConfig 또는 TokenClientConfig 객체의 login_hint 매개변수 값으로 직접 사용합니다. 이는 플랫폼에서 사용자 계정 관리 시스템을 유지 관리하지 않는 경우에 특히 유용합니다.
  • Google 계정을 조회하여 플랫폼의 기존 로컬 사용자 계정과 연결하면 플랫폼에서 중복 계정을 최소화하는 데 도움이 됩니다.
  • 새로운 로컬 계정이 생성되면 가입 대화상자 및 흐름을 사용자 인증 대화상자 및 흐름과 명확하게 구분할 수 있으므로 필요한 단계 수가 줄어들고 이탈율이 향상됩니다.

로그인 후 액세스 토큰이 발급되기 전에 사용자는 요청된 범위에 관한 애플리케이션 동의를 제공해야 합니다.

동의 후에는 사용자가 승인하거나 거부한 범위 목록과 함께 액세스 토큰이 반환됩니다.

세분화된 권한을 통해 사용자는 개별 범위를 승인하거나 거부할 수 있습니다. 여러 범위에 대한 액세스를 요청할 때는 다른 범위와 관계없이 각 범위가 부여되거나 거부됩니다. 사용자의 선택에 따라 앱에서는 개별 범위에 종속되는 기능을 선택적으로 사용 설정합니다.

암시적 흐름

Google 로그인 자바스크립트 클라이언트 참조Google Identity Services대체합니다.

객체:

  • gapi.auth2.AuthorizeResponse, TokenClient.TokenResponse 공동작업
  • gapi.auth2.AuthResponse, TokenClient.TokenResponse 공동작업

메서드

  • GoogleUser.hasGrantedScopes()(google.accounts.oauth2.hasGrantedAllScopes() 포함)
  • GoogleUser.getGrantedScopes()(google.accounts.oauth2.hasGrantedAllScopes() 포함)

Google 로그인 자바스크립트 클라이언트 참조 삭제:

메서드

  • GoogleUser.getAuthResponse()

세분화된 권한 예시에 따라 hasGrantedAllScopes()hasGrantedAnyScope()로 웹 앱을 업데이트합니다.

승인 코드 흐름

인증 코드 처리의 안내에 따라 승인 코드 엔드포인트를 백엔드 플랫폼에 업데이트 또는 추가합니다.

코드 모델 사용 가이드에 설명된 단계에 따라 플랫폼을 업데이트하여 요청을 검증하고 액세스 토큰과 갱신 토큰을 가져옵니다.

증분 승인 안내에 따라 사용자가 승인한 개별 범위에 따라 선택적으로 기능을 사용 설정하거나 사용 중지하도록 플랫폼을 업데이트하고 사용자가 부여한 액세스 범위를 검사합니다.

암시적 흐름의 예

기존 방식

GAPI 클라이언트 라이브러리

사용자 동의를 위한 팝업 대화상자를 사용하여 브라우저에서 실행되는 JavaScript용 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 서비스 자바스크립트 라이브러리만 보여줍니다. 클라이언트를 구성하고, 액세스 토큰을 요청 및 가져오고, 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 async/await

이 예시에서는 토큰 모델을 사용하여 Google ID 서비스 라이브러리를 추가하고, gapi.auth2 모듈을 삭제하고, JavaScript용 Google API 클라이언트 라이브러리를 사용하여 API를 호출하는 방법을 보여줍니다.

Promise, async, await는 라이브러리 로드 순서를 적용하고 승인 오류를 포착하여 재시도하는 데 사용됩니다. API 호출은 유효한 액세스 토큰을 사용할 수 있게 된 후에만 이루어집니다.

사용자는 페이지가 처음 로드될 때 액세스 토큰이 누락되었거나 액세스 토큰이 만료된 후에 '캘린더 표시' 버튼을 눌러야 합니다.

<!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 &lt;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 서비스 자바스크립트 라이브러리만 사용자 동의를 위한 팝업 대화상자, 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 라이브러리 Notes
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 서비스 라이브러리 간의 객체 및 메서드 비교, 이전 중에 취해야 할 추가 정보 및 작업이 포함된 메모

변경 전 신규 Notes
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 인증 2.0 엔드포인트에 대한 직접 호출은 단일 응답으로 OAuth 2.0 액세스 토큰과 OpenID Connect ID 토큰을 모두 반환합니다.

access_tokenid_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"
    }
  }