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"