서버 측 승인 구현

Gmail API에 대한 요청은 OAuth 2.0을 사용하여 승인되어야 합니다. 사용자 인증 정보를 제공합니다 애플리케이션이 사용자를 대신하여 Google API에 액세스해야 하는 경우(예: 사용자가 있습니다. 이 방법을 사용하려면 클라이언트 서버로 복사해야 하며 이 코드는 액세스 토큰을 획득하고 사용할 수 있습니다

서버 측 Google OAuth 2.0 구현에 대한 자세한 내용은 다음을 참조하세요. 웹 서버 애플리케이션용 OAuth 2.0 사용

목차

클라이언트 ID 및 클라이언트 비밀번호 만들기

Gmail API를 사용하려면 먼저 사용 설정 도구를 볼 수 있습니다. 이 도구는 Google API 콘솔, API 사용 설정, 사용자 인증 정보 만들기

  1. 사용자 인증 정보 페이지에서 사용자 인증 정보 만들기 > OAuth 클라이언트 ID를 클릭하여 OAuth 2.0 사용자 인증 정보를 만들거나 사용자 인증 정보 만들기 > 서비스 account key를 사용하여 서비스 계정을 만듭니다.
  2. OAuth 클라이언트 ID를 만든 경우 애플리케이션 유형을 선택합니다.
  3. 양식을 작성하고 만들기를 클릭합니다.

이제 애플리케이션의 클라이언트 ID 및 서비스 계정 키가 사용자 인증 정보 페이지로 이동합니다 자세한 내용을 보려면 클라이언트 ID를 클릭하세요. 매개변수는 이메일 주소, 클라이언트 비밀번호, JavaScript 출처 또는 리디렉션 URI입니다.

나중에 코드에 추가해야 하므로 클라이언트 ID를 기록해 둡니다.

승인 요청 처리

사용자가 애플리케이션을 처음 로드할 때 애플리케이션이 Gmail에 액세스하도록 권한을 부여하는 대화상자 계정에 권한을 부여해야 합니다(권한 범위). 이 초기 사용자에게 권한 대화상자가 앱의 클라이언트 ID가 변경되거나 요청된 범위가 변경된 경우.

사용자 인증

처음 로그인하면 다음과 같은 승인 결과 객체가 반환됩니다. 승인 코드가 필요합니다.

승인 코드를 액세스 토큰으로 교환

승인 코드는 서버에서 교환할 수 있는 일회성 코드입니다. 액세스 토큰 이 액세스 토큰은 Gmail API로 전달되어 사용자 데이터에 대한 애플리케이션 액세스를 제한하지 않습니다.

애플리케이션에 offline 액세스 권한이 필요한 경우 앱이 처음 교환할 때 업데이트되면 애플리케이션은 호출 시 사용하는 갱신 토큰도 수신합니다. 이전 토큰이 만료된 후 새 액세스 토큰을 받을 수 있음 애플리케이션 는 나중에 사용할 수 있습니다.

다음 코드 샘플은 offline 액세스 권한이 있는 액세스 토큰과 갱신 토큰 저장

Python

CLIENTSECRETS_LOCATION 값을 client_secrets.json 파일.

import logging
from oauth2client.client import flow_from_clientsecrets
from oauth2client.client import FlowExchangeError
from apiclient.discovery import build
# ...


# Path to client_secrets.json which should contain a JSON document such as:
#   {
#     "web": {
#       "client_id": "[[YOUR_CLIENT_ID]]",
#       "client_secret": "[[YOUR_CLIENT_SECRET]]",
#       "redirect_uris": [],
#       "auth_uri": "https://accounts.google.com/o/oauth2/auth",
#       "token_uri": "https://accounts.google.com/o/oauth2/token"
#     }
#   }
CLIENTSECRETS_LOCATION = '<PATH/TO/CLIENT_SECRETS.JSON>'
REDIRECT_URI = '<YOUR_REGISTERED_REDIRECT_URI>'
SCOPES = [
    'https://www.googleapis.com/auth/gmail.readonly',
    'https://www.googleapis.com/auth/userinfo.email',
    'https://www.googleapis.com/auth/userinfo.profile',
    # Add other requested scopes.
]

class GetCredentialsException(Exception):
  """Error raised when an error occurred while retrieving credentials.

  Attributes:
    authorization_url: Authorization URL to redirect the user to in order to
                       request offline access.
  """

  def __init__(self, authorization_url):
    """Construct a GetCredentialsException."""
    self.authorization_url = authorization_url


class CodeExchangeException(GetCredentialsException):
  """Error raised when a code exchange has failed."""


class NoRefreshTokenException(GetCredentialsException):
  """Error raised when no refresh token has been found."""


class NoUserIdException(Exception):
  """Error raised when no user ID could be retrieved."""


def get_stored_credentials(user_id):
  """Retrieved stored credentials for the provided user ID.

  Args:
    user_id: User's ID.
  Returns:
    Stored oauth2client.client.OAuth2Credentials if found, None otherwise.
  Raises:
    NotImplemented: This function has not been implemented.
  """
  # TODO: Implement this function to work with your database.
  #       To instantiate an OAuth2Credentials instance from a Json
  #       representation, use the oauth2client.client.Credentials.new_from_json
  #       class method.
  raise NotImplementedError()


def store_credentials(user_id, credentials):
  """Store OAuth 2.0 credentials in the application's database.

  This function stores the provided OAuth 2.0 credentials using the user ID as
  key.

  Args:
    user_id: User's ID.
    credentials: OAuth 2.0 credentials to store.
  Raises:
    NotImplemented: This function has not been implemented.
  """
  # TODO: Implement this function to work with your database.
  #       To retrieve a Json representation of the credentials instance, call the
  #       credentials.to_json() method.
  raise NotImplementedError()


def exchange_code(authorization_code):
  """Exchange an authorization code for OAuth 2.0 credentials.

  Args:
    authorization_code: Authorization code to exchange for OAuth 2.0
                        credentials.
  Returns:
    oauth2client.client.OAuth2Credentials instance.
  Raises:
    CodeExchangeException: an error occurred.
  """
  flow = flow_from_clientsecrets(CLIENTSECRETS_LOCATION, ' '.join(SCOPES))
  flow.redirect_uri = REDIRECT_URI
  try:
    credentials = flow.step2_exchange(authorization_code)
    return credentials
  except FlowExchangeError, error:
    logging.error('An error occurred: %s', error)
    raise CodeExchangeException(None)


def get_user_info(credentials):
  """Send a request to the UserInfo API to retrieve the user's information.

  Args:
    credentials: oauth2client.client.OAuth2Credentials instance to authorize the
                 request.
  Returns:
    User information as a dict.
  """
  user_info_service = build(
      serviceName='oauth2', version='v2',
      http=credentials.authorize(httplib2.Http()))
  user_info = None
  try:
    user_info = user_info_service.userinfo().get().execute()
  except errors.HttpError, e:
    logging.error('An error occurred: %s', e)
  if user_info and user_info.get('id'):
    return user_info
  else:
    raise NoUserIdException()


def get_authorization_url(email_address, state):
  """Retrieve the authorization URL.

  Args:
    email_address: User's e-mail address.
    state: State for the authorization URL.
  Returns:
    Authorization URL to redirect the user to.
  """
  flow = flow_from_clientsecrets(CLIENTSECRETS_LOCATION, ' '.join(SCOPES))
  flow.params['access_type'] = 'offline'
  flow.params['approval_prompt'] = 'force'
  flow.params['user_id'] = email_address
  flow.params['state'] = state
  return flow.step1_get_authorize_url(REDIRECT_URI)


def get_credentials(authorization_code, state):
  """Retrieve credentials using the provided authorization code.

  This function exchanges the authorization code for an access token and queries
  the UserInfo API to retrieve the user's e-mail address.
  If a refresh token has been retrieved along with an access token, it is stored
  in the application database using the user's e-mail address as key.
  If no refresh token has been retrieved, the function checks in the application
  database for one and returns it if found or raises a NoRefreshTokenException
  with the authorization URL to redirect the user to.

  Args:
    authorization_code: Authorization code to use to retrieve an access token.
    state: State to set to the authorization URL in case of error.
  Returns:
    oauth2client.client.OAuth2Credentials instance containing an access and
    refresh token.
  Raises:
    CodeExchangeError: Could not exchange the authorization code.
    NoRefreshTokenException: No refresh token could be retrieved from the
                             available sources.
  """
  email_address = ''
  try:
    credentials = exchange_code(authorization_code)
    user_info = get_user_info(credentials)
    email_address = user_info.get('email')
    user_id = user_info.get('id')
    if credentials.refresh_token is not None:
      store_credentials(user_id, credentials)
      return credentials
    else:
      credentials = get_stored_credentials(user_id)
      if credentials and credentials.refresh_token is not None:
        return credentials
  except CodeExchangeException, error:
    logging.error('An error occurred during code exchange.')
    # Drive apps should try to retrieve the user and credentials for the current
    # session.
    # If none is available, redirect the user to the authorization URL.
    error.authorization_url = get_authorization_url(email_address, state)
    raise error
  except NoUserIdException:
    logging.error('No user ID could be retrieved.')
  # No refresh token has been retrieved.
  authorization_url = get_authorization_url(email_address, state)
  raise NoRefreshTokenException(authorization_url)

저장된 사용자 인증 정보로 승인

최초 승인 성공 후 사용자가 앱을 방문하는 경우 애플리케이션은 저장된 갱신 토큰을 사용하여 요청을 승인하고 다시 표시할 필요가 없습니다

이미 사용자를 인증한 경우 애플리케이션에서 토큰을 갱신하고 서버 측에 해당 토큰을 세션입니다. 갱신 토큰이 취소되거나 잘못된 경우 문제를 파악하고 적절한 조치를 취해야 합니다

OAuth 2.0 사용자 인증 정보 사용

아래 그림과 같이 OAuth 2.0 사용자 인증 정보가 검색되면 이전 섹션에서 Gmail 서비스 객체를 승인하는 데 사용할 수 있으며 API에 요청을 보낼 수 있습니다

서비스 객체 인스턴스화

이 코드 샘플은 서비스 객체를 인스턴스화한 후 승인하는 방법을 보여줍니다. API 요청을 보낼 수 있습니다

Python

from apiclient.discovery import build
# ...

def build_service(credentials):
  """Build a Gmail service object.

  Args:
    credentials: OAuth 2.0 credentials.

  Returns:
    Gmail service object.
  """
  http = httplib2.Http()
  http = credentials.authorize(http)
  return build('gmail', 'v1', http=http)

승인된 요청 전송 및 취소된 사용자 인증 정보 확인

다음 코드 스니펫은 승인된 Gmail 서비스 인스턴스를 사용하여 메일 목록을 가져올 수 있습니다.

오류가 발생하면 코드에서 HTTP 401 상태 코드를 확인합니다. 이 작업은 사용자를 승인 페이지로 리디렉션하여 처리해야 합니다. URL입니다.

더 많은 Gmail API 작업은 API 참조에 설명되어 있습니다.

Python

from apiclient import errors
# ...

def ListMessages(service, user, query=''):
  """Gets a list of messages.

  Args:
    service: Authorized Gmail API service instance.
    user: The email address of the account.
    query: String used to filter messages returned.
           Eg.- 'label:UNREAD' for unread Messages only.

  Returns:
    List of messages that match the criteria of the query. Note that the
    returned list contains Message IDs, you must use get with the
    appropriate id to get the details of a Message.
  """
  try:
    response = service.users().messages().list(userId=user, q=query).execute()
    messages = response['messages']

    while 'nextPageToken' in response:
      page_token = response['nextPageToken']
      response = service.users().messages().list(userId=user, q=query,
                                         pageToken=page_token).execute()
      messages.extend(response['messages'])

    return messages
  except errors.HttpError, error:
    print 'An error occurred: %s' % error
    if error.resp.status == 401:
      # Credentials have been revoked.
      # TODO: Redirect the user to the authorization URL.
      raise NotImplementedError()

다음 단계

Gmail API 요청을 승인하는 데 익숙해지면 메시지, 대화목록, 라벨 처리 시작 개발자 가이드 섹션에 나와 있습니다.

사용 가능한 API 메서드에 대한 자세한 내용은 API 참조