From 5699a692565dc0f03ab7dc53fa8cd814bad2e724 Mon Sep 17 00:00:00 2001 From: josebarrueta Date: Fri, 21 Nov 2014 21:21:05 -0800 Subject: [PATCH] Issue #10 Switching Claims validation after the JWT signature was validated. --- .../io/jsonwebtoken/ClaimJwtException.java | 48 +++++++++++ .../io/jsonwebtoken/ExpiredJwtException.java | 10 +-- .../jsonwebtoken/PrematureJwtException.java | 10 +-- .../jsonwebtoken/impl/DefaultJwtParser.java | 86 +++++++++---------- .../io/jsonwebtoken/JwtParserTest.groovy | 16 +++- 5 files changed, 113 insertions(+), 57 deletions(-) create mode 100644 src/main/java/io/jsonwebtoken/ClaimJwtException.java diff --git a/src/main/java/io/jsonwebtoken/ClaimJwtException.java b/src/main/java/io/jsonwebtoken/ClaimJwtException.java new file mode 100644 index 00000000..89059b2b --- /dev/null +++ b/src/main/java/io/jsonwebtoken/ClaimJwtException.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2014 jsonwebtoken.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.jsonwebtoken; + +/** + * ClaimJwtException is a subclass of the {@link JwtException} that is thrown after a validation of an JTW claim failed. + * + * @since 0.5 + */ +public abstract class ClaimJwtException extends JwtException { + + private final Header header; + + private final Claims claims; + + protected ClaimJwtException(Header header, Claims claims, String message) { + super(message); + this.header = header; + this.claims = claims; + } + + protected ClaimJwtException(Header header, Claims claims, String message, Throwable cause) { + super(message, cause); + this.header = header; + this.claims = claims; + } + + public Claims getClaims() { + return claims; + } + + public Header getHeader() { + return header; + } +} diff --git a/src/main/java/io/jsonwebtoken/ExpiredJwtException.java b/src/main/java/io/jsonwebtoken/ExpiredJwtException.java index ef0c5855..7fd4862f 100644 --- a/src/main/java/io/jsonwebtoken/ExpiredJwtException.java +++ b/src/main/java/io/jsonwebtoken/ExpiredJwtException.java @@ -20,13 +20,13 @@ package io.jsonwebtoken; * * @since 0.3 */ -public class ExpiredJwtException extends JwtException { +public class ExpiredJwtException extends ClaimJwtException { - public ExpiredJwtException(String message) { - super(message); + public ExpiredJwtException(Header header, Claims claims, String message) { + super(header, claims, message); } - public ExpiredJwtException(String message, Throwable cause) { - super(message, cause); + public ExpiredJwtException(Header header, Claims claims, String message, Throwable cause) { + super(header, claims, message, cause); } } diff --git a/src/main/java/io/jsonwebtoken/PrematureJwtException.java b/src/main/java/io/jsonwebtoken/PrematureJwtException.java index 8b838425..05412bef 100644 --- a/src/main/java/io/jsonwebtoken/PrematureJwtException.java +++ b/src/main/java/io/jsonwebtoken/PrematureJwtException.java @@ -20,14 +20,14 @@ package io.jsonwebtoken; * * @since 0.3 */ -public class PrematureJwtException extends JwtException { +public class PrematureJwtException extends ClaimJwtException { - public PrematureJwtException(String message) { - super(message); + public PrematureJwtException(Header header, Claims claims, String message) { + super(header, claims, message); } @SuppressWarnings("UnusedDeclaration") - public PrematureJwtException(String message, Throwable cause) { - super(message, cause); + public PrematureJwtException(Header header, Claims claims, String message, Throwable cause) { + super(header, claims, message, cause); } } diff --git a/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java b/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java index 040d9125..5a2626a8 100644 --- a/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java +++ b/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java @@ -178,49 +178,6 @@ public class DefaultJwtParser implements JwtParser { claims = new DefaultClaims(claimsMap); } - //since 0.3: - if (claims != null) { - - Date now = null; - SimpleDateFormat sdf; - - //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: - Date exp = claims.getExpiration(); - if (exp != null) { - - now = new Date(); - - if (now.equals(exp) || now.after(exp)) { - sdf = new SimpleDateFormat(ISO_8601_FORMAT); - String expVal = sdf.format(exp); - String nowVal = sdf.format(now); - - String msg = "JWT expired at " + expVal + ". Current time: " + nowVal; - throw new ExpiredJwtException(msg); - } - } - - //https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-30#section-4.1.5 - //token MUST NOT be accepted before any specified nbf time: - Date nbf = claims.getNotBefore(); - if (nbf != null) { - - if (now == null) { - now = new Date(); - } - - if (now.before(nbf)) { - sdf = new SimpleDateFormat(ISO_8601_FORMAT); - String nbfVal = sdf.format(nbf); - String nowVal = sdf.format(now); - - String msg = "JWT must not be accepted before " + nbfVal + ". Current time: " + nowVal; - throw new PrematureJwtException(msg); - } - } - } - // =============== Signature ================= if (base64UrlEncodedDigest != null) { //it is signed - validate the signature @@ -287,6 +244,49 @@ public class DefaultJwtParser implements JwtParser { } } + //since 0.3: + if (claims != null) { + + Date now = null; + SimpleDateFormat sdf; + + //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: + Date exp = claims.getExpiration(); + if (exp != null) { + + now = new Date(); + + if (now.equals(exp) || now.after(exp)) { + sdf = new SimpleDateFormat(ISO_8601_FORMAT); + String expVal = sdf.format(exp); + String nowVal = sdf.format(now); + + String msg = "JWT expired at " + expVal + ". Current time: " + nowVal; + throw new ExpiredJwtException(header, claims, msg); + } + } + + //https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-30#section-4.1.5 + //token MUST NOT be accepted before any specified nbf time: + Date nbf = claims.getNotBefore(); + if (nbf != null) { + + if (now == null) { + now = new Date(); + } + + if (now.before(nbf)) { + sdf = new SimpleDateFormat(ISO_8601_FORMAT); + String nbfVal = sdf.format(nbf); + String nowVal = sdf.format(now); + + String msg = "JWT must not be accepted before " + nbfVal + ". Current time: " + nowVal; + throw new PrematureJwtException(header, claims, msg); + } + } + } + Object body = claims != null ? claims : payload; if (base64UrlEncodedDigest != null) { diff --git a/src/test/groovy/io/jsonwebtoken/JwtParserTest.groovy b/src/test/groovy/io/jsonwebtoken/JwtParserTest.groovy index 510bc405..5309706c 100644 --- a/src/test/groovy/io/jsonwebtoken/JwtParserTest.groovy +++ b/src/test/groovy/io/jsonwebtoken/JwtParserTest.groovy @@ -421,35 +421,43 @@ class JwtParserTest { @Test void testParseClaimsJwsWithExpiredJws() { + String sub = 'Joe' + byte[] key = randomKey() long nowMillis = System.currentTimeMillis(); //some time in the past: Date exp = new Date(nowMillis - 1000); - String compact = Jwts.builder().setSubject('Joe').signWith(SignatureAlgorithm.HS256, key).setExpiration(exp).compact() + String compact = Jwts.builder().setSubject(sub).signWith(SignatureAlgorithm.HS256, key).setExpiration(exp).compact() try { - Jwts.parser().parseClaimsJwt(compact); + Jwts.parser().setSigningKey(key).parseClaimsJwt(compact); fail(); } catch (ExpiredJwtException e) { assertTrue e.getMessage().startsWith('JWT expired at ') + assertEquals e.getClaims().getSubject(), sub + assertEquals e.getHeader().getAlgorithm(), "HS256" } } @Test void testParseClaimsJwsWithPrematureJws() { + String sub = 'Joe' + byte[] key = randomKey() Date nbf = new Date(System.currentTimeMillis() + 100000); - String compact = Jwts.builder().setSubject('Joe').setNotBefore(nbf).signWith(SignatureAlgorithm.HS256, key).compact(); + String compact = Jwts.builder().setSubject(sub).setNotBefore(nbf).signWith(SignatureAlgorithm.HS256, key).compact(); try { - Jwts.parser().parseClaimsJws(compact); + Jwts.parser().setSigningKey(key).parseClaimsJws(compact); } catch (PrematureJwtException e) { assertTrue e.getMessage().startsWith('JWT must not be accepted before ') + assertEquals e.getClaims().getSubject(), sub + assertEquals e.getHeader().getAlgorithm(), "HS256" } }