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

Các yêu cầu gửi đế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 quy trình phía máy chủ khi ứng dụng của bạn cần thay mặt người dùng truy cập vào các API của Google, chẳng hạn như khi người dùng đang ngoại tuyến. Phương pháp này yêu cầu truyền mã uỷ quyền một lần từ máy khách đến máy chủ của bạn; mã này dùng để lấy mã truy cập và làm mới mã thông báo cho máy chủ của bạn.

Để tìm hiểu thêm về cách triển khai Google OAuth 2.0 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 khách và mật khẩu ứng dụng khách

Để 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 cách tạo dự án trong Bảng điều khiển API của Google, 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 Create credentials > OAuth client ID (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 của bạn 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ố thay đổi tuỳ theo loại mã, nhưng có thể bao gồm địa chỉ email, mật khẩu ứng dụng khách, nguồn gốc JavaScript hoặc URI chuyển hướng.

Hãy ghi lại Client-ID vì sau này bạn sẽ cần thêm mã này vào mã.

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

Khi tải ứng dụng của bạn lần đầu tiên, người dùng 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ọ bằng 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ỉ nhìn thấy hộp thoại cấp quyền nếu mã ứng dụng khách của ứng dụng 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 có chứa mã uỷ quyền nếu thành công.

Đổ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ể đổ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 thời gian giới hạn.

Nếu ứng dụng của bạn yêu cầu quyền truy cập vào 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ã làm mới mà ứng dụng dùng để nhận mã truy cập mới sau khi mã thông báo trước đó hết hạn. Ứng dụng của bạn lưu trữ mã làm mới này (thường trong cơ sở dữ liệu trên máy chủ) để sau này sử dụng.

Các mã mẫu sau đây minh hoạ cách trao đổi mã uỷ quyền cho mã truy cập bằng 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 trữ

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

Nếu bạn đã xác thực người dùng, thì ứng dụng có thể truy xuất mã 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ã làm mới bị thu hồi hoặc không hợp lệ, thì bạn cần phát hiện và có biện pháp xử lý 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ư hiển thị ở phần trước, thông tin xác thực này có thể được dùng để uỷ quyền cho một đối tượng dịch vụ Gmail và gửi yêu cầu tới API.

Tạo thực thể cho một đối tượng dịch vụ

Mã mẫu này cho biết cách tạo thực thể cho một đối tượng dịch vụ rồi 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ụ được uỷ quyền của Gmail để truy xuất danh sách thư.

Nếu xảy ra lỗi, mã này sẽ kiểm tra mã trạng thái HTTP 401. Mã này sẽ được xử lý 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 hoạt động của 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 đã thoải mái cho phép các yêu cầu API Gmail, bạn có thể bắt đầu xử lý thư, chuỗi và nhãn, như mô tả trong các phần Hướng dẫn 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.