Add more granular options in skipping validation of JWTs

Issue #19 resolved
Thomas Broyer created an issue

AFAICT, an "empty" JwtConsumer (created with new JwtConsumerBuilder().build()) will validate nothing except dates (if the claims are present).

When validating id_token_hints in OpenID Connect, you generally want to accept expired ID Tokens (see here and here). With the current API, you're forced to do a two-pass parse so you can get the ID Token's iat and pass it to setEvaluationTime.

It should be possible to somehow disable the NumericDateValidator without skipping all validators; either by setting the evaluation time to null (will currently throw an NPE in the validator) or with a new skipDateValidations method.

Comments (11)

  1. Thomas Broyer reporter

    Actually, there's also a similar issue with the aid claim: if you don't call setExpectedAudience and the JWT contains an aud claim, then you'll have an validation error. If should be possible to skip validating the audience value (ideally, you'd be able to validate the presence of an aud claim without validating its value; it SHOULD be as easy as providing an empty list of acceptable audiences – currently, this case is even special-cased to provide a specific error message).
    The workaround is again to use two passes and feed the JWT's own audience into the acceptable audiences.

    I think what I'm asking for is a separation between validating the JWT's "schema" (presence of claims, with the appropriate type) and verifying expected values.

    Another case is validating an expected subject (could be useful when parsing the response of a UserInfo Endpoing when you asked for a JWT response; or validating the sub of an ID token after you re-authenticated the user –sending id_token_hint to the IdP–, it would be an error to receive an ID Token for another user). The current API only allows you to validate the presence and type of the claim, not its value.

    Should I open separate issues for these?

  2. Brian Campbell repo owner

    Hello again Thomas,

    I understand your request, in general I think, but I have to say that I have some reservations about it. It appears that most of what you're requesting is to make it easier to support the id_token_hint parameter in its various forms from OpenID Connect? In full disclosure - I'm not a big fan of id_token_hint in Connect (ya hear that @ve7jtb?). I think it's underspecified, potentially dangerous in some cases, and encourages or even demands behavior that is contradictory to the (soon to be RFC) JWT spec itself. From https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4 exp and aud are good examples of such that clearly say an expired JWT and/or a JWT that doesn't identify the recipient as an audience shouldn't be accepted (MUST be rejected). So JWTConsumer[Builder] attempts to follow the spec and at least make it somewhat difficult to do insecure things. By design, an "empty" JwtConsumer will validate against aud, exp, and nbf when they are present in the JWT - it can assume evaluation time is the current time but doesn't have a reasonable way of knowing the expected audience so one needs to be given.

    Okay, I just had to clear the air with my thoughts on id_token_hint. But with all that said, I do want to make this API useful and easy to use.

    For the date validation, you can currently get very very close to the same thing as a new skipDateValidations method would provide by setting the allowable clock skew to a very large number, i.e.:

    new JwtConsumerBuilder().setAllowedClockSkewInSeconds(Integer.MAX_VALUE) 
    

    or have you tried that already and run into problems that I'm not anticipating?

    For audience, I reluctantly see the value in providing something like a skipAudienceValidation method on the builder and will consider adding something like that. In the meantime, you should know the expected audience in an id_token_hint during an authentication request because it'll be the client_id from the request. That's not the case in logout/session management though and a two-pass validation might be needed there for now (I still believe that area of the spec needs some work though, for example). FWIW, the two-pass approach is a bit more code but shouldn't incur much/any of a performance impact as the parsing and crypto is only done once.

    Given an expected subject to the JWTConsumer[Builder] is a reasonable ask for the cases you mention (and likely others). I'll add something for that. In the meantime you can create an implementation of Validator that does exactly that and give it to the builder with registerValidator(...). Or you can, of course, do a comparison yourself afterwords.

  3. Thomas Broyer reporter

    Thanks Brian.

    I understand your reluctance to support id_token_hint, as I too am a bit uncomfortable with it (if it's supposed to be a secret, then it shouldn't be sent as a query-string parameter –the response_type=id_token forbids response_mode=query–, plus it likely contains private data about the user – the sub to begin with), and there's a risk of replay attacks unless the OP includes a jti and tracks already-used ones. The sid from HTTP-Based Logout would be better; or maybe even better first exchange your id_token or sid for a logout token. But that's not the place to discuss this.

    FYI, for now I used a custom Validator to validate the subject in the end_session_endpoint, but did a comparison afterwards in the authorization_endpoint to be able to return a different error (login_required instead of invalid_param, see https://bitbucket.org/openid/connect/issue/878 )

    I can't remember why I didn't use setAllowedClockSkewInSeconds(Integer.MAX_VALUE).

  4. Log in to comment