Skip to content

How to use Optional parameters for Authenticate Request in open_id_connect.py backend #1305

@mbucknell

Description

@mbucknell

I have an implementation of open_id_connect that uses acr_values. I had created my own backend with auth_params overriden to set params["acr_values"] = self.setting("ACR_VALUES") which is retrieved from the openid-configurations call. I am not sure how to set the configuration so that open_id_connect's auth_params doesn't raise AuthNotImplementedParameter for my acr_values.

I have included below my backend class:

import base64
import time

from jose import jwk, jwt
from jose.utils import base64url_decode

from django.core.management.utils import get_random_secret_key

from social_core.backends.open_id_connect import OpenIdConnectAuth


class LoginDotGov(OpenIdConnectAuth):
    name = "login-gov"

    def auth_html(self):
        """Abstract Method Inclusion"""

    def oidc_config(self):
        return self.get_json(
            self.setting("OIDC_ENDPOINT") + "/.well-known/openid-configuration"
        )

    def get_key_and_secret(self):
        client_id, client_secret = super().get_key_and_secret()
        decoded_client_secret = base64.b64decode(client_secret)
        return client_id, decoded_client_secret

    def auth_params(self, state=None):
        params = super().auth_params(state)
        params["acr_values"] = self.setting("ACR_VALUES")
        return params

    def auth_complete_params(self, state=None):
        client_id, client_secret = self.get_key_and_secret()
        client_assertion = jwt.encode(
            {
                "iss": client_id,
                "sub": client_id,
                "aud": self.access_token_url(),
                "jti": get_random_secret_key(),
                "exp": int(time.time() + 5),
            },
            client_secret,
            algorithm="RS256",
        )

        return {
            "grant_type": "authorization_code",
            "code": self.data.get("code", ""),  # server response code
            "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
            "client_assertion": client_assertion,
        }

    def find_valid_key(self, id_token):
        kid = jwt.get_unverified_header(id_token).get("kid")

        keys = self.get_jwks_keys()
        if kid is not None:
            for key in keys:
                if kid == key.get("kid"):
                    break
            else:
                # In case the key id is not found in the cached keys, just
                # reload the JWKS keys. Ideally this should be done by
                # invalidating the cache.
                self.get_jwks_keys.invalidate()
                keys = self.get_jwks_keys()

        for key in keys:
            if kid is None or kid == key.get("kid"):
                key["alg"] = "RS256"
                rsakey = jwk.construct(key)
                message, encoded_sig = id_token.rsplit(".", 1)
                decoded_sig = base64url_decode(encoded_sig.encode("utf-8"))
                if rsakey.verify(message.encode("utf-8"), decoded_sig):
                    return key
        return None

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions