The creation of ID Tokens is ultimately not done by OAuthLib but by your
RequestValidator subclass. This is because their
content is dependent on your implementation of users, their attributes, any claims you may wish to support, as well as the
details of how you model the notion of a Client Application. As such OAuthLib simply calls your validator’s
method at the appropriate times during the authorization flow, depending on the grant type requested (Authorization Code, Implicit,
See examples below.
finalize_id_token(id_token, token, token_handler, request)
Finalize OpenID Connect ID token & Sign or Encrypt.
In the OpenID Connect workflows when an ID Token is requested this method is called. Subclasses should implement the construction, signing and optional encryption of the ID Token as described in the OpenID Connect spec.
The id_token parameter is a dict containing a couple of OIDC technical fields related to the specification. Prepopulated attributes are:
- aud, equals to request.client_id.
- iat, equals to current time.
- nonce, if present, is equals to the nonce from the authorization request.
- at_hash, hash of access_token, if relevant.
- c_hash, hash of code, if relevant.
This method MUST provide required fields as below:
- iss, REQUIRED. Issuer Identifier for the Issuer of the response.
- sub, REQUIRED. Subject Identifier
- exp, REQUIRED. Expiration time on or after which the ID Token MUST NOT be accepted by the RP when performing authentication with the OP.
Additional claims must be added, note that request.scope should be used to determine the list of claims.
More information can be found at OpenID Connect Core#Claims
- id_token – A dict containing technical fields of id_token
- token – A Bearer token dict
- token_handler – the token handler (BearerToken class)
- request (oauthlib.common.Request) – OAuthlib request.
The ID Token (a JWS signed JWT or JWE encrypted JWT)
JWT/JWS example with pyjwt library¶
An example below using Cryptography library to load the private key and PyJWT to sign the JWT. Note that the claims list in the “data” dict must be set accordingly to the auth request.
You can switch to jwcrypto library if you want to return JWE instead.
class MyValidator(RequestValidator): def __init__(self, **kwargs): with open(path.join(path.dirname(path.realpath(__file__)), "./id_rsa"), 'rb') as fd: from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import serialization self.private_pem = serialization.load_pem_private_key( fd.read(), password=None, backend=default_backend() ) super().__init__(self, **kwargs) def finalize_id_token(self, id_token, token, token_handler, request): import jwt id_token["iss"] = "https://my.cool.app.com" id_token["sub"] = request.user.id id_token["exp"] = id_token["iat"] + 3600 * 24 # keep it valid for 24hours for claim_key in request.claims: id_token[claim_key] = request.userattributes[claim_key] # this must be set in another callback return jwt.encode(id_token, self.private_pem, 'RS256')