Skip to content

Commit 28260d0

Browse files
author
Paul Schilling
committed
[#2932] Update eHerkenning OIDC flow: get & store vestigingsnummer
- When logging in with eHerkenning via OIDC, get the vestigingsnummer from the OIDC claim (if present) and store in session
1 parent 235ec5a commit 28260d0

File tree

5 files changed

+107
-2
lines changed

5 files changed

+107
-2
lines changed

src/eherkenning/backends.py

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ class eHerkenningBackend(_eHerkenningBackend):
1212
Custom backend to identify users based on the KvK number instead of RSIN
1313
"""
1414

15+
# TODO: get vestigingsnummer from saml_response
16+
1517
def get_or_create_user(self, request, saml_response, saml_attributes):
1618
kvk = self.get_kvk_number(saml_attributes)
1719
if kvk == "":

src/eherkenning/mock/backends.py

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class eHerkenningBackend(BaseBackend):
2222
}
2323
)
2424

25+
# TODO: update mock to test retrieval/storage of vestigingsnummer
26+
2527
def get_or_create_user(self, request, kvk):
2628
created = False
2729
try:

src/open_inwoner/accounts/backends.py

+39-2
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,21 @@
55
from django.contrib.auth import get_user_model
66
from django.contrib.auth.backends import ModelBackend
77
from django.contrib.auth.hashers import check_password
8+
from django.contrib.auth.models import AbstractUser
9+
from django.core.exceptions import SuspiciousOperation
810
from django.urls import reverse, reverse_lazy
911

1012
from axes.backends import AxesBackend
1113
from digid_eherkenning.oidc.backends import BaseBackend
1214
from mozilla_django_oidc_db.backends import OIDCAuthenticationBackend
1315
from mozilla_django_oidc_db.config import dynamic_setting
16+
from mozilla_django_oidc_db.typing import JSONObject
1417
from oath import accept_totp
1518

1619
from open_inwoner.configurations.models import SiteConfiguration
20+
from open_inwoner.kvk.branches import KVK_BRANCH_SESSION_VARIABLE
1721
from open_inwoner.utils.hash import generate_email_from_string
22+
from open_inwoner.utils.views import LogMixin
1823

1924
from .choices import LoginTypeChoices
2025
from .models import OpenIDDigiDConfig, OpenIDEHerkenningConfig
@@ -147,7 +152,7 @@ def filter_users_by_claims(self, claims):
147152
return self.UserModel.objects.filter(**{"oidc_id__iexact": unique_id})
148153

149154

150-
class DigiDEHerkenningOIDCBackend(BaseBackend):
155+
class DigiDEHerkenningOIDCBackend(LogMixin, BaseBackend):
151156
OIP_UNIQUE_ID_USER_FIELDNAME = dynamic_setting[Literal["bsn", "kvk"]]()
152157
OIP_LOGIN_TYPE = dynamic_setting[LoginTypeChoices]()
153158

@@ -158,6 +163,26 @@ def _check_candidate_backend(self) -> bool:
158163
OpenIDEHerkenningConfig,
159164
)
160165

166+
def _store_vestigingsnummer_in_session(self, claims: JSONObject):
167+
"""Get company vestigingsnummer from OIDC claims & store in session"""
168+
169+
eherkenning_config = self.config_class.get_solo()
170+
171+
branch_number_claim = eherkenning_config.branch_number_claim[0]
172+
if not (vestigingsnummer := claims.get(branch_number_claim)):
173+
return
174+
175+
self.request.session[KVK_BRANCH_SESSION_VARIABLE] = vestigingsnummer
176+
self.request.session.save()
177+
178+
identifier_claim = eherkenning_config.identifier_type_claim[0]
179+
kvk_or_rsin = claims.get(identifier_claim)
180+
181+
self.log_system_action(
182+
f"Vestigingsnummer {vestigingsnummer} retrieved from IdP for "
183+
f"eHerkenning user (KVK/RSIN: {kvk_or_rsin})"
184+
)
185+
161186
def filter_users_by_claims(self, claims):
162187
"""Return all users matching the specified subject."""
163188
unique_id = self._extract_username(claims)
@@ -169,7 +194,11 @@ def filter_users_by_claims(self, claims):
169194
)
170195

171196
def create_user(self, claims):
172-
"""Return object for a newly created user account."""
197+
"""
198+
Return object for a newly created user account.
199+
200+
Get vestigingsnummer from OIDC claims & store in session
201+
"""
173202

174203
unique_id = self._extract_username(claims)
175204

@@ -185,4 +214,12 @@ def create_user(self, claims):
185214
}
186215
)
187216

217+
if self.config_class is OpenIDEHerkenningConfig:
218+
self._store_vestigingsnummer_in_session(claims)
219+
188220
return user
221+
222+
def update_user(self, user: AbstractUser, claims: JSONObject):
223+
if self.config_class is OpenIDEHerkenningConfig:
224+
self._store_vestigingsnummer_in_session(claims)
225+
return super().update_user(user, claims)

src/open_inwoner/accounts/tests/test_oidc_views.py

+63
Original file line numberDiff line numberDiff line change
@@ -1828,3 +1828,66 @@ def test_redirect_after_login_no_registration_and_no_branch_selection(
18281828
profile_response = self.app.get(profile_response.url)
18291829

18301830
self.assertEqual(profile_response.status_code, 200)
1831+
1832+
@patch("open_inwoner.kvk.client.KvKClient.get_all_company_branches")
1833+
@patch("open_inwoner.utils.context_processors.SiteConfiguration")
1834+
@patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_userinfo")
1835+
@patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.store_tokens")
1836+
@patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.verify_token")
1837+
@patch("mozilla_django_oidc_db.backends.OIDCAuthenticationBackend.get_token")
1838+
@patch(
1839+
"open_inwoner.accounts.models.OpenIDEHerkenningConfig.get_solo",
1840+
return_value=OpenIDEHerkenningConfig(
1841+
id=1,
1842+
enabled=True,
1843+
legal_subject_claim=["kvk"],
1844+
oidc_op_authorization_endpoint="http://idp.local/auth",
1845+
),
1846+
)
1847+
def test_redirect_after_login_branch_already_selected(
1848+
self,
1849+
mock_get_solo,
1850+
mock_get_token,
1851+
mock_verify_token,
1852+
mock_store_tokens,
1853+
mock_get_userinfo,
1854+
mock_siteconfig,
1855+
mock_kvk,
1856+
):
1857+
"""
1858+
KVK branch selection should be skipped if KVK_BRANCH_SESSION_VARIABLE is present in session
1859+
"""
1860+
user = eHerkenningUserFactory.create(kvk="12345678", rsin="123456789")
1861+
mock_get_userinfo.return_value = {
1862+
"sub": "some_username",
1863+
"kvk": "12345678",
1864+
"urn:etoegang:1.9:ServiceRestriction:Vestigingsnr": "123456789000",
1865+
}
1866+
mock_siteconfig.return_value = SiteConfiguration(id=1, eherkenning_enabled=True)
1867+
mock_kvk.return_value = [
1868+
{"kvkNummer": "12345678"},
1869+
{"kvkNummer": "87654321"},
1870+
]
1871+
1872+
self.assertEqual(User.objects.count(), 1)
1873+
1874+
redirect_url = reverse("profile:detail")
1875+
1876+
callback_response = perform_oidc_login(
1877+
self.app, "eherkenning", redirect_url=redirect_url
1878+
)
1879+
1880+
user = User.objects.get()
1881+
1882+
self.assertEqual(user.pk, int(self.app.session.get("_auth_user_id")))
1883+
self.assertEqual(user.kvk, "12345678")
1884+
self.assertEqual(
1885+
self.app.session.get(KVK_BRANCH_SESSION_VARIABLE), "123456789000"
1886+
)
1887+
1888+
self.assertRedirects(
1889+
callback_response, reverse("profile:detail"), fetch_redirect_response=False
1890+
)
1891+
1892+
response = self.app.get(callback_response.url)
1893+
self.assertEqual(response.status_code, 200)

src/open_inwoner/kvk/views.py

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ def get(self, request, *args, **kwargs):
5959
return HttpResponse(_("Unauthorized"), status=401)
6060

6161
redirect = self.get_redirect()
62+
6263
context = super().get_context_data()
6364

6465
form = context["form"]

0 commit comments

Comments
 (0)