Search code examples
pythondjangodjango-oauth-toolkit

How to edit claims in ID Token supplied with access token in django Oauth toolkit?


When working with Django OAuth Toolkit, using OIDC if you supply openid as the scope in request you get a id_token with access token. This ID Token can initially be used to identify the user you have got access token for and also create a session.

This is what i am getting in JWT ID Token

{
 "aud": "audience",
 "iat": 1681801110,
 "at_hash": "hash of included access token",
 "sub": "1",
 "given_name": "Dushyant",
 "preferred_username": "dsh",
 "iss": "authEndpointurl",
 "exp": 1681837110,
 "auth_time": 1681567804,
 "jti": "tokenid"
}

But I wish to modify the claims in this JWT ID Token, it reveals the primary key of the database of Authorization Server as the unique id of the user which i don't want in the claim called sub. I want to use another unique key as the value of sub claim. I tried overriding the response a Custom Backend:

class CustomOAuthBackend(OAuthLibCore):
def __init__(self, server):
    super().__init__(server)
    
def create_token_response(self, request):
    response_data = super().create_token_response(request)
    #Modify the response here
    return response_data

And added this as the Custom Backend in the settings. "OAUTH2_BACKEND_CLASS":"pathto.CustomOAuthBackend",

I haven't written any modification code for the response here since the token here is already created When I am calling the original create_token_response.

I want to override it in a way that I can modify the claims dict, I am not sure where it is getting created. We get the id_token all prepared. I want to override the process of creating id_token and change sub claim value.

Let me know if any more information is required.

Update 1: While looking for a possible solution, found oauth2_provider.oauth2_validators have methods get_oidc_claims() and get_id_token_dictionary() which looks like could lead to a possible solution. Figuring out how to use it.

There is a write up in the code

This method is OPTIONAL and is NOT RECOMMENDED. finalize_id_token SHOULD be implemented instead. However, if you want a full control over the minting of the id_token, you MAY want to override get_id_token instead of using finalize_id_token.

This write up talks about get_id_token() method. Going through the code I see that finalize_id_token() is the method where JWT is created from claims. This method calls get_id_token_dictionary then this internally calls get_oidc_claims() then this calls get_claim_dict(request). get_claim_dict(request) only takes a request while others take token etc also. This method looks correct to override as this actually adds that sub claim to claims.

def get_claim_dict(self, request):
    if self._get_additional_claims_is_request_agnostic():
        claims = {"sub": lambda r: str(r.user.id)}
    else:
        claims = {"sub": str(request.user.id)}

    # https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
    if self._get_additional_claims_is_request_agnostic():
        add = self.get_additional_claims()
    else:
        add = self.get_additional_claims(request)
    claims.update(add)

    return claims

I can get claims from this modify it and return the new updated claims.


Solution

  • I was able to do it by the same process I mentioned in the question. At the end I override get_claim_dict(self, request) method.

    class CustomOAuth2Validator(OAuth2Validator):
    
      def get_claim_dict(self, request):
           claims = super().get_claim_dict(request)
           claims["sub"] = lambda request: get_user_guid(request=request)
           return claims
    

    I override the get_claim_dict in OAuth2Validator. Add this validator as custom validator in django settings "OAUTH2_VALIDATOR_CLASS": "pathTo.CustomOAuth2Validator",

    On decoding the JWT ID Token you will get the new value.

    Got New Sub Value