From 468d4c0822312a8bfc6692767b7b6103eb3ddfd8 Mon Sep 17 00:00:00 2001
From: tegwick
Date: Thu, 15 Oct 2026 00:47:38 +0200
Subject: [PATCH] USER-WP-0039: show the account-site session's assurance
beside its uid (NK-WP-0051-T03)
The account-site line shows its KeyCape token's aal1/aal2. Authelia's own
authentication_level is not shown: KeyCape checks the one-time code, so
Authelia reports level 1 even for an aal2 sign-in.
Co-Authored-By: Claude Opus 5.5
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1041694@bnt-lap001
Assistant-Session: 7af0bb68-ac4a-4ce3-8639-24ddd84c0b52
---
src/user_engine/web.py | 18 +++++++++++++++++-
tests/test_account_identity_disclosure.py | 9 +++++++--
2 files changed, 24 insertions(+), 3 deletions(-)
diff --git a/src/user_engine/web.py b/src/user_engine/web.py
index 1366ced..7b338fa 100644
--- a/src/user_engine/web.py
+++ b/src/user_engine/web.py
@@ -68,6 +68,19 @@ def _jsonable(value: Any) -> Any:
return value
+def _assurance_text(level: object) -> str:
+ """Name the account-site session's level from its KeyCape token.
+
+ Authelia's own authentication_level is not shown: KeyCape checks the
+ one-time code, so Authelia reports level 1 even for an aal2 sign-in.
+ """
+ if level == "aal2":
+ return ' · aal2 one-time code verified'
+ if level == "aal1":
+ return ' · aal1 password only'
+ return ""
+
+
class PortalApplication:
"""Small, auditable HTTP adapter over :class:`UserEngineService`."""
@@ -2343,10 +2356,13 @@ Use the login name they provide; it may differ from your display name.
'
"Account site session uid"
- f'{escape(account_uid)}'
+ f'{escape(account_uid)}'
+ f'{account_level}'
"Authelia session uid (shared NetKingdom sign-in)"
+ (
f'{escape(authelia_uid)}'
diff --git a/tests/test_account_identity_disclosure.py b/tests/test_account_identity_disclosure.py
index 3458d50..746ecd2 100644
--- a/tests/test_account_identity_disclosure.py
+++ b/tests/test_account_identity_disclosure.py
@@ -73,7 +73,7 @@ class AccountIdentityDisclosureTests(unittest.TestCase):
for path in ["/", "/onboarding"]:
with self.subTest(path=path):
body = self._uid_lines(path, "sample.user")
- self.assertIn(b'sample.user', body)
+ self.assertIn(b'sample.user · ', body)
self.assertIn(b'sample.user', body)
self.assertIn(b"Account site session uid", body)
self.assertIn(b"Authelia session uid", body)
@@ -84,7 +84,7 @@ class AccountIdentityDisclosureTests(unittest.TestCase):
for path in ["/", "/onboarding"]:
with self.subTest(path=path):
body = self._uid_lines(path, "bernd.worsch-3")
- self.assertIn(b'sample.user', body)
+ self.assertIn(b'sample.user · ', body)
self.assertIn(b'bernd.worsch-3', body)
self.assertIn(b'role="alert" class="session-uid-mismatch"', body)
self.assertIn(b"These sessions differ.", body)
@@ -105,6 +105,11 @@ class AccountIdentityDisclosureTests(unittest.TestCase):
self.assertNotIn(b"session-uid-mismatch", body)
self.assertNotIn(b"