Reading and validating aggregated claims
All domains, identifiers, and tokens in these examples are fictional.
The printer has your UserInfo response with Northfield's statement inside it. Its sign-in code has already checked the response the usual way: it came from the photo service over HTTPS, in answer to the printer's own access token, and its sub matches the ID token. The discount now depends on the embedded JWT.
Decoding that JWT takes one line of code, and the decoded claim says true. Applying the discount at that point would mean trusting a statement nobody has checked. The photo service did not sign it, and nothing so far shows that Northfield did either. To the printer, the JWT is a string inside the response, and a string can say anything, including a header with "alg": "none" and no signature at all.
Two signers, two decisions
The response combines statements from two parties, and the printer trusts each one for something different:
| Question | Who answers it | How the printer checks |
|---|---|---|
| Which photo account signed in? | The photo service | The validated ID token, and a UserInfo response whose sub matches it. |
| Whose enrollment does the statement describe? | The photo service, by placing the statement in this response | The same checks. Northfield's JWT names no one. |
| Is that person enrolled, and until when? | Northfield | Northfield's signature, issuer, and expiry, checked against the printer's own configuration. |
The second row is easy to miss. Because Northfield's statement carries no subject, the printer relies on the photo service to have attached the right statement to the right person. It can check Northfield's signature, but not that the photo service picked your statement rather than a classmate's. That is acceptable only because the printer already trusts the photo service to identify you, and it is a reason to accept Northfield statements only from providers the printer has agreed to take them from. If the mail service, which the printer also supports for sign-in, began including Northfield statements, the printer would ignore them until it had made the same arrangement with the mail service.
The third row needs a trust relationship of its own. Trusting the photo service gives the printer no reason to trust Northfield, so the printer configures Northfield separately, as it would any issuer. In this example Northfield publishes provider metadata like an OpenID Provider. The printer fetched it from https://id.northfield.example/.well-known/openid-configuration, took the key set address from its jwks_uri, and recorded that Northfield signs with ES256 and may vouch for https://id.northfield.example/enrolled.
Checking the embedded JWT
For each aggregated claim it wants, the printer works through these steps:
- Follow the reference. Look up the claim name in
_claim_names, find the source it points to in_claim_sources, and take that source'sJWTmember. A missing source, or one without a JWT, means the claim is absent. - Check the issuer before using any key. Read
isswithout trusting it yet, and compare it exactly with the claims providers in the printer's configuration that may vouch for this claim name. Stop if it is not one of them. The printer never fetches keys from an issuer just because a JWT names it. - Verify the signature with a key from that issuer's configured key set, selected by
kid, using the algorithm configured for that issuer.nonefails here, and so does any key the JWT tries to bring with it. - Check the time.
expmust not have passed, andiatmust not be in the future, allowing a small tolerance for clock differences. The printer can add a limit of its own as well, such as refusing statements made before the current term began. - Check the audience, if there is one. The specification does not say whom these JWTs are addressed to, so the arrangement with Northfield settles it. Northfield's statements carry no
aud, and a Northfield JWT that names any audience is rejected. That rule stops a different kind of Northfield JWT being passed off as a statement. An ID token that Northfield issued to some other application is also signed with Northfield's key, may even contain the enrolled claim, and names that application inaud. - Read only the claims that were named. The JWT must contain every claim that
_claim_namespoints to it. The photo service may leave some of a claims provider's claims out of_claim_names, for example because you did not agree to share them, and the printer leaves those alone even if the JWT contains them.
As a sketch:
source = response._claim_sources.get(response._claim_names.get(ENROLLED))
if source is None or "JWT" not in source:
return not_available()
statement = decode_without_trusting(source.JWT)
provider = claims_providers.get(statement.iss) # the printer's own configuration
if provider is None or ENROLLED not in provider.claim_names:
return rejected("unexpected_issuer")
key = provider.keys.find(statement.header.kid) # refetches Northfield's jwks_uri, within a limit, for a new kid
if key is None or statement.header.alg not in provider.algorithms:
return rejected("unknown_key_or_algorithm")
if not verify(key, source.JWT):
return rejected("bad_signature")
if expired(statement.exp) or issued_in_future(statement.iat):
return rejected("stale_statement")
if "aud" in statement: # this arrangement expects none
return rejected("unexpected_audience")
if ENROLLED not in statement:
return not_available()
return statement[ENROLLED]
The helper names are illustrative. The order is the point: nothing reads the enrolled claim until the issuer, key, signature, and time have all passed.
Whose statement, about whom
The printer links your account to the pair https://auth.photos.example and user-2048, as Connecting a sign-in to an account described. Northfield's statement changes nothing about that. The discount belongs to the printer account the photo service signed you into.
Some claims providers include a sub after all. The specification says one should appear only when the value is the person's identifier at the claims provider. If Northfield sent "sub": "s-551207", that would be your identifier at Northfield, and it would not match user-2048. The mismatch is expected. Subjects are scoped by their issuer, as Supporting several providers explained, so the printer never compares a Northfield subject with a photo service subject, and never uses one to find or create an account.
A Northfield subject could still have a use. A printer worried that one enrollment might unlock discounts on many accounts could record it, under Northfield's issuer, to notice the same student record appearing again. It is also a good reason to leave the subject out: an identifier that is the same for every application you share it with lets those applications recognize you across all of them. Unless the arrangement calls for it, the printer does not store it.
When the statement fails
A failed check on Northfield's statement is not a failed sign-in. The ID token and the UserInfo response were valid, and you are still signed in to the same account. Only the discount is in question, and the printer handles each case on that basis:
| Situation | What the printer does |
|---|---|
| No Northfield statement in the response | Not an error. You may have declined to share it. No discount, and the card upload is offered instead. |
Unknown kid | Northfield may have started signing with a new key. Fetch Northfield's key set again, with the usual limit on how often, and verify again. |
| Northfield's key set cannot be fetched | The statement cannot be checked right now. No discount for the moment, with an offer to try again later. |
| Expired statement | Treated as missing. You can renew your student status with the photo service. |
| Bad signature, unexpected issuer, or unexpected audience | The JWT was altered or forged, or it is the wrong kind of JWT. Reject it and record a security event. |
In every row, the discount waits for a statement that passes every check, and nothing falls back to the decoded value. The printer's log records the stage, the failed check, the claims provider's issuer, the kid, and a correlation ID, never the JWT.
Once a statement passes, the printer keeps only what the discount needs: that Northfield confirmed your enrollment, when the statement was made, and when it expires. After that expiry, the next discount means asking again.