Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ignore validation options and static methods for validation of claims independent to token parsing #175

Closed
wants to merge 12 commits into from

Conversation

selimok
Copy link

@selimok selimok commented Sep 30, 2016

This pull request contains the features requested in #80 and additionally code refactoring to reuse claim validation codes in static validation methods.

With these changes we can:

  • Parse token by ignoring expiration, notbefore claim values or signature problems.
  • Do these validations explicitly and separately by calling static validation methods.

Use case: As part of our token expiration strategy, we create tokens with short life spans and re-create new tokens if needed. To do this in more convenient way, we need aforementioned ignore methods while parsing. This way we can do additional security checks like XSRF token validation or check JWT integrity by validating signature seperately, even if the JWT token expired.

P.S.: I must add new unit test as Java code, since I have no idea how to write code in groovy. Additionally I made some code refactoring and splitted huge methods into smaller. I hope the names of new methods are appropriate to existing (if any) naming conventions.

Thanks.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.6%) to 99.369% when pulling 0f4a6c1 on selimok:master into 48dae36 on jwtk:master.

@lhazlewood
Copy link
Contributor

Please write the tests in Groovy - we have standardized on it for testing and it's even easier than Java. We won't accept a PR with Java tests or without 100% coverage.

@lhazlewood
Copy link
Contributor

Actually, we can convert the Java to Groovy - I don't want to discourage your PR. If you can though, please try!

@selimok
Copy link
Author

selimok commented Sep 30, 2016

Ok, i will give a try. Thanks. And sorry for the test coverage :(

@lhazlewood
Copy link
Contributor

No worries! We're happy to have the help!

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.6%) to 99.369% when pulling e974307 on selimok:master into 48dae36 on jwtk:master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.09%) to 99.91% when pulling 8ebd92c on selimok:master into 48dae36 on jwtk:master.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.09%) to 99.91% when pulling 8ae456f on selimok:master into 48dae36 on jwtk:master.

@selimok
Copy link
Author

selimok commented Oct 1, 2016

Do you have an idea how can fix that https://coveralls.io/jobs/18754160/source_files/1580589642#L15 ?

Thanks.

* @return the parser for method chaining.
* @see SignatureException
*/
JwtParser ignoreSignature();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we want to allow this - it is not good for a spec-compliant parser to allow avoiding mandated security rules. It's probably better to catch the exception and then ignore it if you want to - otherwise it is not obvious in code that you're ignoring security checks.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok we can remove this method. Security comes first. :)

