實作伺服器端授權

您必須使用 OAuth 2.0 憑證授權傳送至 Gmail API 的要求。當應用程式需要代表使用者存取 Google API 時 (例如使用者離線時),您應該使用伺服器端流程。這個方法需要將一次性授權碼從用戶端傳送至伺服器;這組程式碼可用於取得伺服器的存取權杖,以及更新伺服器的權杖。

如要進一步瞭解如何在伺服器端導入 Google OAuth 2.0,請參閱「針對網路伺服器應用程式使用 OAuth 2.0」。

目錄

建立用戶端 ID 和用戶端密鑰

如要開始使用 Gmail API,請先使用設定工具。這項工具會引導您在 Google API 控制台中建立專案、啟用 API,並建立憑證。

  1. 在「憑證」頁面上,按一下「建立憑證」>「OAuth 用戶端 ID」建立 OAuth 2.0 憑證,或點選「Create credentials」>「Service account key」來建立服務帳戶。
  2. 如果您建立了 OAuth 用戶端 ID,請選取應用程式類型。
  3. 填寫表單然後按一下「建立」

「憑證」頁面現在會列出應用程式的用戶端 ID 和服務帳戶金鑰。如需詳細資訊,請按一下用戶端 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 狀態碼,應將使用者重新導向至授權網址以進行處理。

如需瞭解更多 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 參考資料