@@ -29,8 +29,25 @@ case class CallContext(
2929 dauthRequestPayload : Option [JSONFactoryDAuth .PayloadOfJwtJSON ] = None , // Never update these values inside the case class !!!
3030 dauthResponseHeader : Option [String ] = None ,
3131 spelling : Option [String ] = None ,
32+ // The AUTHENTICATED principal. Not always a person: under a consent this is the
33+ // consent's own shadow user. Stored data (metric rows, created_by_user_id columns)
34+ // always records this id — the human is resolved at read time via the consent table.
3235 user : Box [User ] = Empty ,
36+ // The human who CREATED the consent this request runs under. Populated only by the
37+ // OBP-native consent path (applyConsentRulesCommon) from the consent JWT's
38+ // createdByUserId claim, resolved against the users table. For OBP-native consents
39+ // the creator is the granting human (they create their own consent in the Portal).
40+ // Not set by Berlin Group / UK flows, where the consent may be created by a TPP flow
41+ // with no human logged in — see `consenter` for those.
42+ // Read via humanUser / effectiveHumanUserId, where it takes precedence over consenter.
3343 onBehalfOfUser : Box [User ] = Empty ,
44+ // The human (PSU) who AUTHORISED the consent this request runs under — the owner of
45+ // record, from the consent table's userId (bound by updateConsentUser during the
46+ // authorise ceremony). Populated by the Berlin Group and UK consent paths, whose
47+ // consents are created by TPP flows and only gain their human at authorisation.
48+ // The UK ownership check (checkUKConsent) compares the consent's userId against this.
49+ // In practice onBehalfOfUser and consenter are never both set: each consent standard
50+ // populates the one whose source is authoritative for it.
3451 consenter : Box [User ] = Empty ,
3552 consumer : Box [Consumer ] = Empty ,
3653 ipAddress : String = " " ,
@@ -94,7 +111,9 @@ case class CallContext(
94111 * `user` is not always a person: a consent resolves to a shadow user that exists only for that
95112 * consent (Berlin Group, OBP-native, and -- since UK consents moved to the same model -- UK too).
96113 * Anything that must name a human rather than a principal reads this instead: the CBS adapter,
97- * which tells the core banking system who is asking, and metric attribution.
114+ * which tells the core banking system who is asking, and the consent ownership checks.
115+ * Stored data (metric rows included) always carries the authenticated principal; the human is
116+ * resolved at read time via the consent table (see effectiveHumanUserId).
98117 */
99118 def humanUser : Box [User ] = onBehalfOfUser.or(consenter).or(user)
100119
@@ -159,12 +178,13 @@ case class CallContext(
159178 CallContextLight (
160179 gatewayLoginRequestPayload = this .gatewayLoginRequestPayload,
161180 gatewayLoginResponseHeader = this .gatewayLoginResponseHeader,
162- // Metrics name the human, not the principal. A consent's shadow user would record a per-consent
163- // UUID and an empty username, which is what Berlin Group and OBP-native traffic has always
164- // looked like on the metrics table; the consent itself stays identifiable via
165- // consentReferenceId below.
166- userId = this .humanUser.map(_.userId).toOption,
167- userName = this .humanUser.map(_.name).toOption,
181+ // Like for like with CallContext: userId/userName are the AUTHENTICATED principal
182+ // (CallContext.user), never a resolved human. Under a consent that principal is the
183+ // consent's own shadow user (a per-consent UUID with an empty name) — the on-behalf-of
184+ // human is not stored here but resolved at read time via the consent table
185+ // (consentReferenceId below -> consent.userId), see CallContext.effectiveHumanUserId.
186+ userId = this .user.map(_.userId).toOption,
187+ userName = this .user.map(_.name).toOption,
168188 consumerId = this .consumer.map(_.consumerId.get).toOption,
169189 appName = this .consumer.map(_.name.get).toOption,
170190 developerEmail = this .consumer.map(_.developerEmail.get).toOption,
0 commit comments