*
* @since 0.8
*/
public class JwtParts {
Copy link
Contributor

@lhazlewood lhazlewood Oct 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not entirely correct. A JWT can be a JWS or JWE, and they have different numbers of 'parts' (Unsecured JWT = 2 parts, JWS = 3, JWE = 5). Whatever implementation we'd have around that needs to account for any/all 3 of these scenarios, and probably typed accordingly, e.g. JwsParts extends JwtParts and JweParts extends JwtParts.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because JWE is being implemented in the jwe branch, you might not need to worry about jwe parts yet, but whatever implementation we create should be extensible enough to support that upcoming feature/release.

if (!this.ignoreExpiry) {
//https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-30#section-4.1.4
//token MUST NOT be accepted on or after any specified exp time:
ValidationHelper.validateExpiration(header, claims, this.allowedClockSkewMillis, this.clock);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No static/helper methods in the implementation please. Such methods are not object-oriented - they are more procedural and harder to test.

/**
* This class contains static methods for validation of JWT claims.
*/
public class ValidationHelper {
Copy link
Contributor

@lhazlewood lhazlewood Oct 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please no 'Helper' or 'Utils' or such classes for anything other than things that augment the Java language itself (e.g. Strings.whatever, Objects.whatever, Assert.whatever are fine because they augment the Java language and those things are often not overridable - such as String being final) - these typically often represent procedural (non OO) logic.

If anything, this would be a Validator or something similarly named. Additionally, anything that can be configured/overridable should be an interface and a default implementation provided so that people can plug in different implementations or override them as desired, e.g. Validator and DefaultValidator (assuming it is correct to have such a component).

Copy link
Contributor

@lhazlewood lhazlewood Oct 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon further review of this class, it appears the most OO/pluggable way for this it to have a generic Validator interface and you could have multiple implementations, e.g. ExpirationValidator PrematureValidator, etc. For example:

public interface Validator<T> {
    public void validate(T value);
}

And the type used by the parser would be Validator<Claims>

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok i will made some changes and remove that static methods. Thanks for advice.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with Validator interface, that we cannot put additional method parameters into the validate method. For example for checking expires claim we need to optionally provide allowedClockSkewMillis or clock. Do you have any suggestion for this?

@lhazlewood
Copy link
Contributor

I added a code review - the use of the static class is why you're getting code coverage failures. HTH!

- Remove ValidationHelper and its static methods.
- Create JwsParts class and extend it from JwtParts
- Implement validation methods inside of JwtParser according to OO principals.
- P.S.: Unit tests follows...
@coveralls
Copy link

Coverage Status

Coverage decreased (-2.5%) to 97.484% when pulling 735934d on selimok:master into 48dae36 on jwtk:master.

@coveralls
Copy link

Coverage Status

Coverage remained the same at 100.0% when pulling 80758c2 on selimok:master into 48dae36 on jwtk:master.

@selimok
Copy link
Author

selimok commented Oct 3, 2016

Hi @lhazlewood, can you please check my changes again. Thx. :)

@coveralls
Copy link

Coverage Status

Coverage remained the same at 100.0% when pulling a0e4b69 on selimok:master into 48dae36 on jwtk:master.

* @param jwt the compact serialized JWT to check
* @return {@code true} if the specified JWT compact string is expired, {@code false} otherwise.
*/
boolean isExpired(String jwt);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe this or isPremature is necessary - just parse the JWT - if there is an exception, you know it isn't a valid JWT. Thoughts?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean isPremature or isExpired or both? In our use case we need really first ignore the expiration validation and then separately check if the token expired or not. I don't really know, if there are other cases for isPremature out there. From my point of view, we can kick all ignoreNotBefore, isPremature and validateNotBefore methods entirely. But why not let them there, may be some one need them for a specific use case.

@@ -0,0 +1,32 @@
package io.jsonwebtoken;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be in the io.jsonwebtoken.impl package - we don't want JJWT users to be able to use this class directly.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. I will change this.

*
* @throws ExpiredJwtException if the validation of 'expiration time' ({@code exp}) claim is failed.
*/
void validateExpiration(String jwt);
Copy link
Contributor

@lhazlewood lhazlewood Oct 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There could be a lot of validation scenarios - I don't feel we should represent them as individual methods on the parser. Same with validateNotBefore. What is the desire for these methods?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isExpired method is just a wrapper on validateExpiration. I explained already above in my first comment, why we need isExpired method. So in my case again we can let validateExpiration method private. But again why to remove this method? Both validateExpiration and validateNotBefore methods was in the code already, just there was a part of the huge parse method. I only moved that parts of the code into the smaller methods and made them accessible. So the decision is up to you.

@@ -0,0 +1,52 @@
package io.jsonwebtoken;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

io.jsonwebtoken.impl

@coveralls
Copy link

Coverage Status

Coverage remained the same at 100.0% when pulling e4ae4c5 on selimok:master into 48dae36 on jwtk:master.

@selimok
Copy link
Author

selimok commented Oct 18, 2016

Any decisions?

@selimok
Copy link
Author

selimok commented Dec 12, 2016

Are there anything else that I can do for a PR acceptance?

@selimok
Copy link
Author

selimok commented Dec 13, 2016

If my PR less likely to be accepted, I would open a new feature request for setting clock skew seperately for notBefore and expiry claims. So that I can at least explicitly set a great amount of clock skew to simulate ignore expiry behaviour. Currently if I set the clock skew value, it affects both nb and exp claim validation.

Thanks.

@lhazlewood
Copy link
Contributor

Hi @selimok - sorry - we just haven't had time to review this due to conferences (Gartner, re:Invent) and holiday/end-of-quarter deadlines. We'll review this as soon as we can!

@selimok
Copy link
Author

selimok commented Dec 13, 2016

Hi @lhazlewood, no problem. Sorry for my impatience.
Thanks in advance for your effort.

@lhazlewood
Copy link
Contributor

@selimok No worries! I'm sorry for the delay! We really appreciate your patience, and we're grateful for the PR!

@torben-amann
Copy link

Hey, what's the status of this pr? We are also trying to accomplish a validation without expiration date even though it's contained in the jwt.

@pvegh
Copy link

pvegh commented Jun 14, 2017

Wow @selimok delivers a PR and all the requested changes but 6 months later there is still no feedback from the devs. 6 months!
This is a great example how not to handle an open source project.
If you have no time to support it anymore please add it to the README.MD so that others can decide if they really want to integrate this or look for another solution.

@emeraquino
Copy link

Any update for the future of this PR?

@hudson155
Copy link

Any update on this?

@SeanLMcCullough
Copy link

Bump, this feature is important for a project I am working on

@jonalbertini
Copy link

Years after the PR, all the quick fix @selimok done. still not merged... now I bet too much conflicts to merge... contribute they said...

@lhazlewood
Copy link
Contributor

lhazlewood commented Feb 17, 2020

@jonalbertini we're taking a different route with a proper Validator interface/builder with chaining methods and such for a future release. This one slipped through the cracks because it was too specialized and not flexible enough for custom validation. We're going with a different approach.

See #474

@lhazlewood
Copy link
Contributor

Closing just as a matter of housekeeping in favor of the work that will be done via #474. If you feel otherwise, please let us know and we can discuss.

@lhazlewood lhazlewood closed this Sep 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants