diff --git a/src/digid_eherkenning_oidc_generics/digid_urls.py b/src/digid_eherkenning_oidc_generics/digid_urls.py index 8ef8c21a17..8c591eba4a 100644 --- a/src/digid_eherkenning_oidc_generics/digid_urls.py +++ b/src/digid_eherkenning_oidc_generics/digid_urls.py @@ -5,6 +5,7 @@ from .views import ( DigiDOIDCAuthenticationCallbackView, DigiDOIDCAuthenticationRequestView, + DigiDOIDCLogoutView, ) app_name = "digid_oidc" @@ -21,4 +22,9 @@ DigiDOIDCAuthenticationRequestView.as_view(), name="init", ), + path( + "logout/", + DigiDOIDCLogoutView.as_view(), + name="logout", + ), ] + urlpatterns diff --git a/src/digid_eherkenning_oidc_generics/eherkenning_urls.py b/src/digid_eherkenning_oidc_generics/eherkenning_urls.py index c1fbbd4523..453f3ab754 100644 --- a/src/digid_eherkenning_oidc_generics/eherkenning_urls.py +++ b/src/digid_eherkenning_oidc_generics/eherkenning_urls.py @@ -5,6 +5,7 @@ from .views import ( eHerkenningOIDCAuthenticationCallbackView, eHerkenningOIDCAuthenticationRequestView, + eHerkenningOIDCLogoutView, ) app_name = "eherkenning_oidc" @@ -21,4 +22,9 @@ eHerkenningOIDCAuthenticationRequestView.as_view(), name="init", ), + path( + "logout/", + eHerkenningOIDCLogoutView.as_view(), + name="logout", + ), ] + urlpatterns diff --git a/src/digid_eherkenning_oidc_generics/views.py b/src/digid_eherkenning_oidc_generics/views.py index 9ceb876118..6b8cffdd64 100644 --- a/src/digid_eherkenning_oidc_generics/views.py +++ b/src/digid_eherkenning_oidc_generics/views.py @@ -1,8 +1,14 @@ import logging +from django.conf import settings from django.contrib import auth from django.core.exceptions import SuspiciousOperation +from django.http import HttpResponseRedirect +from django.shortcuts import resolve_url +from django.views import View +import requests +from furl import furl from mozilla_django_oidc.views import ( OIDCAuthenticationCallbackView as _OIDCAuthenticationCallbackView, OIDCAuthenticationRequestView as _OIDCAuthenticationRequestView, @@ -86,6 +92,31 @@ def get(self, request): return self.login_failure() +class OIDCLogoutView(View): + def get_success_url(self): + return resolve_url(settings.LOGOUT_REDIRECT_URL) + + def get(self, request): + if "oidc_id_token" in request.session: + logout_endpoint = self.config_class.get_solo().oidc_op_logout_endpoint + if logout_endpoint: + logout_url = furl(logout_endpoint).set( + { + "id_token_hint": request.session["oidc_id_token"], + } + ) + requests.get(str(logout_url)) + + del request.session["oidc_id_token"] + + if "oidc_login_next" in request.session: + del request.session["oidc_login_next"] + + auth.logout(request) + + return HttpResponseRedirect(self.get_success_url()) + + class DigiDOIDCAuthenticationRequestView( SoloConfigDigiDMixin, OIDCAuthenticationRequestView ): @@ -98,6 +129,10 @@ class DigiDOIDCAuthenticationCallbackView( auth_backend_class = OIDCAuthenticationDigiDBackend +class DigiDOIDCLogoutView(SoloConfigDigiDMixin, OIDCLogoutView): + pass + + class eHerkenningOIDCAuthenticationRequestView( SoloConfigEHerkenningMixin, OIDCAuthenticationRequestView ): @@ -108,3 +143,7 @@ class eHerkenningOIDCAuthenticationCallbackView( SoloConfigEHerkenningMixin, OIDCAuthenticationCallbackView ): auth_backend_class = OIDCAuthenticationEHerkenningBackend + + +class eHerkenningOIDCLogoutView(SoloConfigEHerkenningMixin, OIDCLogoutView): + pass diff --git a/src/open_inwoner/accounts/models.py b/src/open_inwoner/accounts/models.py index a7b146297e..53b8360831 100644 --- a/src/open_inwoner/accounts/models.py +++ b/src/open_inwoner/accounts/models.py @@ -19,6 +19,10 @@ from privates.storages import PrivateMediaFileSystemStorage from timeline_logger.models import TimelineLog +from digid_eherkenning_oidc_generics.models import ( + OpenIDConnectDigiDConfig, + OpenIDConnectEHerkenningConfig, +) from open_inwoner.utils.hash import create_sha256_hash from open_inwoner.utils.validators import ( CharFieldValidator, @@ -399,11 +403,22 @@ def require_necessary_fields(self) -> bool: return False def get_logout_url(self) -> str: - return ( - reverse("digid:logout") - if self.login_type == LoginTypeChoices.digid - else reverse("logout") - ) + # Exit early, because for some reason reverse("logout") fails after checking + # the singletonmodels + if self.login_type not in [ + LoginTypeChoices.digid, + LoginTypeChoices.eherkenning, + ]: + return reverse("logout") + + if self.login_type == LoginTypeChoices.digid: + if OpenIDConnectDigiDConfig.get_solo().enabled: + return reverse("digid_oidc:logout") + return reverse("digid:logout") + elif self.login_type == LoginTypeChoices.eherkenning: + if OpenIDConnectEHerkenningConfig.get_solo().enabled: + return reverse("eherkenning_oidc:logout") + return reverse("eherkenning:logout") def get_contact_update_url(self): return reverse("profile:contact_edit", kwargs={"uuid": self.uuid}) diff --git a/src/open_inwoner/conf/base.py b/src/open_inwoner/conf/base.py index 0a66a9e5c1..3f18a0d368 100644 --- a/src/open_inwoner/conf/base.py +++ b/src/open_inwoner/conf/base.py @@ -885,6 +885,8 @@ OIDC_AUTHENTICATE_CLASS = "mozilla_django_oidc_db.views.OIDCAuthenticationRequestView" OIDC_CALLBACK_CLASS = "mozilla_django_oidc_db.views.OIDCCallbackView" OIDC_AUTHENTICATION_CALLBACK_URL = "oidc_authentication_callback" +# ID token is required to enable OIDC logout +OIDC_STORE_ID_TOKEN = True MOZILLA_DJANGO_OIDC_DB_CACHE = "oidc" MOZILLA_DJANGO_OIDC_DB_CACHE_TIMEOUT = 1