mirror of https://github.com/jwtk/jjwt.git
Issue #6: Fixed tests to not reference static (stale) expiration dates
This commit is contained in:
parent
076fa3074c
commit
35a42826ea
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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.");
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue