Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a18e2d-f513-7742-b8e7-12af0b744ff3
3.2 KiB
Bind UserInfo to the verified sign-in identity
WP-0015-T04 source correction. No native issuer reset, LDAP password change, production deployment or new productive acceptance is claimed. UC-009 retains its existing personal acceptance and remains an unestablished automated journey. The sealed storage proposal and its candidate image are unchanged; this source correction requires a subsequent reviewed release before productive use.
The callback previously merged arbitrary UserInfo values over verified ID-token
claims and accepted a token-response userinfo object when no ID token existed.
Signed-token callback reproductions demonstrated that a mismatched UserInfo
subject could establish a session, and a matching subject with altered issuer
could create a member under that replacement issuer. Seven of eight initial
reproduction cases failed before the fix; the valid-token/no-UserInfo case passed.
These are isolated reproductions, not evidence that production has been exploited.
The assembler now requires an ID token and the existing signature/issuer/audience/ expiry validation. Nonempty UserInfo must carry exactly the same string subject. It may update only the three profile fields used by this app: name, email and preferred username. Identity, tenant, client binding, groups/roles and assurance remain the signed token's assertions. UserInfo cannot satisfy a requested AAL2 step-up over an AAL1 token. An unavailable/401 UserInfo endpoint still allows login from a valid signed token, preserving the existing accepted fallback.
OpenID Connect Core §5.3.2 requires exact subject equality before UserInfo values are used. The profile-only merge is the app's stricter IAM boundary, preserving its verified provenance. KeyCape's inspected owner implementation returns its canonical token subject and profile information from UserInfo; no provider modification is needed here.
tests/test_oidc_userinfo_binding.py uses ephemeral RSA keys and actual JWT/JWKS
verification in the real Django callback. Only the code exchange and HTTP peer
are synthetic; every HTTP request must name an exact fixture endpoint. No socket,
real account, credential, reset URL or production provider is used. Thirteen cases
cover mismatched/missing/non-string subjects, absent/empty/non-string ID tokens,
profile enrichment with conflicting authority values, optional UserInfo absence,
step-up refusal and valid UserInfo failing to rescue wrong issuer/audience,
expired tokens or an empty signed subject. Rejection creates neither Member nor
authenticated session and consumes the callback state. Existing profile-enrichment
test data now includes the provider-required subject.
Twenty focused identity tests and all 396 full make check cases pass, with
zero failures or skips and mandatory browser journeys included. The companion JSON records the gate,
source/test hashes and explicit execution boundaries. UC-009's registry links
the new callback checks without promoting the journey or changing its accepted
contract. The complete isolated native reset→directory→inbox→app browser journey,
native release and owner/productive review remain open on WP-0015-T04.