diff --git a/src/openforms/authentication/contrib/digid_eherkenning_oidc/backends.py b/src/openforms/authentication/contrib/digid_eherkenning_oidc/backends.py index b515b6880c..d0fff616b7 100644 --- a/src/openforms/authentication/contrib/digid_eherkenning_oidc/backends.py +++ b/src/openforms/authentication/contrib/digid_eherkenning_oidc/backends.py @@ -10,6 +10,7 @@ from digid_eherkenning_oidc_generics.mixins import ( SoloConfigDigiDMachtigenMixin, SoloConfigDigiDMixin, + SoloConfigEHerkenningBewindvoeringMixin, SoloConfigEHerkenningMixin, ) from digid_eherkenning_oidc_generics.utils import obfuscate_claim @@ -17,6 +18,7 @@ from .constants import ( DIGID_MACHTIGEN_OIDC_AUTH_SESSION_KEY, DIGID_OIDC_AUTH_SESSION_KEY, + EHERKENNING_BEWINDVOERING_OIDC_AUTH_SESSION_KEY, EHERKENNING_OIDC_AUTH_SESSION_KEY, ) @@ -103,3 +105,65 @@ def verify_claims(self, claims: dict) -> bool: return False return True + + +class OIDCAuthenticationEHerkenningBewindvoeringBackend( + SoloConfigEHerkenningBewindvoeringMixin, OIDCAuthenticationBackend +): + session_key = EHERKENNING_BEWINDVOERING_OIDC_AUTH_SESSION_KEY + + def get_or_create_user(self, access_token, id_token, payload): + claims_verified = self.verify_claims(payload) + if not claims_verified: + msg = "Claims verification failed" + raise SuspiciousOperation(msg) + + self.extract_claims(payload) + + user = AnonymousUser() + user.is_active = True + return user + + @property + def claim_names(self): + return [ + self.config.vertegenwoordigde_company_claim_name, + self.config.vertegenwoordigde_person_claim_name, + self.config.gemachtigde_person_claim_name, + ] + + def extract_claims(self, payload: dict) -> None: + self.request.session[self.session_key] = {} + for claim_name in self.claim_names: + self.request.session[self.session_key][claim_name] = glom( + payload, claim_name + ) + + def log_received_claims(self, claims: dict): + copied_claims = deepcopy(claims) + + def _obfuscate_claims_values(claims_to_obfuscate: dict) -> dict: + for key, value in claims_to_obfuscate.items(): + if isinstance(value, dict): + _obfuscate_claims_values(value) + else: + claims_to_obfuscate[key] = obfuscate_claim(value) + return claims_to_obfuscate + + obfuscated_claims = _obfuscate_claims_values(copied_claims) + logger.debug("OIDC claims received: %s", obfuscated_claims) + + def verify_claims(self, claims: dict) -> bool: + self.log_received_claims(claims) + + for expected_claim in self.claim_names: + try: + glom(claims, expected_claim) + except PathAccessError: + logger.error( + "`%s` not in OIDC claims, cannot proceed with eHerkenning bewindvoering authentication", + expected_claim, + ) + return False + + return True diff --git a/src/openforms/authentication/contrib/digid_eherkenning_oidc/constants.py b/src/openforms/authentication/contrib/digid_eherkenning_oidc/constants.py index af3cb390cb..ce6ead1307 100644 --- a/src/openforms/authentication/contrib/digid_eherkenning_oidc/constants.py +++ b/src/openforms/authentication/contrib/digid_eherkenning_oidc/constants.py @@ -1,3 +1,6 @@ DIGID_OIDC_AUTH_SESSION_KEY = "digid_oidc:bsn" EHERKENNING_OIDC_AUTH_SESSION_KEY = "eherkenning_oidc:kvk" DIGID_MACHTIGEN_OIDC_AUTH_SESSION_KEY = "digid_machtigen_oidc:machtigen" +EHERKENNING_BEWINDVOERING_OIDC_AUTH_SESSION_KEY = ( + "eherkenning_bewindvoering_oidc:machtigen" +) diff --git a/src/openforms/authentication/contrib/digid_eherkenning_oidc/eherkenning_bewindvoering_urls.py b/src/openforms/authentication/contrib/digid_eherkenning_oidc/eherkenning_bewindvoering_urls.py new file mode 100644 index 0000000000..12ad7c6984 --- /dev/null +++ b/src/openforms/authentication/contrib/digid_eherkenning_oidc/eherkenning_bewindvoering_urls.py @@ -0,0 +1,24 @@ +from django.urls import path + +from mozilla_django_oidc.urls import urlpatterns + +from .views import ( + EHerkenningBewindvoeringOIDCAuthenticationCallbackView, + EHerkenningBewindvoeringOIDCAuthenticationRequestView, +) + +app_name = "eherkenning_bewindvoering_oidc" + + +urlpatterns = [ + path( + "callback/", + EHerkenningBewindvoeringOIDCAuthenticationCallbackView.as_view(), + name="callback", + ), + path( + "authenticate/", + EHerkenningBewindvoeringOIDCAuthenticationRequestView.as_view(), + name="init", + ), +] + urlpatterns diff --git a/src/openforms/authentication/contrib/digid_eherkenning_oidc/plugin.py b/src/openforms/authentication/contrib/digid_eherkenning_oidc/plugin.py index 35b6fbab9f..ae3fefd9aa 100644 --- a/src/openforms/authentication/contrib/digid_eherkenning_oidc/plugin.py +++ b/src/openforms/authentication/contrib/digid_eherkenning_oidc/plugin.py @@ -9,6 +9,7 @@ from digid_eherkenning_oidc_generics.models import ( OpenIDConnectDigiDMachtigenConfig, + OpenIDConnectEHerkenningBewindvoeringConfig, OpenIDConnectEHerkenningConfig, OpenIDConnectPublicConfig, ) @@ -25,6 +26,7 @@ from .constants import ( DIGID_MACHTIGEN_OIDC_AUTH_SESSION_KEY, DIGID_OIDC_AUTH_SESSION_KEY, + EHERKENNING_BEWINDVOERING_OIDC_AUTH_SESSION_KEY, EHERKENNING_OIDC_AUTH_SESSION_KEY, ) @@ -163,3 +165,31 @@ def get_label(self) -> str: def get_logo(self, request) -> Optional[LoginLogo]: return LoginLogo(title=self.get_label(), **get_digid_logo(request)) + + +@register("eherkenning_bewindvoering_oidc") +class EHerkenningBewindvoeringOIDCAuthentication(OIDCAuthentication): + verbose_name = _("eHerkenning bewindvoering via OpenID Connect") + provides_auth = AuthAttribute.kvk + init_url = "eherkenning_bewindvoering_oidc:init" + session_key = EHERKENNING_BEWINDVOERING_OIDC_AUTH_SESSION_KEY + config_class = OpenIDConnectEHerkenningBewindvoeringConfig + + def add_claims_to_sessions_if_not_cosigning(self, claim, request): + # set the session auth key only if we're not co-signing + if claim and CO_SIGN_PARAMETER not in request.GET: + config = self.config_class.get_solo() + request.session[FORM_AUTH_SESSION_KEY] = { + "plugin": self.identifier, + "attribute": self.provides_auth, + "value": claim[config.vertegenwoordigde_company_claim_name], + "machtigen": request.session[ + EHERKENNING_BEWINDVOERING_OIDC_AUTH_SESSION_KEY + ], + } + + def get_label(self) -> str: + return "eHerkenning bewindvoering" + + def get_logo(self, request) -> Optional[LoginLogo]: + return LoginLogo(title=self.get_label(), **get_eherkenning_logo(request)) diff --git a/src/openforms/authentication/contrib/digid_eherkenning_oidc/views.py b/src/openforms/authentication/contrib/digid_eherkenning_oidc/views.py index f4068f25a5..58ac0094bb 100644 --- a/src/openforms/authentication/contrib/digid_eherkenning_oidc/views.py +++ b/src/openforms/authentication/contrib/digid_eherkenning_oidc/views.py @@ -10,6 +10,7 @@ from digid_eherkenning_oidc_generics.mixins import ( SoloConfigDigiDMachtigenMixin, SoloConfigDigiDMixin, + SoloConfigEHerkenningBewindvoeringMixin, SoloConfigEHerkenningMixin, ) from digid_eherkenning_oidc_generics.views import ( @@ -22,6 +23,7 @@ OIDCAuthenticationDigiDBackend, OIDCAuthenticationDigiDMachtigenBackend, OIDCAuthenticationEHerkenningBackend, + OIDCAuthenticationEHerkenningBewindvoeringBackend, ) logger = logging.getLogger(__name__) @@ -107,3 +109,16 @@ class DigiDMachtigenOIDCAuthenticationCallbackView( ): plugin_identifier = "digid_machtigen_oidc" auth_backend_class = OIDCAuthenticationDigiDMachtigenBackend + + +class EHerkenningBewindvoeringOIDCAuthenticationRequestView( + SoloConfigEHerkenningBewindvoeringMixin, OIDCAuthenticationRequestView +): + plugin_identifier = "eherkenning_bewindvoering_oidc" + + +class EHerkenningBewindvoeringOIDCAuthenticationCallbackView( + SoloConfigEHerkenningBewindvoeringMixin, OIDCAuthenticationCallbackView +): + plugin_identifier = "eherkenning_bewindvoering_oidc" + auth_backend_class = OIDCAuthenticationEHerkenningBewindvoeringBackend diff --git a/src/openforms/urls.py b/src/openforms/urls.py index 8a11ecbdc6..4324a77383 100644 --- a/src/openforms/urls.py +++ b/src/openforms/urls.py @@ -91,6 +91,12 @@ "openforms.authentication.contrib.digid_eherkenning_oidc.digid_machtigen_urls", ), ), + path( + "eherkenning-bewindvoering-oidc/", + include( + "openforms.authentication.contrib.digid_eherkenning_oidc.eherkenning_bewindvoering_urls", + ), + ), path("payment/", include("openforms.payments.urls", namespace="payments")), # NOTE: we dont use the User creation feature so don't enable all the mock views path("digid/", include("openforms.authentication.contrib.digid.urls")),