OAuth 2 工作流

简介

以下部分提供了一些示例代码,演示了使用 requests-oauthlib 可以使用的部分可能的 OAuth2 流。我们提供了四个示例:每个示例对应于 OAuth2 RFC 定义的一种授权类型。这些授权类型(或工作流)是授权码授权(或 Web 应用程序流)、隐式授权(或移动应用程序流)、资源所有者密码凭据授权(或更简洁地说,旧版应用程序流)和客户端凭据授权(或后端应用程序流)。

可用的工作流

有四个核心工作流

  1. 授权码授权(Web 应用程序流)。

  2. 隐式授权(移动应用程序流)。

  3. 资源所有者密码凭据授权(旧版应用程序流)。

  4. 客户端凭据授权(后端应用程序流)。

Web 应用程序流

以下步骤概述了如何使用默认授权授权类型流来获取访问令牌和获取受保护的资源。在此示例中,提供程序是 Google,受保护的资源是用户的个人资料。

  1. 手动从 OAuth 提供程序获取凭据。至少需要一个 client_id,但可能还需要一个 client_secret。在此过程中,还可能需要注册默认重定向 URI 以供应用程序使用。将这些内容保存在 Python 脚本中

>>> client_id = r'your_client_id'
>>> client_secret = r'your_client_secret'
>>> redirect_uri = 'https://your.callback/uri'
  1. 通过重定向进行用户授权。首先,我们将根据提供程序给出的基本 URL 和之前获取的凭据创建一个授权 URL。此外,大多数提供程序会要求您请求访问特定范围。在此示例中,我们将要求 Google 访问用户的电子邮件地址和用户个人资料。

# Note that these are Google specific scopes
>>> scope = ['https://www.googleapis.com/auth/userinfo.email',
             'https://www.googleapis.com/auth/userinfo.profile']
>>> oauth = OAuth2Session(client_id, redirect_uri=redirect_uri,
                          scope=scope)
>>> authorization_url, state = oauth.authorization_url(
        'https://127.0.0.1/o/oauth2/auth',
        # access_type and prompt are Google specific extra
        # parameters.
        access_type="offline", prompt="select_account")

>>> print(f'Please go to {authorization_url} and authorize access.')
>>> authorization_response = input('Enter the full callback URL')
  1. 使用用户授权期间获取的授权码从提供程序获取访问令牌。

>>> token = oauth.fetch_token(
        'https://127.0.0.1/o/oauth2/token',
        authorization_response=authorization_response,
        # Google specific extra parameter used for client
        # authentication
        client_secret=client_secret)
  1. 使用刚获取的访问令牌访问受保护的资源。例如,获取用户的个人资料信息。

>>> r = oauth.get('https://www.googleapis.com/oauth2/v1/userinfo')
>>> # Enjoy =)

移动应用程序流

以下步骤概述了如何使用隐式代码授权类型流来获取访问令牌。

  1. 您将需要以下设置。

>>> client_id = 'your_client_id'
>>> scopes = ['scope_1', 'scope_2']
>>> auth_url = 'https://your.oauth2/auth'
  1. 获取 authorization_url

>>> from oauthlib.oauth2 import MobileApplicationClient
>>> from requests_oauthlib import OAuth2Session
>>> oauth = OAuth2Session(client=MobileApplicationClient(client_id=client_id), scope=scopes)
>>> authorization_url, state = oauth.authorization_url(auth_url)
  1. 从提供商获取访问令牌。

>>> response = oauth.get(authorization_url)
>>> oauth.token_from_fragment(response.url)

旧版应用程序流程

以下步骤概述了如何使用资源所有者密码凭据授权类型流程获取访问令牌。

  1. 您将需要以下设置。 client_secret 是可选的,具体取决于提供商。

>>> client_id = 'your_client_id'
>>> client_secret = 'your_client_secret'
>>> username = 'your_username'
>>> password = 'your_password'
  1. 从提供商获取访问令牌。

>>> from oauthlib.oauth2 import LegacyApplicationClient
>>> from requests_oauthlib import OAuth2Session
>>> oauth = OAuth2Session(client=LegacyApplicationClient(client_id=client_id))
>>> token = oauth.fetch_token(token_url='https://somesite.com/oauth2/token',
        username=username, password=password, client_id=client_id,
        client_secret=client_secret)

后端应用程序流程

