Gmail API에 대한 요청은 OAuth 2.0 사용자 인증 정보를 사용하여 승인해야 합니다. 사용자가 오프라인 상태인 경우와 같이 애플리케이션에서 사용자를 대신하여 Google API에 액세스해야 하는 경우 서버 측 흐름을 사용해야 합니다. 이 접근 방식을 사용하려면 일회성 승인 코드를 클라이언트에서 서버로 전달해야 합니다. 이 코드는 서버의 액세스 토큰 및 갱신 토큰을 가져오는 데 사용됩니다.
서버 측 Google OAuth 2.0 구현에 관한 자세한 내용은 웹 서버 애플리케이션용 OAuth 2.0 사용을 참고하세요.
목차
클라이언트 ID 및 클라이언트 비밀번호 만들기
Gmail API를 사용하려면 먼저 설정 도구를 사용해야 합니다. 이 도구는 Google API 콘솔에서 프로젝트를 만들고, API를 사용 설정하고, 사용자 인증 정보를 생성하는 방법을 안내합니다.
- 사용자 인증 정보 페이지에서 사용자 인증 정보 만들기 > OAuth 클라이언트 ID를 클릭하여 OAuth 2.0 사용자 인증 정보를 만들거나 사용자 인증 정보 만들기 > 서비스 계정 키를 클릭하여 서비스 계정을 만듭니다.
- OAuth 클라이언트 ID를 만든 경우 애플리케이션 유형을 선택합니다.
- 양식을 작성하고 만들기를 클릭합니다.
이제 애플리케이션의 클라이언트 ID와 서비스 계정 키가 사용자 인증 정보 페이지에 표시됩니다. 자세한 내용을 보려면 클라이언트 ID를 클릭하세요. 매개변수는 ID 유형에 따라 다르지만 이메일 주소, 클라이언트 비밀번호, JavaScript 출처 또는 리디렉션 URI를 포함할 수 있습니다.
나중에 코드에 추가해야 하므로 클라이언트 ID를 기록해 둡니다.
승인 요청 처리
사용자가 애플리케이션을 처음 로드하면 애플리케이션이 요청된 권한 범위로 Gmail 계정에 액세스할 수 있는 권한을 부여하는 대화상자가 표시됩니다. 이 초기 승인 후에는 앱의 클라이언트 ID가 변경되거나 요청된 범위가 변경된 경우에만 사용자에게 권한 대화상자가 표시됩니다.
사용자 인증
이 최초 로그인은 성공하면 승인 코드가 포함된 승인 결과 객체를 반환합니다.
승인 코드를 액세스 토큰으로 교환
승인 코드는 서버에서 액세스 토큰으로 교환할 수 있는 일회성 코드입니다. 이 액세스 토큰은 Gmail API에 전달되어 애플리케이션에 제한된 시간 동안 사용자 데이터에 대한 액세스 권한을 부여합니다.
애플리케이션에 offline
액세스가 필요한 경우 앱이 승인 코드를 처음 교환할 때 이전 토큰이 만료된 후 새 액세스 토큰을 수신하는 데 사용하는 갱신 토큰도 수신합니다. 애플리케이션은 나중에 사용할 수 있도록 이 새로고침 토큰을 일반적으로 서버의 데이터베이스에 저장합니다.
다음 코드 샘플은 offline
액세스 권한으로 액세스 토큰의 승인 코드를 교환하고 갱신 토큰을 저장하는 방법을 보여줍니다.
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 요청을 실행하는 방법을 보여줍니다.
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 참조를 참고하세요.
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 참조를 참고하세요.