summaryrefslogtreecommitdiff
path: root/docs/usage.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/usage.rst')
-rw-r--r--docs/usage.rst71
1 files changed, 71 insertions, 0 deletions
diff --git a/docs/usage.rst b/docs/usage.rst
index 91d9679..a85fa18 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -297,3 +297,74 @@ Retrieve RSA signing keys from a JWKS endpoint
... )
>>> print(data)
{'iss': 'https://dev-87evx9ru.auth0.com/', 'sub': 'aW4Cca79xReLWUz0aE2H6kD0O3cXBVtC@clients', 'aud': 'https://expenses-api', 'iat': 1572006954, 'exp': 1572006964, 'azp': 'aW4Cca79xReLWUz0aE2H6kD0O3cXBVtC', 'gty': 'client-credentials'}
+
+OIDC Login Flow
+---------------
+
+The following usage demonstrates an OIDC login flow using pyjwt. Further
+reading about the OIDC spec is recommended for implementers.
+
+In particular, this demonstrates validation of the ``at_hash`` claim.
+This claim relies on data from outside of the the JWT for validation. Methods
+are provided which support computation and validation of this claim, but it
+is not built into pyjwt.
+
+.. code-block:: python
+
+ import base64
+ import jwt
+ import requests
+
+
+ # Part 1: setup
+ # get the OIDC config and JWKs to use
+
+ # in OIDC, you must know your client_id (this is the OAuth 2.0 client_id)
+ client_id = ...
+
+ # example of fetching data from your OIDC server
+ # see: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig
+ oidc_server = ...
+ oidc_config = requests.get(
+ f"https://{oidc_server}/.well-known/openid-configuration"
+ ).json()
+ signing_algos = oidc_config["id_token_signing_alg_values_supported"]
+
+ # setup a PyJWKClient to get the appropriate signing key
+ jwks_client = jwt.PyJWKClient(oidc_config["jwks_uri"])
+
+
+ # Part 2: login / authorization
+ # when a user completes an OIDC login flow, there will be a well-formed
+ # response object to parse/handle
+
+ # data from the login flow
+ # see: https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse
+ token_response = ...
+ id_token = token_response["id_token"]
+ access_token = token_response["access_token"]
+
+
+ # Part 3: decode and validate at_hash
+ # after the login is complete, the id_token needs to be decoded
+ # this is the stage at which an OIDC client must verify the at_hash
+
+ # get signing_key from id_token
+ signing_key = jwks_client.get_signing_key_from_jwt(id_token)
+
+ # now, decode_complete to get payload + header
+ data = jwt.decode_complete(
+ id_token,
+ key=signing_key.key,
+ algorithms=signing_algos,
+ audience=client_id,
+ )
+ payload, header = data["payload"], data["header"]
+
+ # get the pyjwt algorithm object
+ alg_obj = jwt.get_algorithm_by_name(header["alg"])
+
+ # compute at_hash, then validate / assert
+ digest = alg_obj.compute_hash_digest(access_token)
+ at_hash = base64.urlsafe_b64encode(digest[: (len(digest) // 2)]).rstrip("=")
+ assert at_hash == payload["at_hash"]