Claim Peeking in JARM
Closed this issue · 4 comments
Why are you validating peeked claims instead of
DecryptedResponse
?
Originally posted by @maennchen in #321 (comment)
DecryptedResponse
is still a binary. We validate againstPeekClaims
(rather than getting the claims after callingoidcc_jwt_util:verify_signature/3
) because the spec says that the client must validate some of the claims before validating the signature: https://openid.net/specs/oauth-v2-jarm-final.html#name-processing-rules
Originally posted by @paulswartz in #321 (comment)
@paulswartz I'm not sure I understand your reasoning.
Currently the code is:
- decrypting
- peeking & validating peeked claims
- verifying & unpacking the decrypted token
- returning the verified claims
Why are we not:
- decypting
- verifying token & extracting claims
- check the verified claims
Do you read the spec as having a defined order to the steps? I doesn't specifically say to do so as far as I can see. (Might be wrong on this)
I also don't see any hints that peeking is expected nor do I see a good reason to do so.
Originally posted by @maennchen in #321 (comment)
Here's the text from https://openid.net/specs/oauth-v2-jarm-final.html#section-2.4-2
The client MUST process the JWT secured response as follows:
- (OPTIONAL) The client decrypts the JWT using the default key for the respective issuer or, if applicable, determined by the kid JWT header parameter. The key might be a private key, where the corresponding public key is registered with the expected issuer of the response ("use":"enc" via the client's metadata jwks or jwks_uri) or a key derived from its client secret (see Section 2.2).
- The client obtains the iss element from the JWT and checks whether its value is well known and identifies the expected issuer of the authorization process in examination. If the check fails, the client MUST abort processing and refuse the response.
- The client obtains the aud element from the JWT and checks whether it matches the client id the client used to identify itself in the corresponding authorization request. If the check fails, the client MUST abort processing and refuse the response.
- The client checks the JWT's exp element to determine if the JWT is still valid. If the check fails, the client MUST abort processing and refuse the response.
- The client MUST check the signature of the JWT according to [RFC7515] and the algorithm none ("alg":"none") MUST NOT be accepted. If the check fails, the client MUST abort processing and refuse the response.
My read of this is:
- decrypt if necessary
- check the "iss" claim
- check the "aud" claim
- check the "exp" claim
- check the signature and verify that "alg": "none" was not used
Since steps 2, 3, and 4 require checking the claims before the signature has been checked in step 5, we need to peek into the JWT to do that.
Originally posted by @paulswartz in #321 (comment)
Extracted into a new issue from #321.
I'll check with a colleague to see how they read that part of the spec. It seems strange to me that the spec would require validating unverified claims and I'm not sure if the spec just specifies the steps or if it also specifies the order.
When I have some time, I'll try to look for some other implementations to see what they're doing.
Node's openid-client
seems to do it this way as well (for all JWTs): https://github.com/panva/node-openid-client/blob/9d3cfb8bffc03d3b987a574f20ebbd209fec2cf1/lib/client.js#L896-L1086
Unfortunately, that's the only open-source client I can see that supports JARM.
@paulswartz I’ve discussed the issue with some colleagues that implement an OpenID provider. The consensus is that the order of the steps in the specification is not relevant and that the claims should not be read before validating the signature. The exception would be the iss
claim if the origin of the token is not known beforehand.