Google ID 서비스로 이전

개요

Google API를 호출하기 위한 사용자별 액세스 토큰을 얻기 위해 Google은 여러 자바스크립트 라이브러리를 제공합니다.

이 가이드에서는 이러한 라이브러리에서 Google ID 서비스 라이브러리로 마이그레이션하는 방법을 안내합니다.

이 가이드의 내용은 다음과 같습니다.

  • 지원 중단된 플랫폼 라이브러리를 ID 서비스 라이브러리로 대체합니다.
  • API 클라이언트 라이브러리를 사용하는 경우 지원 중단된 gapi.auth2 모듈과 그 모듈 및 객체를 삭제하고 상응하는 Identity Services로 바꿉니다.

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

사용자 가입 및 로그인에 대한 인증을 찾고 있다면 Google 로그인에서 이전을 참고하세요.

승인 흐름 확인하기

가능한 두 가지 사용자 승인 흐름인 암시적 및 승인 코드.

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

웹 앱이 암시적 흐름을 사용하고 있음을 나타내는 지표

웹 앱에서 승인 코드 흐름을 사용하고 있음을 나타내는 지표

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

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

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

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

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

코드베이스가 두 흐름을 모두 지원하는 경우도 있습니다.

승인 절차 선택

마이그레이션을 시작하기 전에 기존 흐름을 계속 사용할지 아니면 다른 흐름을 채택하여 요구사항을 가장 잘 충족하는지 확인해야 합니다.

승인 흐름 선택에서 두 흐름의 주요 차이점과 장단점을 알아보세요.

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

아래 선택기를 사용하여 승인 절차를 선택하세요.

암시적 흐름

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

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

승인 코드 흐름

Google에서 발급한 사용자당 승인 코드는 백엔드 플랫폼으로 전송되고 이후 액세스 토큰 및 갱신 토큰으로 교환됩니다.

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

이 가이드를 통해 굵게 표시된 안내에 따라 기존 기능을 추가, 삭제, 업데이트 또는 교체하세요.

브라우저 내 웹 앱 변경사항

이 섹션에서는 Google ID 서비스 자바스크립트 라이브러리로 이전할 때 브라우저 내 웹 앱의 변경사항을 검토합니다.

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

디버그 쿠키는 영향을 받는 코드를 찾고 지원 중단 후 동작을 테스트하는 데 도움이 될 수 있습니다.

크거나 복잡한 앱에서는 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 서비스 라이브러리로 대체합니다.

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

<script src="https://accounts.google.com/gsi/client" async defer></script>

gapi.load('auth2', function)를 사용하여 auth2 모듈을 로드하는 모든 인스턴스를 삭제합니다.

gapi.auth2 모듈 대신 Google ID 서비스 라이브러리가 사용됩니다. 자바스크립트용 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 ID 서비스대체합니다.

객체:

  • TokenClientConfig이(가) 포함된 gapi.auth2.AuthorizeConfig

메서드

  • google.accounts.oauth2.initTokenClient()이(가) 포함된 gapi.auth2.init()

매개변수:

  • TokenClientConfig.hintgapi.auth2.AuthorizeConfig.login_hint 호출
  • TokenClientConfig.hosted_domaingapi.auth2.GoogleUser.getHostedDomain() 호출

승인 코드 흐름

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

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

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

객체:

  • gapi.auth2.AuthorizeConfig

메서드

  • gapi.auth2.init()

매개변수:

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

토큰 요청

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

암시적 흐름

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

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

메서드

  • TokenClient.requestAccessToken()이(가) 포함된 gapi.auth2.authorize()
  • TokenClient.requestAccessToken()이(가) 포함된 GoogleUser.reloadAuthResponse()

링크 또는 버튼을 추가하여 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 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 객체의 hint 매개변수 값으로 직접 사용합니다. 이 기능은 플랫폼에서 사용자 계정 관리 시스템을 유지하지 않는 경우 특히 유용합니다.
  • Google 계정을 조회하여 플랫폼의 기존 로컬 사용자 계정과 연결하면 플랫폼에서 중복 계정을 최소화할 수 있습니다.
  • 새 로컬 계정을 만들면 가입 대화상자 및 흐름을 사용자 인증 대화상자 및 흐름과 명확하게 분리하여 필요한 단계 수를 줄이고 감소율을 개선할 수 있습니다.

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

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

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

암시적 흐름

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

객체:

  • TokenClient.TokenResponse이(가) 포함된 gapi.auth2.AuthorizeResponse
  • TokenClient.TokenResponse이(가) 포함된 gapi.auth2.AuthResponse

메서드

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

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

메서드

  • 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 모듈 또는 자바스크립트 라이브러리를 사용하지 않습니다.

<!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 Identity Service 자바스크립트 라이브러리만 보여줍니다. 클라이언트 구성, 액세스 토큰 요청 및 가져오기, 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 모듈을 삭제하며 자바스크립트용 Google API 클라이언트 라이브러리를 사용하여 API를 호출하는 방법을 보여줍니다.

프로미스, 비동기, 대기는 라이브러리 로드 순서를 적용하고 승인 오류를 포착하고 재시도하는 데 사용됩니다. 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 모듈을 삭제하며 자바스크립트용 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 ID 서비스 라이브러리 팝업 UX는 URL 리디렉션을 사용하여 승인 코드를 백엔드 토큰 엔드포인트로 직접 반환하거나 사용자의 브라우저에서 실행되는 자바스크립트 콜백 핸들러를 사용하여 플랫폼에 대한 응답을 프록시할 수 있습니다. 두 경우 모두 백엔드 플랫폼이 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 Identity Service 자바스크립트 라이브러리만 사용자 동의 및 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>

자바스크립트 라이브러리

Google ID 서비스는 다양한 인증 및 모듈과 같은 기능을 통합하고 교체하는 사용자 인증 및 승인에 사용되는 단일 자바스크립트 라이브러리입니다.

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 로그인 자바스크립트 클라이언트 라이브러리와 Google ID 서비스 라이브러리 및 Notes 간의 객체 및 메서드 비교(마이그레이션 중에 수행해야 하는 추가 정보와 작업 포함)

변경 전 신규 메모
GoogleAuth 객체 및 관련 메서드를 사용합니다.
GoogleAuth.attachClickHandler() 삭제
GoogleAuth.currentUser.get() 삭제
GoogleAuth.currentUser.listen() 삭제
GoogleAuth.disconnect() 클래스의 생성자 google.accounts.auth2.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() 또는 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.authorized() requestCode() 또는 requestAccessToken() 기존 기기를 새 기기로 교체
gapi.auth2.ClientConfig() TokenClientConfig 또는 CodeClientConfig 기존 기기를 새 기기로 교체
gapi.auth2.getAuthInstance() 삭제
gapi.auth2.init() initTokenClient() 또는 initCodeClient() 기존 기기를 새 기기로 교체
gapi.auth2.OfflineAccessOptions 객체 삭제
gapi.auth2.SignInOptions 객체 삭제
gapi.signin2 객체 및 관련 메서드를 호출합니다.
gapi.signin2.render() 삭제. g_id_signin 요소 또는 google.accounts.id.renderButton에 대한 JS 호출을 HTML DOM 로드하면 Google 계정에 대한 사용자 로그인이 트리거됩니다.

사용자 인증 정보 예시

기존 사용자 인증 정보

Google 로그인 플랫폼 라이브러리, 자바스크립트용 Google API 클라이언트 라이브러리 또는 Google Auth 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"
    }
  }