Issue #6: Fixed tests to not reference static (stale) expiration dates

This commit is contained in:
Les Hazlewood 2014-10-28 17:40:37 -07:00
parent 076fa3074c
commit 35a42826ea
4 changed files with 81 additions and 25 deletions

View File

@ -16,7 +16,7 @@
package io.jsonwebtoken; package io.jsonwebtoken;
/** /**
* Exception indicating that a JWT was referenced after it expired and should be rejected. * Exception indicating that a JWT was accepted after it expired and must be rejected.
* *
* @since 0.3 * @since 0.3
*/ */

View File

@ -20,8 +20,8 @@ import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Header; import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwsHeader; import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtHandler; import io.jsonwebtoken.JwtHandler;
import io.jsonwebtoken.JwtHandlerAdapter; import io.jsonwebtoken.JwtHandlerAdapter;
import io.jsonwebtoken.JwtParser; import io.jsonwebtoken.JwtParser;
@ -45,6 +45,9 @@ import java.util.Map;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class DefaultJwtParser implements JwtParser { public class DefaultJwtParser implements JwtParser {
//don't need millis since JWT date fields are only second granularity:
private static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
private ObjectMapper objectMapper = new ObjectMapper(); private ObjectMapper objectMapper = new ObjectMapper();
private byte[] keyBytes; private byte[] keyBytes;
@ -167,16 +170,18 @@ public class DefaultJwtParser implements JwtParser {
//since 0.3: //since 0.3:
if (claims != null) { if (claims != null) {
Date exp = claims.getExpiration(); 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) { if (exp != null) {
Date now = new Date(); now = new Date();
if (now.after(exp)) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); //don't need millis since JWT exp field only has second granularity
if (now.equals(exp) || now.after(exp)) {
sdf = new SimpleDateFormat(ISO_8601_FORMAT);
String expVal = sdf.format(exp); String expVal = sdf.format(exp);
String nowVal = sdf.format(now); String nowVal = sdf.format(now);
@ -184,6 +189,27 @@ public class DefaultJwtParser implements JwtParser {
throw new ExpiredJwtException(msg); 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 ================= // =============== Signature =================
@ -218,7 +244,8 @@ public class DefaultJwtParser implements JwtParser {
if (!Objects.isEmpty(this.keyBytes)) { if (!Objects.isEmpty(this.keyBytes)) {
Assert.isTrue(!algorithm.isRsa(), "Key bytes cannot be specified for RSA signatures. Please specify a PublicKey or PrivateKey instance."); Assert.isTrue(!algorithm.isRsa(),
"Key bytes cannot be specified for RSA signatures. Please specify a PublicKey or PrivateKey instance.");
key = new SecretKeySpec(keyBytes, algorithm.getJcaName()); key = new SecretKeySpec(keyBytes, algorithm.getJcaName());
} }
@ -248,7 +275,8 @@ public class DefaultJwtParser implements JwtParser {
} }
@Override @Override
public <T> T parse(String compact, JwtHandler<T> handler) throws ExpiredJwtException, MalformedJwtException, SignatureException { public <T> T parse(String compact, JwtHandler<T> handler)
throws ExpiredJwtException, MalformedJwtException, SignatureException {
Assert.notNull(handler, "JwtHandler argument cannot be null."); Assert.notNull(handler, "JwtHandler argument cannot be null.");
Assert.hasText(compact, "JWT String argument cannot be null or empty."); Assert.hasText(compact, "JWT String argument cannot be null or empty.");

View File

@ -174,6 +174,22 @@ class JwtParserTest {
} }
} }
/*
@Test
void testParseWithPrematureJwt() {
Date nbf = new Date(System.currentTimeMillis() + 100000);
String compact = Jwts.builder().setSubject('Joe').setNotBefore(nbf).compact();
try {
Jwts.parser().parse(compact);
} catch (PrematureJwtException e) {
assertTrue e.getMessage().startsWith('JWT must not be accepted before ')
}
}
*/
// ======================================================================== // ========================================================================
// parsePlaintextJwt tests // parsePlaintextJwt tests
// ======================================================================== // ========================================================================

View File

@ -91,7 +91,7 @@ class JwtsTest {
@Test @Test
void testParsePlaintextToken() { void testParsePlaintextToken() {
def claims = [iss: 'joe', exp: 1300819380, 'http://example.com/is_root':true] def claims = [iss: 'joe', exp: later(), 'http://example.com/is_root':true]
String jwt = Jwts.builder().setClaims(claims).compact(); String jwt = Jwts.builder().setClaims(claims).compact();
@ -220,10 +220,22 @@ class JwtsTest {
assertNull claims.getAudience() assertNull claims.getAudience()
} }
private static Date dateWithOnlySecondPrecision() { private static Date now() {
return dateWithOnlySecondPrecision(System.currentTimeMillis()); return dateWithOnlySecondPrecision(System.currentTimeMillis());
} }
private static int later() {
return laterDate().getTime() / 1000;
}
private static Date laterDate(int seconds) {
return dateWithOnlySecondPrecision(System.currentTimeMillis() + (seconds * 1000));
}
private static Date laterDate() {
return laterDate(10000);
}
private static Date dateWithOnlySecondPrecision(long millis) { private static Date dateWithOnlySecondPrecision(long millis) {
long seconds = millis / 1000; long seconds = millis / 1000;
long secondOnlyPrecisionMillis = seconds * 1000; long secondOnlyPrecisionMillis = seconds * 1000;
@ -232,14 +244,14 @@ class JwtsTest {
@Test @Test
void testConvenienceExpiration() { void testConvenienceExpiration() {
Date now = dateWithOnlySecondPrecision() //jwt exp only supports *seconds* since epoch: Date then = laterDate();
String compact = Jwts.builder().setExpiration(now).compact(); String compact = Jwts.builder().setExpiration(then).compact();
Claims claims = Jwts.parser().parse(compact).body as Claims Claims claims = Jwts.parser().parse(compact).body as Claims
def claimedDate = claims.getExpiration() def claimedDate = claims.getExpiration()
assertEquals claimedDate, now assertEquals claimedDate, then
compact = Jwts.builder().setIssuer("Me") compact = Jwts.builder().setIssuer("Me")
.setExpiration(now) //set it .setExpiration(then) //set it
.setExpiration(null) //null should remove it .setExpiration(null) //null should remove it
.compact(); .compact();
@ -249,7 +261,7 @@ class JwtsTest {
@Test @Test
void testConvenienceNotBefore() { void testConvenienceNotBefore() {
Date now = dateWithOnlySecondPrecision() //jwt exp only supports *seconds* since epoch: Date now = now() //jwt exp only supports *seconds* since epoch:
String compact = Jwts.builder().setNotBefore(now).compact(); String compact = Jwts.builder().setNotBefore(now).compact();
Claims claims = Jwts.parser().parse(compact).body as Claims Claims claims = Jwts.parser().parse(compact).body as Claims
def claimedDate = claims.getNotBefore() def claimedDate = claims.getNotBefore()
@ -266,7 +278,7 @@ class JwtsTest {
@Test @Test
void testConvenienceIssuedAt() { void testConvenienceIssuedAt() {
Date now = dateWithOnlySecondPrecision() //jwt exp only supports *seconds* since epoch: Date now = now() //jwt exp only supports *seconds* since epoch:
String compact = Jwts.builder().setIssuedAt(now).compact(); String compact = Jwts.builder().setIssuedAt(now).compact();
Claims claims = Jwts.parser().parse(compact).body as Claims Claims claims = Jwts.parser().parse(compact).body as Claims
def claimedDate = claims.getIssuedAt() def claimedDate = claims.getIssuedAt()
@ -370,7 +382,7 @@ class JwtsTest {
PublicKey publicKey = kp.getPublic(); PublicKey publicKey = kp.getPublic();
PrivateKey privateKey = kp.getPrivate(); PrivateKey privateKey = kp.getPrivate();
def claims = [iss: 'joe', exp: 1300819380, 'http://example.com/is_root':true] def claims = [iss: 'joe', exp: later(), 'http://example.com/is_root':true]
String jwt = Jwts.builder().setClaims(claims).signWith(alg, privateKey).compact(); String jwt = Jwts.builder().setClaims(claims).signWith(alg, privateKey).compact();
@ -393,7 +405,7 @@ class JwtsTest {
byte[] key = new byte[64]; byte[] key = new byte[64];
random.nextBytes(key); random.nextBytes(key);
def claims = [iss: 'joe', exp: 1300819380, 'http://example.com/is_root':true] def claims = [iss: 'joe', exp: later(), 'http://example.com/is_root':true]
String jwt = Jwts.builder().setClaims(claims).signWith(alg, key).compact(); String jwt = Jwts.builder().setClaims(claims).signWith(alg, key).compact();