Merge a federated profile's mapped claims into the locally authoritative
claims envelope, under a single precedence rule: the local record wins,
and everything else is namespaced (#279).
Federation is an authentication signal, not an authorization one. The local
account is already resolved — the callback route looked it up by
provider:sub — so any field the local User declares is this deployment's
answer, and an upstream IdP does not get to replace it. Where the local
record is silent on a claim in PROMOTABLE_FEDERATED_CLAIMS, a string
mapped value fills the gap.
The mapped claims are also recorded under FEDERATED_CLAIMS_KEY in
full — the promoted values, the values that lost to a local claim, and the
ones that were never promotable — so the record of what the IdP said stays
complete and stays separate from what this deployment holds. That key is
written only when at least one claim was mapped; see
FEDERATED_CLAIMS_KEY for why absence rather than {}, and for the
presence check a consumer therefore owes it.
Returns a fresh envelope. Neither localClaims nor mappedClaims is
mutated, and the namespaced snapshot is a shallow copy.
Promotion is written as one named read per promotable claim rather than a
loop over PROMOTABLE_FEDERATED_CLAIMS. That is the point: there is no
expression in this function that can carry a key the compiler has not seen
into the top-level envelope, so groups — or a roles an adapter invents —
cannot reach it by any input, only by someone writing a new line here.
mappedClaims is typed unknown on purpose. A federation adapter is a
third-party extension point reached across an untyped boundary; a hostile or
simply broken one returning null, an array or a string must not be able to
corrupt the envelope.
Merge a federated profile's mapped claims into the locally authoritative claims envelope, under a single precedence rule: the local record wins, and everything else is namespaced (#279).
Federation is an authentication signal, not an authorization one. The local account is already resolved — the callback route looked it up by
provider:sub— so any field the localUserdeclares is this deployment's answer, and an upstream IdP does not get to replace it. Where the local record is silent on a claim in PROMOTABLE_FEDERATED_CLAIMS, a string mapped value fills the gap.The mapped claims are also recorded under FEDERATED_CLAIMS_KEY in full — the promoted values, the values that lost to a local claim, and the ones that were never promotable — so the record of what the IdP said stays complete and stays separate from what this deployment holds. That key is written only when at least one claim was mapped; see FEDERATED_CLAIMS_KEY for why absence rather than
{}, and for the presence check a consumer therefore owes it.Returns a fresh envelope. Neither
localClaimsnormappedClaimsis mutated, and the namespaced snapshot is a shallow copy.Promotion is written as one named read per promotable claim rather than a loop over PROMOTABLE_FEDERATED_CLAIMS. That is the point: there is no expression in this function that can carry a key the compiler has not seen into the top-level envelope, so
groups— or arolesan adapter invents — cannot reach it by any input, only by someone writing a new line here.mappedClaimsis typedunknownon purpose. A federation adapter is a third-party extension point reached across an untyped boundary; a hostile or simply broken one returningnull, an array or a string must not be able to corrupt the envelope.