# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['requests_oauth2client',
 'requests_oauth2client.flask',
 'requests_oauth2client.vendor_specific',
 'tests',
 'tests.unit_tests']

package_data = \
{'': ['*']}

install_requires = \
['binapy>=0.6,<0.7', 'furl>=2.1.2', 'jwskate>=0.3.0,<0.4.0', 'requests>=2.19.0']

setup_kwargs = {
    'name': 'requests-oauth2client',
    'version': '1.1.0',
    'description': 'An OAuth2.x Client based on requests.',
    'long_description': '`requests_oauth2client` is a OAuth 2.x client for Python, able to obtain, refresh and revoke tokens from any OAuth2.x/OIDC\ncompliant Authorization Server. It sits upon and extends the famous [requests] HTTP client module.\n\nIt can act as an [OAuth 2.0](https://tools.ietf.org/html/rfc6749) /\n[2.1](https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1) client, to automatically get and renew Access Tokens,\nbased on the\n[Client Credentials](https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-03.html#name-client-credentials),\n[Authorization Code](https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-03.html#name-authorization-code),\n[Refresh token](https://www.ietf.org/archive/id/draft-ietf-oauth-v2-1-03.html#name-refresh-token),\n[Token Exchange](https://www.rfc-editor.org/rfc/rfc8693.html),\n[Device Authorization](https://www.rfc-editor.org/rfc/rfc8628.html), or\n[CIBA](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html) grants.\n\nIt also supports [OpenID Connect 1.0](https://openid.net/specs/openid-connect-core-1_0.html),\n[PKCE](https://www.rfc-editor.org/rfc/rfc7636.html),\n[Client Assertions](https://www.rfc-editor.org/rfc/rfc7523.html),\n[Token Revocation](https://www.rfc-editor.org/rfc/rfc7009.html),\nand [Introspection](https://www.rfc-editor.org/rfc/rfc7662.html),\n[Resource Indicators](https://www.rfc-editor.org/rfc/rfc8707.html),\n[JWT-secured Authorization Requests](https://datatracker.ietf.org/doc/rfc9101/),\n[Pushed Authorization Requests](https://datatracker.ietf.org/doc/rfc9126/),\n[Authorization Server Issuer Identification](https://www.rfc-editor.org/rfc/rfc9207.html)\nas well as using custom params to any endpoint, and other important features that are often overlooked in other client\nlibraries.\n\nAnd it also includes a [wrapper][apiclient] around [requests.Session] that makes it super easy to use REST-style APIs,\nwith or without OAuth 2.x.\n\nPlease note that despite the name, this library has no relationship with Google\n[oauth2client](https://github.com/googleapis/oauth2client) library.\n\n[![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org/)\n[![Downloads](https://pepy.tech/badge/requests_oauth2client/month)](https://pepy.tech/project/requests_oauth2client)\n[![Supported Versions](https://img.shields.io/pypi/pyversions/requests_oauth2client.svg)](https://pypi.org/project/requests_oauth2client)\n[![PyPi license](https://badgen.net/pypi/license/requests_oauth2client/)](https://pypi.com/project/requests_oauth2client/)\n[![PyPI status](https://img.shields.io/pypi/status/requests_oauth2client.svg)](https://pypi.python.org/pypi/requests_oauth2client/)\n[![GitHub commits](https://badgen.net/github/commits/guillp/requests_oauth2client)](https://github.com/guillp/requests_oauth2client/commit/)\n[![GitHub latest commit](https://badgen.net/github/last-commit/guillp/requests_oauth2client)](https://github.com/guillp/requests_oauth2client/commit/)\n\n# Documentation\n\nFull module documentation is available at https://guillp.github.io/requests_oauth2client/\n\n# Installation\n\n`requests_oauth2client` is [available from PyPi](https://pypi.org/project/requests-oauth2client/), so installing it is as easy as:\n\n```shell\npip install requests_oauth2client\n```\n\n# Usage\n\nEverything from `requests_oauth2client` is available from the root module, so you can import it like this:\n\n```python\nfrom requests_oauth2client import *\n```\n\nNote that this automatically imports `requests`, so no need to import it yourself.\n\n## Calling APIs with Access Tokens\n\nIf you already managed to obtain an access token, you can simply use the [BearerAuth] Auth Handler for [requests]:\n\n```python\nimport requests\nfrom requests_oauth2client import BearerToken\n\ntoken = "an_access_token"\nresp = requests.get("https://my.protected.api/endpoint", auth=BearerAuth(token))\n```\n\nThis authentication handler will add a properly formatted `Authorization` header in the request, with your access token\naccording to [RFC6750](https://datatracker.ietf.org/doc/html/rfc6750#section-2.1).\n\n## Using an OAuth2Client\n\n[OAuth2Client] offers several methods that implement the communication to the various endpoints that are standardised by\nOAuth 2.0 and its extensions. Those endpoints include the Token Endpoint, the Revocation, Introspection, UserInfo,\nBackChannel Authentication and Device Authorization Endpoints. You only have to provide those if you intend to use them.\n\nTo initialize an [OAuth2Client], you only need a Token Endpoint URI, and the credentials for your application, which are\noften a `client_id` and a `client_secret`:\n\n```python\nfrom requests_oauth2client import OAuth2Client\n\noauth2client = OAuth2Client(\n    token_endpoint="https://myas.local/token_endpoint",\n    auth=("client_id", "client_secret"),\n)\n```\n\nThe Token Endpoint is the only endpoint that is mandatory to obtain tokens. Credentials are used to authenticate the\nclient everytime it sends a request to its Authorization Server. Usually, those are a static Client ID and Secret, which\nare the direct equivalent of a username and a password, but meant for an application instead of for a human user. The\ndefault authentication method used by `OAuth2Client` is *Client Secret Post*, but other standardised methods such as\n*Client Secret Basic*, *Client Secret JWT* or *Private Key JWT* are supported as well. See\n[more about client authentication methods below](#supported-client-authentication-methods).\n\n## Obtaining tokens\n\n[OAuth2Client] has methods to send requests to the Token Endpoint using the different standardised (and/or custom)\ngrants. Since the token endpoint and authentication method are already declared for the client at init time, the only\nrequired parameters are those that will be sent in the request to the Token Endpoint.\n\nThose methods directly return a [BearerToken] if the request is successful, or raise an exception if it fails.\n[BearerToken] will manage the token expiration, will contain the eventual refresh token that matches the access token,\nand will keep track of other associated metadata as well. You can create such a [BearerToken] yourself if you need:\n\n```python\nfrom requests_oauth2client import BearerToken\n\nbearer_token = BearerToken(access_token="an_access_token", expires_in=60)\nprint(bearer_token)\n# {\'access_token\': \'an_access_token\',\n#  \'expires_in\': 55,\n#  \'token_type\': \'Bearer\'}\nprint(bearer_token.expires_at)\n# datetime.datetime(2021, 8, 20, 9, 56, 59, 498793)\nassert not bearer_token.is_expired()\n```\n\nNote that the `expires_in` indicator here is not static. It keeps track of the token lifetime and is calculated as the\ntime flies. The actual static expiration date is accessible with the `expires_at` property. You can check if a token is\nexpired with [bearer_token.is_expired()](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.tokens.BearerToken.is_expired).\n\nYou can use a [BearerToken] instance anywhere you can supply an access_token as string.\n\n### Using OAuth2Client as a requests Auth Handler\n\nWhile using [OAuth2Client] directly is great for testing or debugging OAuth2.x flows, it is not a viable option for\nactual applications where tokens must be obtained, used during their lifetime then obtained again or refreshed once they\nare expired. `requests_oauth2client` contains several [requests] compatible Auth Handlers (as subclasses of\n[requests.auth.AuthBase](https://requests.readthedocs.io/en/latest/user/advanced/#custom-authentication)), that will\ntake care of obtaining tokens when required, then will cache those tokens until they are expired, and will obtain new\nones (or refresh them, when possible), once the initial token is expired. Those are best used with a [requests.Session],\nor an [ApiClient], which is a wrapper around `Session` with a few enhancements as described below.\n\n### Client Credentials grant\n\nTo send a request using the Client Credentials grant, use the aptly named\n[.client_credentials()](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client.OAuth2Client.client_credentials)\nmethod, with the parameters to send in the token request as keyword parameters:\n\n```python\ntoken = oauth2client.client_credentials(scope="myscope", resource="https://myapi.local")\n```\n\nParameters such as `scope`, `resource` or `audience` that may be required by the AS can be passed as keyword\nparameters. Those will be included in the token request that is sent to the AS.\n\n#### As Auth Handler\n\nYou can use the\n[OAuth2ClientCredentialsAuth](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.auth.OAuth2ClientCredentialsAuth)\nauth handler. It takes an [OAuth2Client] as parameter, and the additional kwargs to pass to the token endpoint:\n\n```python\nimport requests\n\nauth = OAuth2ClientCredentialsAuth(\n    oauth2client, scope="myscope", resource="https://myapi.local"\n)\n\n# use it like this:\nrequests.get("https://myapi.local/resource", auth=auth)\n\n# or\nsession = requests.Session()\nsession.auth = auth\n\nresp = session.get("https://myapi.local/resource")\n```\n\nOnce again, extra parameters such as `scope`, `resource` or `audience` are allowed if required.\n\nWhen you send your first request, [OAuth2ClientCredentialsAuth](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.auth.OAuth2ClientCredentialsAuth)\nwill automatically retrieve an access token from the AS using the Client Credentials grant, then will include it in the\nrequest. Next requests will use the same token, as long as it is valid. A new token will be automatically retrieved once\nthe previous one is expired.\n\n### Authorization Code Grant\n\nObtaining tokens with the Authorization code grant is made in 3 steps:\n\n1. your application must open specific url called the *Authentication Request* in a browser.\n\n2. your application must obtain and validate the *Authorization Response*, which is a redirection back to your\n   application that contains an *Authorization Code* as parameter.\n\n3. your application must then exchange this Authorization Code for an *Access Token*, with a request to the Token\n   Endpoint.\n\n[OAuth2Client] doesn\'t implement anything that is related to the Authorization Request or Response. It is only able to\nexchange the Authorization Code for a Token in step 3. But `requests_oauth2client` has other classes to help you with\nsteps 1 and 2, as described below:\n\n#### Generating Authorization Requests\n\nYou can generate valid authorization requests with the\n[AuthorizationRequest](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.authorization_request.AuthorizationRequest)\nclass:\n\n```python\nauth_request = AuthorizationRequest(\n    authorization_endpoint,\n    client_id,\n    redirect_uri=redirect_uri,\n    scope=scope,\n    resource=resource,  # extra parameters can be included as well if required by your AS\n)\nprint(auth_request)  # redirect the user to that URL to get a code\n```\n\nThis request will look like this (with line breaks for display purposes only):\n\n```\nhttps://myas.local/authorize\n?client_id=my_client_id\n&redirect_uri=http%3A%2F%2Flocalhost%2Fcallback\n&response_type=code\n&state=kHWL4VwcbUbtPR4mtht6yMAGG_S-ZcBh5RxI_IGDmJc\n&nonce=mSGOS1M3LYU9ncTvvutoqUR4n1EtmaC_sQ3db4dyMAc\n&scope=openid+email+profile\n&code_challenge=Dk11ttaDb_Hyq1dObMqQcTIlfYYRVblFMC9lFM3UWW8\n&code_challenge_method=S256\n&resource=https%3A%2F%2Fmy.resource.local%2Fapi\n```\n\n[AuthorizationRequest] supports PKCE and uses it by default. You can avoid it by passing `code_challenge_method=None` to\n[AuthorizationRequest]. You can obtain the generated code_verifier from `auth_request.code_verifier`.\n\nRedirecting or otherwise sending the user to this url is your application responsibility, as well as obtaining the\nAuthorization Response url.\n\n#### Validating the Authorization Response\n\nOnce the user is successfully authenticated and authorized, the AS will respond with a redirection to your redirect_uri.\nThat is the *Authorization Response*. It contains several parameters that must be retrieved by your client. The\nauthorization code is one of those parameters, but you must also validate that the *state* matches your request. You can\ndo this with:\n\n```python\nresponse_uri = input(\n    "Please enter the full url and/or params obtained on the redirect_uri: "\n)\nauth_response = auth_request.validate_callback(response_uri)\n```\n\n#### Exchanging code for tokens\n\nTo exchange a code for Access and/or ID tokens, use the\n[OAuth2Client.authorization_code()](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client.OAuth2Client.authorization_code)\nmethod. If you have obtained an AuthorizationResponse as described above, you can simply do:\n\n```python\ntoken = oauth2client.authorization_code(auth_response)\n```\n\nThis will automatically include the `code`, `redirect_uri` and `code_verifier` parameters in the Token Request,\nas expected by the AS.\n\nIf you managed another way to obtain an Authorization Code, you can manually pass those parameters like this:\n\n```python\ntoken = oauth2client.authorization_code(\n    code=code,\n    code_verifier=code_verifier,\n    redirect_uri=redirect_uri,\n    custom_param=custom_value,\n)\n```\n\n#### As Auth Handler\n\nThe\n[OAuth2AuthorizationCodeAuth](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.auth.OAuth2AuthorizationCodeAuth)\nhandler takes an [OAuth2Client] and an authorization code as parameter, plus whatever additional keyword parameters are\nrequired by your Authorization Server:\n\n```python\napi_client = ApiClient(\n    "https://your.protected.api/endpoint",\n    auth=OAuth2AuthorizationCodeAuth(\n        client,\n        code,\n        code_verifier=auth_request.code_verifier,\n        redirect_uri=redirect_uri,\n    ),\n)\n\nresp = api_client.post(\n    data={...}\n)  # first call will exchange the code for an initial access/refresh tokens\n```\n\n[OAuth2AuthorizationCodeAuth](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.auth.OAuth2AuthorizationCodeAuth)\nwill take care of refreshing the token automatically once it is expired, using the refresh token, if available.\n\n### Device Authorization Grant\n\nHelpers for the Device Authorization Grant are also included. To get device and user codes:\n\n```python\nclient = OAuth2Client(\n    token_endpoint="https://myas.local/token",\n    device_authorization_endpoint="https://myas.local/device",\n    auth=(client_id, client_secret),\n)\n\nda_resp = client.authorize_device()\n```\n\n`da_resp` contains the Device Code, User Code, Verification URI and other info returned by the AS:\n\n```python\nda_resp.device_code\nda_resp.user_code\nda_resp.verification_uri\nda_resp.verification_uri_complete\nda_resp.expires_at  # just like for BearerToken, expiration is tracked by requests_oauth2client\nda_resp.interval\n```\n\nSend/show the Verification Uri and User Code to the user. He must use a browser to visit that url, authenticate and\ninput the User Code. You can then request the Token endpoint to check if the user successfully authorized you using an\n[OAuth2Client]:\n\n```python\ntoken = client.device_code(da_resp.device_code)\n```\n\nThis will raise an exception, either\n[AuthorizationPending](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.exceptions.AuthorizationPending),\n[SlowDown](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.exceptions.SlowDown),\n[ExpiredToken](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.exceptions.ExpiredToken), or\n[AccessDenied](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.exceptions.AccessDenied) if the\nuser did not yet finish authorizing your device, if you should increase your pooling period, or if the device code is no\nlonger valid, or the user finally denied your access, respectively. Other exceptions may be raised depending on the\nerror code that the AS responds with. If the user did finish authorizing successfully, `token` will contain your access\ntoken.\n\nTo make pooling easier, you can use a\n[DeviceAuthorizationPoolingJob](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.device_authorization.DeviceAuthorizationPoolingJob)\nlike this:\n\n```python\npool_job = DeviceAuthorizationPoolingJob(\n    client, device_auth_resp.device_code, interval=device_auth_resp.interval\n)\n\nresp = None\nwhile resp is None:\n    resp = pool_job()\n\nassert isinstance(resp, BearerToken)\n```\n\n[DeviceAuthorizationPoolingJob](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.device_authorization.DeviceAuthorizationPoolingJob)\nwill automatically obey the pooling period. Everytime you call pool_job(), it will wait the appropriate number of\nseconds as indicated by the AS, and will apply slow_down requests.\n\n#### As Auth Handler\n\nUse\n[OAuth2DeviceCodeAuth](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.auth.OAuth2DeviceCodeAuth)\nas auth handler to exchange a device code for an access token:\n\n```python\napi_client = ApiClient(\n    "https://your.protected.api/endpoint",\n    auth=OAuth2DeviceCodeAuth(\n        client,\n        device_auth_resp.device_code,\n        interval=device_auth_resp.interval,\n        expires_in=device_auth_resp.expires_in,\n    ),\n)\n\nresp = api_client.post(\n    data={...}\n)  # first call will hang until the user authorizes your app and the token endpoint returns a token.\n```\n\n### Client-Initiated BackChannel Authentication (CIBA)\n\nTo initiate a BackChannel Authentication against the dedicated endpoint:\n\n```python\nclient = OAuth2Client(\n    token_endpoint="https://myas.local/token",\n    backchannel_authentication_endpoint="https://myas.local/backchannel_authorize",\n    auth=(client_id, client_secret),\n)\n\nba_resp = client.backchannel_authentication_request(\n    scope="openid email profile",\n    login_hint="user@example.net",\n)\n```\n\n`ba_resp` will contain the response attributes as returned by the AS, including an `auth_req_id`:\n\n```python\nba_resp.auth_req_id\nba_resp.expires_in  # decreases as times fly\nba_resp.expires_at  # a datetime to keep track of the expiration date, based on the "expires_in" returned by the AS\nba_resp.interval  # the pooling interval indicated by the AS\nba_resp.custom  # if the AS respond with additional attributes, they are also accessible\n```\n\nTo pool the Token Endpoint until the end-user successfully authenticates:\n\n```python\npool_job = BackChannelAuthenticationPoolingJob(\n    client=client,\n    auth_req_id=ba_resp.auth_req_id,\n    interval=bca_resp.interval,\n)\n\nresp = None\nwhile resp is None:\n    resp = pool_job()\n\nassert isinstance(resp, BearerToken)\n```\n\nHints by the AS to slow down pooling will automatically be obeyed.\n\n### Token Exchange\n\nTo send a token exchange request, use the\n[OAuth2Client.token_exchange()](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client.OAuth2Client.token_exchange)\nmethod:\n\n```python\nclient = OAuth2Client(token_endpoint, auth=...)\ntoken = client.token_exchange(\n    subject_token="your_token_value",\n    subject_token_type="urn:ietf:params:oauth:token-type:access_token",\n)\n```\n\nAs with the other grant-type specific methods, you may specify additional keyword parameters, that will be passed to the\ntoken endpoint, including any standardised attribute like `actor_token` or `actor_token_type`, or any custom parameter.\nThere are short names for token types, that will be automatically translated to standardised types:\n\n```python\ntoken = client.token_exchange(\n    subject_token="your_token_value",\n    subject_token_type="access_token",  # will be automatically replaced by "urn:ietf:params:oauth:token-type:access_token"\n    actor_token="your_actor_token",\n    actor_token_type="id_token",  # will be automatically replaced by "urn:ietf:params:oauth:token-type:id_token"\n)\n```\n\nOr to make it even easier, types can be guessed based on the supplied subject or actor token:\n\n```python\ntoken = client.token_exchange(\n    subject_token=BearerToken(\n        "your_token_value"\n    ),  # subject_token_type will be "urn:ietf:params:oauth:token-type:access_token"\n    actor_token=IdToken(\n        "your_actor_token"\n    ),  # actor_token_type will be "urn:ietf:params:oauth:token-type:id_token"\n)\n```\n\n## Supported Client Authentication Methods\n\n`requests_oauth2client` supports several client authentication methods, as defined in multiple OAuth2.x standards. You\nselect the appropriate method to use when initializing your [OAuth2Client], with the `auth` parameter. Once initialized,\na client will automatically use the configured authentication method every time it sends a requested to an endpoint that\nrequires client authentication. You don\'t have anything else to do afterwards.\n\n### Client Secret Basic\n\nWith **client_secret_basic**, `client_id` and `client_secret` are included in clear-text in the `Authorization` header when sending requests to the Token Endpoint. To use\nit, just pass a\n[`ClientSecretBasic(client_id, client_secret)`](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client_authentication.ClientSecretBasic)\nas `auth` parameter:\n\n```python\nfrom requests_oauth2client import OAuth2Client, ClientSecretBasic\n\nclient = OAuth2Client(token_endpoint, auth=ClientSecretBasic(client_id, client_secret))\n```\n\n### Client Secret Post\n\nWith **client_secret_post**, `client_id` and `client_secret` are included as part of the body form data. To use it, pass a\n[`ClientSecretPost(client_id, client_secret)`](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client_authentication.ClientSecretPost)\nas `auth` parameter. This is the default when you pass a tuple `(client_id, client_secret)` as\n`auth` when initializing an `OAuth2Client`:\n\n```python\nfrom requests_oauth2client import OAuth2Client, ClientSecretPost\n\nclient = OAuth2Client(token_endpoint, auth=ClientSecretPost(client_id, client_secret))\n# or\nclient = OAuth2Client(token_endpoint, auth=(client_id, client_secret))\n```\n\n### Client Secret JWT\n\nWith **client_secret_jwt**, the client generates an ephemeral JWT assertion including information about itself (client_id), the\nAS (url of the endpoint), and an expiration date a few seconds in the future. To use it, pass a\n[`ClientSecretJwt(client_id, client_secret)`](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client_authentication.ClientSecretJwt)\nas `auth` parameter. Assertion generation is entirely automatic, you don\'t have anything to do:\n\n```python\nfrom requests_oauth2client import OAuth2Client, ClientSecretJwt\n\nclient = OAuth2Client(token_endpoint, auth=ClientSecretJwt(client_id, client_secret))\n```\n\nThis method is more secure than the 2 previous, because only ephemeral credentials are transmitted, which limits the possibility for interception and replay of the Client Secret.\nBut that Client Secret still needs to be shared between the AS and Client owner(s).\n\n### Private Key JWT\n\nWith **private_key_jwt**, client uses a JWT assertion that is just like _client_secret_jwt_, but it is signed with an _asymmetric_ key.\nTo use it, you need a private signing key, in a `dict` that matches the JWK format, or as an instance of `jwskate.Jwk`. The matching public key must be\nregistered for your client on AS side. Once you have that, using this auth method is simple with the\n[`PrivateKeyJwt(client_id, private_jwk)`](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client_authentication.PrivateKeyJwt)\nauth handler:\n\n```python\nfrom requests_oauth2client import OAuth2Client, PrivateKeyJwt\n\nprivate_jwk = {\n    "kid": "mykid",\n    "kty": "RSA",\n    "e": "AQAB",\n    "n": "...",\n    "d": "...",\n    "p": "...",\n    "q": "...",\n    "dp": "...",\n    "dq": "...",\n    "qi": "...",\n}\n\nclient = OAuth2Client(\n    "https://myas.local/token", auth=PrivateKeyJwt(client_id, private_jwk)\n)\n# or\nclient = OAuth2Client(token_endpoint, auth=(client_id, private_jwk))\n```\n\nThis method can be considered more secure than those relying on a client secret, because only ephemeral credentials are sent over the wire, and it uses asymetric cryptography: the signing key is generated by the client, and only the public key is known by the AS.\nTransmitting that public key between owner(s) of the client and of the AS is much easier than transmitting the Client Secret, which is a shared key that must be considered as confidential.\n\n### None\n\nThe latest Client Authentication Method, **none**, is for Public Clients which do not authenticate to the Token Endpoint.\nThose clients only include their `client_id` in body form data, without any authentication credentials. Use\n[`PublicApp(client_id)`](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client_authentication.PublicApp):\n\n```python\nfrom requests_oauth2client import OAuth2Client, PublicApp\n\nclient = OAuth2Client(token_endpoint, auth=PublicApp(client_id, client_secret))\n```\n\n## Token Revocation\n\n[OAuth2Client] can send revocation requests to a Revocation Endpoint. You need to provide a Revocation Endpoint URI when\ncreating the [OAuth2Client] :\n\n```python\noauth2client = OAuth2Client(\n    token_endpoint,\n    revocation_endpoint=revocation_endpoint,\n    auth=ClientSecretJwt("client_id", "client_secret"),\n)\n```\n\nThe\n[OAuth2Client.revoke_token()](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client.OAuth2Client.revoke_token)\nmethod and its specialized aliases\n[.revoke_access_token()](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client.OAuth2Client.revoke_access_token)\nand\n[.revoke_refresh_token()](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client.OAuth2Client.revoke_refresh_token)\nare then available:\n\n```python\noauth2client.revoke_token("mytoken", token_type_hint="access_token")\noauth2client.revoke_access_token(\n    "mytoken"\n)  # will automatically add token_type_hint=access_token\noauth2client.revoke_refresh_token(\n    "mytoken"\n)  # will automatically add token_type_hint=refresh_token\n```\n\nBecause Revocation Endpoints usually don\'t return meaningful responses, those methods return a boolean. This boolean\nindicates that a request was successfully sent and no error was returned. If the Authorization Server actually returns a\nstandardised error, an exception will be raised instead.\n\n## Token Introspection\n\n[OAuth2Client] can send requests to a Token Introspection Endpoint. You need to provide an Introspection Endpoint URI\nwhen creating the `OAuth2Client`:\n\n```python\noauth2client = OAuth2Client(\n    token_endpoint,\n    introspection_endpoint=introspection_endpoint,\n    auth=ClientSecretJwt("client_id", "client_secret"),\n)\n```\n\nThe\n[OAuth2Client.introspect_token()](<https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client.OAuth2Client.instrospect_token()>)\nmethod is then available:\n\n```python\nresp = oauth2client.introspect_token("mytoken", token_type_hint="access_token")\n```\n\nIt returns whatever data is returned by the introspection endpoint (if it is a JSON, its content is returned decoded).\n\n## UserInfo Requests\n\n[OAuth2Client] can send requests to an UserInfo Endpoint. You need to provide an UserInfo Endpoint URI when creating the\n`OAuth2Client`:\n\n```python\noauth2client = OAuth2Client(\n    token_endpoint,\n    userinfo_endpoint=userinfo_endpoint,\n    auth=ClientSecretJwt("client_id", "client_secret"),\n)\n```\n\nThe\n[OAuth2Client.userinfo()](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client.OAuth2Client.userinfo))\nmethod is then available:\n\n```python\nresp = oauth2client.userinfo("mytoken")\n```\n\nIt returns whatever data is returned by the userinfo endpoint (if it is a JSON, its content is returned decoded).\n\n## Initializing an OAuth2Client from a discovery document\n\nYou can initialize an [OAuth2Client] with the endpoint URIs mentioned in a standardised discovery document with the\n[OAuth2Client.from_discovery_endpoint()](https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client.OAuth2Client.from_discovery_document)\nclass method:\n\n```python\noauth2client = OAuth2Client.from_discovery_endpoint(\n    "https://myas.local/.well-known/openid-configuration"\n)\n```\n\nThis will fetch the document from the specified URI, then will decode it and initialize an [OAuth2Client] pointing to\nthe appropriate endpoint URIs.\n\n## Specialized API Client\n\nUsing APIs usually involves multiple endpoints under the same root url, with a common authentication method. To make it\neasier, `requests_oauth2client` includes a [requests.Session] wrapper called [ApiClient], which takes the\nroot API url as parameter on initialization. You can then send requests to different endpoints by passing their relative\npath instead of the full url. [ApiClient] also accepts an `auth` parameter with an AuthHandler. You can pass any of the\nOAuth2 Auth Handler from this module, or any [requests]-compatible\n[Authentication Handler](https://requests.readthedocs.io/en/latest/user/advanced/#custom-authentication). Which makes\nit very easy to call APIs that are protected with an OAuth2 Client Credentials Grant:\n\n```python\noauth2client = OAuth2Client("https://myas.local/token", (client_id, client_secret))\napi = ApiClient(\n    "https://myapi.local/root", auth=OAuth2ClientCredentialsAuth(oauth2client)\n)\n\n# will actually send a GET to https://myapi.local/root/resource/foo\nresp = api.get("/resource/foo")\n```\n\nNote that [ApiClient] will never send requests "outside" its configured root url, unless you specifically give it a full\nurl at request time. The leading `/` in `/resource` above is optional. A leading `/` will not "reset" the url path to\nroot, which means that you can also write the relative path without the `/` and it will automatically be included:\n\n```python\napi.get("resource/foo")  # will also send a GET to https://myapi.local/root/resource/foo\n```\n\nYou may also pass the path as an iterable of strings (or string-able objects), in which case they will be joined with a\n`/` and appended to the url path:\n\n```python\n# will send a GET to https://myapi.local/root/resource/foo\napi.get(["resource", "foo"])\n# will send a GET to https://myapi.local/root/users/1234/details\napi.get(["users", 1234, "details"])\n```\n\nYou can also use a syntax based on `__getattr__` or `__getitem__`:\n\n```python\napi.resource.get()  # will send a GET to https://myapi.local/root/resource\napi["my-resource"].get()  # will send a GET to https://myapi.local/root/my-resource\n```\n\nBoth `__getattr__` and `__getitem__` return a new `ApiClient` initialised on the new base_url.\nSo you can easily call multiple sub-resources on the same API this way:\n\n```python\napi = ApiClient("https://myapi.local")\nusers_api = api.users\nuser = users_api.get("userid")  # GET https://myapi.local/users/userid\nother_user = users_api.get("other_userid")  # GET https://myapi.local/users/other_userid\nresources_api = api.resources\nresources = resources_api.get()  # GET https://myapi.local/resources\n```\n\n[ApiClient] will, by default, raise exceptions whenever a request returns an error status. You can disable that by\npassing `raise_for_status=False` when initializing your [ApiClient]:\n\n```python\napi = ApiClient(\n    "http://httpstat.us", raise_for_status=False\n)  # raise_for_status defaults to True\nresp = api.get("500")\nassert resp is not None\n# without raise_for_status=False, a requests.exceptions.HTTPError exception would be raised instead\n```\n\nYou may override this at request time:\n\n```python\n# raise_for_status at request-time overrides the value defined at init-time\nresp = api.get("500", raise_for_status=True)\n```\n\nYou can access the underlying `requests.Session` with the session attribute, and you can provide an already existing and configured `Session` instance at init time:\n\n```python\nimport requests\n\nsession = requests.Session()\nsession.proxies = {"https": "http://localhost:3128"}\napi = ApiClient("https://myapi.local/resource", session=session)\nassert api.session == session\n```\n\n## Vendor-Specific clients\n\n`requests_oauth2client` being flexible enough to handle most use cases, you should be able to use any AS by any vendor\nas long as it supports OAuth 2.0.\n\nYou can however create a subclass of [OAuth2Client] or [ApiClient] to make it easier to use with specific Authorization\nServers or APIs. The sub-module `requests_oauth2client.vendor_specific` includes such classes for [Auth0](https://auth0.com):\n\n```python\nfrom requests_oauth2client.vendor_specific import Auth0Client\n\na0client = Auth0Client("mytenant.eu", (client_id, client_secret))\n# this will automatically initialize the token endpoint to https://mytenant.eu.auth0.com/oauth/token\n# and other endpoints accordingly\ntoken = a0client.client_credentials(audience="audience")\n\n# this is a wrapper around Auth0 Management API\na0mgmt = Auth0ManagementApiClient("mytenant.eu", (client_id, client_secret))\nmyusers = a0mgmt.get("users")\n```\n\n[apiclient]: https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.api_client.ApiClient\n[authorizationrequest]: https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.authorization_request.AuthorizationRequest\n[bearerauth]: https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.auth.BearerAuth\n[bearertoken]: https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.tokens.BearerToken\n[oauth2client]: https://guillp.github.io/requests_oauth2client/api/#requests_oauth2client.client.OAuth2Client\n[requests]: https://requests.readthedocs.io/en/latest/\n[requests.session]: https://requests.readthedocs.io/en/latest/api/#requests.Session\n',
    'author': 'Guillaume Pujol',
    'author_email': 'guill.p.linux@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/guillp/requests_oauth2client',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
