diff --git a/docs/source/dev/auth.rst b/docs/source/dev/auth.rst new file mode 100644 index 00000000..2da5e1cb --- /dev/null +++ b/docs/source/dev/auth.rst @@ -0,0 +1,44 @@ +.. _auth_methods: + +Auth Methods +************ + + +The ``auth`` parameter of the :py:class:`Consumer` constructor offers a way +to define an auth method to use for all requests. + +.. code-block:: python + + auth_method = SomeAuthMethod(...) + github = GitHub(BASE_URL, auth=auth_method) + + +BasicAuth +========= + +.. autoclass:: uplink.auth.BasicAuth + +ProxyAuth +========= + +.. autoclass:: uplink.auth.ProxyAuth + +BearerToken +=========== + +.. autoclass:: uplink.auth.BearerToken + +MultiAuth +========= + +.. autoclass:: uplink.auth.MultiAuth + +ApiTokenParam +============= + +.. autoclass:: uplink.auth.ApiTokenParam + +ApiTokenHeader +============== + +.. autoclass:: uplink.auth.ApiTokenHeader diff --git a/docs/source/dev/index.rst b/docs/source/dev/index.rst index a1b07546..3d498688 100644 --- a/docs/source/dev/index.rst +++ b/docs/source/dev/index.rst @@ -10,4 +10,5 @@ This guide details the classes and methods in Uplink's public API. decorators.rst types.rst clients.rst - converters.rst \ No newline at end of file + converters.rst + auth.rst \ No newline at end of file diff --git a/docs/source/user/auth.rst b/docs/source/user/auth.rst index 80b66924..36679c38 100644 --- a/docs/source/user/auth.rst +++ b/docs/source/user/auth.rst @@ -3,21 +3,50 @@ Authentication This section covers how to do authentication with Uplink. +In v0.4, we added the :py:attr:`auth` parameter to the +:py:class:`uplink.Consumer` constructor which allowed for +sending HTTP Basic Authentication with all requests. + +In v0.9, we added more auth methods which can be used in the +:py:attr:`auth` parameter of the :py:class:`uplink.Consumer` +constructor. If you are using an uplink-based API library, +the library might extend these methods with additional +API-specific auth methods. + +Some common auth methods are described below, but for a +complete list of auth methods provided with Uplink, see +the :ref:`auth_methods` reference. + .. _basic_authentication: Basic Authentication -------------------- -In v0.4, we added the :py:attr:`auth` parameter to the -:py:class:`uplink.Consumer` constructor. - -Now it's simple to construct a consumer that uses HTTP Basic +It's simple to construct a consumer that uses HTTP Basic Authentication with all requests: .. code-block:: python github = GitHub(BASE_URL, auth=("user", "pass")) +Proxy Authentication +-------------------- + +If you need to supply credentials for an intermediate proxy +in addition to the API's HTTP Basic Authentication, use +:py:class:`uplink.auth.MultiAuth` with :py:class:`uplink.auth.ProxyAuth` +and :py:class:`uplink.auth.BasicAuth`. + +.. code-block:: python + + from uplink.auth import BasicAuth, MultiAuth, ProxyAuth + + auth_methods = MultiAuth( + ProxyAuth("proxy_user", "proxy_pass"), + BasicAuth("user", "pass") + ) + github = GitHub(BASE_URL, auth=auth_methods) + Other Authentication -------------------- @@ -43,6 +72,23 @@ through the consumer's :obj:`session ` property: self.session.params["access_token"] = access_token ... +As of v0.9, you can also supply these tokens via the :py:attr:`auth` +parameter of the :py:class:`uplink.Consumer` constructor. This is +like adding the token to the session (above) so that the token is +sent as part of every request. + +.. code-block:: python + + from uplink.auth import ApiTokenParam, ApiTokenHeader, BearerToken + + # Passing a random auth query parameter + github = GitHub(BASE_URL, auth=ApiTokenParam("access_token", access_token)) + + # Passing a random auth header + github = GitHub(BASE_URL, auth=ApiTokenHeader("Access-Token", access_token)) + + # Passing a Bearer auth token + github = GitHub(BASE_URL, auth=BearerToken(access_token)) Using Auth Support for Requests and aiohttp ------------------------------------------- diff --git a/uplink/auth.py b/uplink/auth.py index 47f66032..a5946565 100644 --- a/uplink/auth.py +++ b/uplink/auth.py @@ -33,7 +33,27 @@ def get_auth(auth_object=None): class ApiTokenParam(object): """ Authorizes requests using a token or key in a query parameter. - Users should subclass this class to define which parameter is the token parameter. + + Users may use this directly, or API library authors may subclass this + to predefine the query parameter name to use. If supplying query parameter + name on a subclass, define the ``_param`` property or attribute and + override ``__init__()`` without using ``super()``. + + .. code-block:: python + + # direct use + token_param = ApiTokenParam(QUERY_PARAM_NAME, TOKEN) + api_consumer = SomeApiConsumerClass(BASE_URL, auth=token_param) + + # subclass in API library + class ExampleApiTokenParam(ApiTokenParam): + _param = "api-token" + def __init__(self, token): + self._param_value = token + + # using the subclass + token_param = ExampleApiTokenParam(TOKEN) + api_consumer = SomeApiConsumerClass(BASE_URL, auth=token_param) """ def __init__(self, param, token): self._param = param @@ -43,20 +63,39 @@ def __call__(self, request_builder): request_builder.info["params"][self._param] = self._param_value -# class ExampleApiTokenParam(ApiTokenParam): -# _param = "api-token" -# def __init__(self, token): -# self._param_value = token - - class ApiTokenHeader(object): """ Authorizes requests using a token or key in a header. Users should subclass this class to define which header is the token header. The subclass may also, optionally, define a token prefix (such as in BearerToken) - _header and/or _prefix may be defined as class attributes on subclasses, - but should also override __init__() when they do so. + Users may use this directly, or API library authors may subclass this + to predefine the header name or a prefix to use. If supplying header name or prefix + in a subclass, define the ``_header`` and/or ``_prefix`` properties or attributes + and override ``__init__()`` without using ``super()``. + + .. code-block:: python + + # direct use + token_header = ApiTokenHeader(HEADER_NAME, TOKEN) + api_consumer = SomeApiConsumerClass(BASE_URL, auth=token_header) + + # subclass in API library with a prefix + class ExampleApiTokenHeader(ApiTokenHeader): + _header = "X-Api-Token" + def __init__(self, token): + self._token = token + + # subclass in API library with a prefix + class ExampleApiTokenHeader(ApiTokenHeader): + _header = "X-App-Id" + _prefix = "APP" + def __init__(self, token): + self._token = token + + # using the subclass + token_header = ExampleApiTokenHeader(TOKEN) + api_consumer = SomeApiConsumerClass(BASE_URL, auth=token_header) """ _header = None _prefix = None @@ -78,7 +117,19 @@ def __call__(self, request_builder): class BasicAuth(ApiTokenHeader): - """Authorizes requests using HTTP Basic Authentication.""" + """ + Authorizes requests using HTTP Basic Authentication. + + There are two ways to use BasicAuth with a Consumer: + + .. code-block:: python + + # implicit BasicAuth + github = Github(BASE_URL, auth=(USER, PASS)) + + # explicit BasicAuth + github = GitHub(BASE_URL, auth=BasicAuth(USER, PASS)) + """ _header = "Authorization" @@ -92,11 +143,37 @@ def _header_value(self): class ProxyAuth(BasicAuth): - """Authorizes requests with an intermediate HTTP proxy.""" + """ + Authorizes requests with an intermediate HTTP proxy. + + If both API auth and intermediate Proxy auth are required, + wrap ProxyAuth in MultiAuth: + + .. code-block:: python + + # only ProxyAuth + github = Github(BASE_URL, auth=ProxyAuth(PROXYUSER, PROXYPASS)) + + # both BasicAuth and ProxyAuth + auth_methods = MultiAuth( + BasicAuth(USER, PASS), + ProxyAuth(PROXYUSER, PROXYPASS) + ) + github = GitHub(BASE_URL, auth=auth_methods) + """ _header = "Proxy-Authorization" class BearerToken(ApiTokenHeader): + """ + Authorizes requests using a Bearer Token. + + .. code-block:: python + + token = something_like_oauth_that_returns_a_token() + bearer = BearerToken(token) + api_consumer = SomeApiConsumerClass(BASE_URL, auth=bearer) + """ _header = "Authorization" _prefix = "Bearer" @@ -108,16 +185,46 @@ def __init__(self, token): class MultiAuth(object): """ Authorizes requests using multiple auth methods at the same time. - api_auth = MultiAuth( - BasicAuth(username, password), - ProxyAuth(proxy_user, proxy_pass) + + This is useful for API users to supply both API credentials and + intermediary credentials (such as for a proxy). + + .. code-block:: python + + auth_methods = MultiAuth( + BasicAuth(USER, PASS), + ProxyAuth(PROXY_USER, PROXY_PASS) ) - api_consumer = SomeApiConsumerClass( - "https://my.base_url.com/", - auth=api_auth + api_consumer = SomeApiConsumerClass(BASE_URL, auth=auth_methods) + + This may also be used if an API requires multiple Auth Tokens. + + .. code-block:: python + + auth_methods = MultiAuth( + BearerToken(API_TOKEN), + ApiTokenParam(QUERY_PARAMETER_NAME, QUERY_PARAMETER_VALUE), + ApiTokenHeader(API_HEADER_NAME, API_TOKEN_2) ) + api_consumer = SomeApiConsumerClass(BASE_URL, auth=auth_methods) + + API library authors may find it more helpful to treat MultiAuth as + a list using ``append`` or ``extend`` to add aditional auth methods. + + .. code-block:: python + + auth_methods = MultiAuth() + + auth_methods.append(BearerToken(API_TOKEN)) + auth_methods.extend([ + ApiTokenParam(QUERY_PARAMETER_NAME, QUERY_PARAMETER_VALUE), + ApiTokenHeader(API_HEADER_NAME, API_TOKEN_2) + ]) + api_consumer = SomeApiConsumerClass(BASE_URL, auth=auth_methods) - Mostly, this is useful for API users to supply intermediary credentials (such as for a proxy). + # looping over contained auth methods is also supported + for method in auth_methods: + print(method.__class__.__name__) """ def __init__(self, *auth_methods): self._auth_methods = [get_auth(auth_method) for auth_method in auth_methods]