서버 측 앱용 Google 로그인

<ph type="x-smartling-placeholder">

사용자가 오프라인 상태일 때 사용자를 대신하여 Google 서비스를 사용하려면 다음 요건을 충족해야 합니다. 사용자가 클라이언트에서 앱을 승인하는 하이브리드 서버 측 흐름 사용 연결하고 특별히 일회성으로 전송 승인 코드를 서버로 전송합니다. 서버에서 이 일회용 코드를 교환합니다. Google로부터 자체 액세스 및 갱신 토큰을 획득하여 서버가 자체 API 호출을 수행할 수 있어야 합니다. 이는 사용자가 오프라인일 때도 수행될 수 있습니다. 이 일회성 코드 흐름은 순수한 서버 측 코드 플로우에 비해 서버에 액세스 토큰을 전송하는 방법을 배웁니다.

서버 측 액세스 토큰을 얻기 위한 로그인 과정 이 애플리케이션은 아래 그림과 같습니다.

일회용 코드에는 여러 보안 이점이 있습니다. 코드를 사용하면 Google은 중개자 없이 서버에 직접 토큰을 제공합니다. 코드 유출은 권장되지 않지만 사용하기가 매우 어렵습니다. 클라이언트 보안 비밀이 없을 수도 있습니다 클라이언트 비밀번호를 비밀로 유지하세요.

일회성 코드 흐름 구현

Google 로그인 버튼은 액세스 토큰승인 코드가 있습니다. 코드는 서버에서 교환할 수 있는 일회성 코드입니다. Google 서버와 통신합니다

다음 샘플 코드는 일회성 코드 플로우입니다.

일회성 코드 흐름으로 Google 로그인을 인증하려면 다음을 실행해야 합니다.

1단계: 클라이언트 ID 및 클라이언트 보안 비밀번호 만들기

클라이언트 ID와 클라이언트 비밀번호를 만들려면 Google API 콘솔 프로젝트를 만듭니다. OAuth 클라이언트 ID를 설정하고 JavaScript 출처를 등록합니다.

  1. Google API 콘솔로 이동합니다.

  2. 프로젝트 드롭다운에서 기존 프로젝트를 선택하거나 새 프로젝트를 만듭니다. 새 프로젝트 만들기를 선택합니다.

  3. 'API 및 서비스' 아래에서 사용자 인증 정보를 선택한 다음 동의 화면 구성

    이메일 주소를 선택하고 제품 이름을 지정한 다음 저장을 누릅니다.

  4. 사용자 인증 정보 탭에서 사용자 인증 정보 만들기 드롭다운을 선택합니다. 클릭하고 OAuth 클라이언트 ID를 선택합니다.

  5. 애플리케이션 유형 아래에서 웹 애플리케이션을 선택합니다.

    앱이 Google API에 대해 살펴보겠습니다. 출처는 프로토콜, 지정할 수 있습니다

    1. 승인된 자바스크립트 원본 필드에 있습니다. 앱이 실행되도록 허용할 출처를 여러 개 입력할 수 있습니다. 하위 도메인일 수 있습니다. 와일드카드는 사용할 수 없습니다. 아래 예에서 두 번째 URL은 프로덕션 URL일 수 있습니다.

      http://localhost:8080
      https://myproductionurl.example.com
      
    2. 승인된 리디렉션 URI 필드에는 값이 필요하지 않습니다. 리디렉션 URI는 JavaScript API와 함께 사용되지 않습니다.

    3. 만들기 버튼을 누릅니다.

  6. 표시되는 OAuth 클라이언트 대화상자에서 클라이언트 ID를 복사합니다. 이 클라이언트 ID를 사용하면 앱에서 사용 설정된 Google API에 액세스할 수 있습니다.

2단계: 페이지에 Google 플랫폼 라이브러리 포함

다음을 수행하는 익명 함수를 보여주는 다음 스크립트를 포함합니다. 이 index.html 웹페이지의 DOM에 스크립트를 삽입합니다.

<!-- The top of file index.html -->
<html itemscope itemtype="http://schema.org/Article">
<head>
  <!-- BEGIN Pre-requisites -->
  <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>
  <!-- END Pre-requisites -->

3단계: GoogleAuth 객체 초기화

auth2 라이브러리를 로드하고 gapi.auth2.init()를 호출하여 GoogleAuth 객체. 클라이언트 ID 및 요청하려는 범위 지정 init() 호출 시

<!-- Continuing the <head> section -->
  <script>
    function start() {
      gapi.load('auth2', function() {
        auth2 = gapi.auth2.init({
          client_id: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
          // Scopes to request in addition to 'profile' and 'email'
          //scope: 'additional_scope'
        });
      });
    }
  </script>
</head>
<body>
  <!-- ... -->
</body>
</html>

4단계: 페이지에 로그인 버튼 추가

웹페이지에 로그인 버튼을 추가하고 클릭 핸들러를 연결하여 grantOfflineAccess() 드림 일회성 코드 흐름을 시작합니다

<!-- Add where you want your sign-in button to render -->
<!-- Use an image that follows the branding guidelines in a real app -->
<button id="signinButton">Sign in with Google</button>
<script>
  $('#signinButton').click(function() {
    // signInCallback defined in step 6.
    auth2.grantOfflineAccess().then(signInCallback);
  });
</script>

5단계: 사용자 로그인

사용자가 로그인 버튼을 클릭하고 앱에 권한 액세스 권한을 부여합니다. 확인할 수 있습니다 그런 다음 grantOfflineAccess().then() 메서드에 다음을 포함한 JSON 객체가 전달됩니다. 승인 코드가 필요합니다. 예를 들면 다음과 같습니다.

{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}

6단계: 서버에 승인 코드 전송

code는 서버에서 자체 코드로 교환할 수 있는 일회성 코드입니다. 액세스 토큰 및 갱신 토큰입니다. 갱신 토큰은 사용자에게 오프라인 액세스를 요청하는 승인 대화상자가 표시되었습니다. select-account promptOfflineAccessOptions 4단계에서 나중에 사용하기 위해 검색한 갱신 토큰을 저장해야 합니다. 이후 교환에서는 갱신 토큰에 null를 반환하기 때문입니다. 이 흐름 표준 OAuth 2.0 흐름보다 강화된 보안을 제공합니다.

액세스 토큰은 항상 유효한 승인을 교환하여 반환됩니다. 생성합니다.

다음 스크립트는 로그인 버튼의 콜백 함수를 정의합니다. 날짜 로그인이 성공하면 함수가 클라이언트 측 액세스 토큰을 저장합니다. 동일한 도메인의 서버로 일회용 코드를 전송합니다.

<!-- Last part of BODY element in file index.html -->
<script>
function signInCallback(authResult) {
  if (authResult['code']) {

    // Hide the sign-in button now that the user is authorized, for example:
    $('#signinButton').attr('style', 'display: none');

    // Send the code to the server
    $.ajax({
      type: 'POST',
      url: 'http://example.com/storeauthcode',
      // Always include an `X-Requested-With` header in every AJAX request,
      // to protect against CSRF attacks.
      headers: {
        'X-Requested-With': 'XMLHttpRequest'
      },
      contentType: 'application/octet-stream; charset=utf-8',
      success: function(result) {
        // Handle or verify the server response.
      },
      processData: false,
      data: authResult['code']
    });
  } else {
    // There was an error.
  }
}
</script>

7단계: 승인 코드를 액세스 토큰으로 교환하기

서버에서 인증 코드를 액세스 및 갱신 토큰으로 교환합니다. 사용 사용자를 대신하여 Google API를 호출하고 선택적으로 액세스 토큰이 만료될 때 새 액세스 토큰을 획득합니다.

프로필 액세스를 요청한 경우 다음을 포함하는 기본 ID 토큰도 받게 됩니다. 사용자의 프로필 정보입니다.

예를 들면 다음과 같습니다.

Java
// (Receive authCode via HTTPS POST)


if (request.getHeader("X-Requested-With") == null) {
  // Without the `X-Requested-With` header, this request could be forged. Aborts.
}

// Set path to the Web application client_secret_*.json file you downloaded from the
// Google API Console: https://console.cloud.google.com/apis/credentials
// You can also find your Web application client ID and client secret from the
// console and specify them directly when you create the GoogleAuthorizationCodeTokenRequest
// object.
String CLIENT_SECRET_FILE = "/path/to/client_secret.json";

// Exchange auth code for access token
GoogleClientSecrets clientSecrets =
    GoogleClientSecrets.load(
        JacksonFactory.getDefaultInstance(), new FileReader(CLIENT_SECRET_FILE));
GoogleTokenResponse tokenResponse =
          new GoogleAuthorizationCodeTokenRequest(
              new NetHttpTransport(),
              JacksonFactory.getDefaultInstance(),
              "https://oauth2.googleapis.com/token",
              clientSecrets.getDetails().getClientId(),
              clientSecrets.getDetails().getClientSecret(),
              authCode,
              REDIRECT_URI)  // Specify the same redirect URI that you use with your web
                             // app. If you don't have a web version of your app, you can
                             // specify an empty string.
              .execute();

String accessToken = tokenResponse.getAccessToken();

// Use access token to call API
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Drive drive =
    new Drive.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential)
        .setApplicationName("Auth Code Exchange Demo")
        .build();
File file = drive.files().get("appfolder").execute();

// Get profile info from ID token
GoogleIdToken idToken = tokenResponse.parseIdToken();
GoogleIdToken.Payload payload = idToken.getPayload();
String userId = payload.getSubject();  // Use this value as a key to identify a user.
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
Python
from apiclient import discovery
import httplib2
from oauth2client import client

# (Receive auth_code by HTTPS POST)


# If this request does not have `X-Requested-With` header, this could be a CSRF
if not request.headers.get('X-Requested-With'):
    abort(403)

# Set path to the Web application client_secret_*.json file you downloaded from the
# Google API Console: https://console.cloud.google.com/apis/credentials
CLIENT_SECRET_FILE = '/path/to/client_secret.json'

# Exchange auth code for access token, refresh token, and ID token
credentials = client.credentials_from_clientsecrets_and_code(
    CLIENT_SECRET_FILE,
    ['https://www.googleapis.com/auth/drive.appdata', 'profile', 'email'],
    auth_code)

# Call Google API
http_auth = credentials.authorize(httplib2.Http())
drive_service = discovery.build('drive', 'v3', http=http_auth)
appfolder = drive_service.files().get(fileId='appfolder').execute()

# Get profile info from ID token
userid = credentials.id_token['sub']
email = credentials.id_token['email']