以下步骤概述了如何使用资源所有者客户端凭据授权类型流程获取访问令牌。

  1. 从您的 OAuth 提供商获取凭据。至少需要 client_idclient_secret

    >>> client_id = 'your_client_id'
    >>> client_secret = 'your_client_secret'
    
  2. 从提供商获取访问令牌。

    >>> from oauthlib.oauth2 import BackendApplicationClient
    >>> from requests_oauthlib import OAuth2Session
    >>> client = BackendApplicationClient(client_id=client_id)
    >>> oauth = OAuth2Session(client=client)
    >>> token = oauth.fetch_token(token_url='https://provider.com/oauth2/token', client_id=client_id,
            client_secret=client_secret)
    

    如果您的提供商要求您在基本身份验证标头中传递身份验证凭据,您可以这样做

    >>> from oauthlib.oauth2 import BackendApplicationClient
    >>> from requests_oauthlib import OAuth2Session
    >>> from requests.auth import HTTPBasicAuth
    >>> auth = HTTPBasicAuth(client_id, client_secret)
    >>> client = BackendApplicationClient(client_id=client_id)
    >>> oauth = OAuth2Session(client=client)
    >>> token = oauth.fetch_token(token_url='https://provider.com/oauth2/token', auth=auth)
    

刷新令牌

某些提供商会为您提供 refresh_tokenaccess_token。这些可用于直接获取新的访问令牌,而无需执行常规的 OAuth 工作流。 requests-oauthlib 提供了获取刷新令牌的三种方法。所有这些都依赖于您在令牌中指定准确的 expires_in

expires_in 是与访问令牌和刷新令牌一起提供的凭据,指示从现在开始多少秒后访问令牌将过期。通常,访问令牌在一小时后过期,而 expires_in 将为 3600。如果没有它,requests-oauthlib 就无法知道令牌何时过期,因为由于令牌过期而导致请求失败的状态代码未定义。

如果您对令牌刷新不感兴趣,请始终为 expires_in 传递一个正值或完全省略它。

(全部)定义令牌、令牌保护程序和所需的凭据

>>> token = {
...     'access_token': 'eswfld123kjhn1v5423',
...     'refresh_token': 'asdfkljh23490sdf',
...     'token_type': 'Bearer',
...     'expires_in': '-30',     # initially 3600, need to be updated by you
...  }
>>> client_id = r'foo'
>>> refresh_url = 'https://provider.com/token'
>>> protected_url = 'https://provider.com/secret'

>>> # most providers will ask you for extra credentials to be passed along
>>> # when refreshing tokens, usually for authentication purposes.
>>> extra = {
...     'client_id': client_id,
...     'client_secret': r'potato',
... }

>>> # After updating the token you will most likely want to save it.
>>> def token_saver(token):
...     # save token in database / session

(首先)定义每个请求上的 Try-Catch TokenExpiredError

这是最基本版本,其中在需要刷新但手动进行刷新时会引发错误。

>>> from requests_oauthlib import OAuth2Session
>>> from oauthlib.oauth2 import TokenExpiredError
>>> try:
...     oauth = OAuth2Session(client_id, token=token)
...     r = oauth.get(protected_url)
>>> except TokenExpiredError as e:
...     token = oauth.refresh_token(refresh_url, **extra)
...     token_saver(token)
>>> oauth = OAuth2Session(client_id, token=token)
>>> r = oauth.get(protected_url)

(其次)定义自动令牌刷新,但手动更新

这可以说是基本刷新方法和方便刷新方法之间令人尴尬的中间方法,其中令牌会自动刷新,但手动保存新令牌。

>>> from requests_oauthlib import OAuth2Session, TokenUpdated
>>> try:
...     oauth = OAuth2Session(client_id, token=token,
...             auto_refresh_kwargs=extra, auto_refresh_url=refresh_url)
...     r = oauth.get(protected_url)
>>> except TokenUpdated as e:
...     token_saver(e.token)

TLS 客户端身份验证

要通过自签名或 CA 颁发的证书使用 TLS 客户端身份验证(draft-ietf-oauth-mtls),请在令牌请求中传递证书并确保在请求中发送了客户端 ID

>>> oauth.fetch_token(token_url='https://somesite.com/oauth2/token',
...     include_client_id=True, cert=('test-client.pem', 'test-client-key.pem'))