Triển khai ủy quyền phía máy chủ

Các yêu cầu đến API Gmail phải được uỷ quyền bằng thông tin xác thực OAuth 2.0. Bạn nên sử dụng luồng phía máy chủ khi ứng dụng cần thay mặt người dùng truy cập vào API của Google, chẳng hạn như khi người dùng không có mạng. Phương pháp này yêu cầu truyền mã uỷ quyền một lần từ ứng dụng khách đến máy chủ; mã này được dùng để lấy mã truy cập và mã làm mới cho máy chủ.

Để tìm hiểu thêm về cách triển khai OAuth 2.0 của Google ở phía máy chủ, hãy xem bài viết Sử dụng OAuth 2.0 cho ứng dụng máy chủ web.

Nội dung

Tạo mã ứng dụng và khoá bí mật của ứng dụng

Để bắt đầu sử dụng API Gmail, trước tiên bạn cần sử dụng công cụ thiết lập. Công cụ này sẽ hướng dẫn bạn quy trình tạo dự án trong Google API Console, bật API và tạo thông tin xác thực.

  1. Trên trang Thông tin xác thực, hãy nhấp vào Tạo thông tin xác thực > Mã ứng dụng khách OAuth để tạo thông tin xác thực OAuth 2.0 hoặc Tạo thông tin xác thực > Khoá tài khoản dịch vụ để tạo tài khoản dịch vụ.
  2. Nếu bạn đã tạo mã ứng dụng khách OAuth, hãy chọn loại ứng dụng.
  3. Điền vào biểu mẫu rồi nhấp vào Tạo.

Mã ứng dụng khách và khoá tài khoản dịch vụ của ứng dụng hiện được liệt kê trên trang Thông tin xác thực. Để biết thông tin chi tiết, hãy nhấp vào một mã ứng dụng khách; các tham số sẽ khác nhau tuỳ thuộc vào loại mã, nhưng có thể bao gồm địa chỉ email, khoá bí mật của ứng dụng khách, nguồn gốc JavaScript hoặc URI chuyển hướng.

Ghi lại mã ứng dụng khách vì bạn sẽ cần thêm mã này vào mã của mình sau.

Xử lý yêu cầu uỷ quyền

Khi người dùng tải ứng dụng của bạn lần đầu tiên, họ sẽ thấy một hộp thoại để cấp quyền cho ứng dụng truy cập vào tài khoản Gmail của họ theo phạm vi quyền được yêu cầu. Sau lần uỷ quyền ban đầu này, người dùng chỉ thấy hộp thoại cấp quyền nếu mã ứng dụng của bạn thay đổi hoặc phạm vi được yêu cầu đã thay đổi.

Xác thực người dùng

Lần đăng nhập ban đầu này sẽ trả về một đối tượng kết quả uỷ quyền chứa mã uỷ quyền nếu thành công.

Trao đổi mã uỷ quyền để lấy mã truy cập

Mã uỷ quyền là mã một lần mà máy chủ của bạn có thể trao đổi lấy mã truy cập. Mã truy cập này được chuyển đến API Gmail để cấp cho ứng dụng của bạn quyền truy cập vào dữ liệu người dùng trong một khoảng thời gian có hạn.

Nếu ứng dụng của bạn yêu cầu quyền truy cập offline, thì trong lần đầu tiên ứng dụng trao đổi mã uỷ quyền, ứng dụng cũng sẽ nhận được một mã làm mới mà ứng dụng sử dụng để nhận mã truy cập mới sau khi mã truy cập trước đó đã hết hạn. Ứng dụng của bạn lưu trữ mã thông báo làm mới này (thường là trong cơ sở dữ liệu trên máy chủ) để sử dụng sau này.

Các mã mẫu sau đây minh hoạ cách trao đổi mã uỷ quyền cho mã truy cập có quyền truy cập offline và lưu trữ mã làm mới.

Python

Thay thế giá trị CLIENTSECRETS_LOCATION bằng vị trí của tệp 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)

Uỷ quyền bằng thông tin xác thực đã lưu

Khi người dùng truy cập vào ứng dụng của bạn sau khi hoàn tất quy trình uỷ quyền lần đầu thành công, ứng dụng của bạn có thể sử dụng mã thông báo làm mới đã lưu trữ để uỷ quyền các yêu cầu mà không cần nhắc người dùng lại.

Nếu bạn đã xác thực người dùng, ứng dụng của bạn có thể truy xuất mã thông báo làm mới từ cơ sở dữ liệu và lưu trữ mã thông báo đó trong một phiên phía máy chủ. Nếu mã thông báo làm mới bị thu hồi hoặc không hợp lệ, bạn sẽ cần phát hiện điều này và thực hiện hành động thích hợp.

Sử dụng thông tin xác thực OAuth 2.0

Sau khi truy xuất thông tin xác thực OAuth 2.0 như trong phần trước, bạn có thể dùng thông tin này để uỷ quyền cho đối tượng dịch vụ Gmail và gửi yêu cầu đến API.

Tạo bản sao cho đối tượng dịch vụ

Mã mẫu này cho biết cách tạo bản sao của đối tượng dịch vụ, sau đó uỷ quyền cho đối tượng đó thực hiện các yêu cầu 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)

Gửi yêu cầu được uỷ quyền và kiểm tra thông tin xác thực đã bị thu hồi

Đoạn mã sau đây sử dụng một thực thể dịch vụ Gmail được uỷ quyền để truy xuất danh sách thư.

Nếu xảy ra lỗi, mã sẽ kiểm tra mã trạng thái HTTP 401. Bạn nên xử lý lỗi này bằng cách chuyển hướng người dùng đến URL uỷ quyền.

Bạn có thể xem thêm các thao tác khác trên API Gmail trong Tài liệu tham khảo 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()

Các bước tiếp theo

Khi đã quen với việc uỷ quyền cho các yêu cầu của API Gmail, bạn có thể bắt đầu xử lý thư, chuỗi thư và nhãn như mô tả trong các phần Hướng dẫn dành cho nhà phát triển.

Bạn có thể tìm hiểu thêm về các phương thức API hiện có trong Tài liệu tham khảo API.