Enable automatic processing of expiry / ttl for JWT tokens
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/3f53562b Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/3f53562b Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/3f53562b Branch: refs/heads/3.0.x-fixes Commit: 3f53562bd221c3f2ee3f16b13a96d62d4cfa60bb Parents: 0bb08f3 Author: Colm O hEigeartaigh <[email protected]> Authored: Fri Oct 9 15:03:15 2015 +0100 Committer: Colm O hEigeartaigh <[email protected]> Committed: Fri Oct 9 15:07:46 2015 +0100 ---------------------------------------------------------------------- .../jose/jaxrs/JwtAuthenticationFilter.java | 31 ++++++++ .../cxf/rs/security/jose/jwt/JwtUtils.java | 79 ++++++++++++++++---- 2 files changed, 94 insertions(+), 16 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/3f53562b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtAuthenticationFilter.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtAuthenticationFilter.java index 408ce20..269a8d4 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtAuthenticationFilter.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JwtAuthenticationFilter.java @@ -34,6 +34,7 @@ import org.apache.cxf.rs.security.jose.JoseException; import org.apache.cxf.rs.security.jose.JoseUtils; import org.apache.cxf.rs.security.jose.jwt.AbstractJoseJwtConsumer; import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.jose.jwt.JwtUtils; import org.apache.cxf.security.SecurityContext; @PreMatching @@ -43,6 +44,8 @@ public class JwtAuthenticationFilter extends AbstractJoseJwtConsumer implements private static final String DEFAULT_AUTH_SCHEME = "JWT"; private String expectedAuthScheme = DEFAULT_AUTH_SCHEME; + private int ttl = 300; + private int futureTTL = 0; @Override public void filter(ContainerRequestContext requestContext) throws IOException { @@ -60,4 +63,32 @@ public class JwtAuthenticationFilter extends AbstractJoseJwtConsumer implements this.expectedAuthScheme = expectedAuthScheme; } + @Override + protected void validateToken(JwtToken jwt) { + // If we have no issued time then we need to have an expiry + boolean expiredRequired = jwt.getClaims().getIssuedAt() == null; + JwtUtils.validateJwtExpiry(jwt.getClaims(), expiredRequired); + + JwtUtils.validateJwtNotBefore(jwt.getClaims(), futureTTL, false); + + // If we have no expiry then we must have an issued at + boolean issuedAtRequired = jwt.getClaims().getExpiryTime() == null; + JwtUtils.validateJwtTTL(jwt.getClaims(), ttl, issuedAtRequired); + } + + public int getTtl() { + return ttl; + } + + public void setTtl(int ttl) { + this.ttl = ttl; + } + + public int getFutureTTL() { + return futureTTL; + } + + public void setFutureTTL(int futureTTL) { + this.futureTTL = futureTTL; + } } http://git-wip-us.apache.org/repos/asf/cxf/blob/3f53562b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java index c846659..64c24e9 100644 --- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java @@ -18,6 +18,7 @@ */ package org.apache.cxf.rs.security.jose.jwt; +import java.util.Date; public final class JwtUtils { private JwtUtils() { @@ -36,27 +37,73 @@ public final class JwtUtils { return new JwtTokenReaderWriter().fromJsonClaims(json); } - public static void validateJwtTimeClaims(JwtClaims claims, int clockOffset, - int issuedAtRange, boolean claimsRequired) { - Long currentTimeInSecs = System.currentTimeMillis() / 1000; - Long expiryTimeInSecs = claims.getExpiryTime(); - if (expiryTimeInSecs == null && claimsRequired - || expiryTimeInSecs != null && currentTimeInSecs > expiryTimeInSecs) { - throw new JwtException("The token expired"); + public static void validateJwtExpiry(JwtClaims claims, boolean claimRequired) { + Long expiryTime = claims.getExpiryTime(); + if (expiryTime == null) { + if (claimRequired) { + throw new JwtException("The token has expired"); + } + return; } - Long issuedAtInSecs = claims.getIssuedAt(); - if (clockOffset <= 0) { - clockOffset = 0; + + Date rightNow = new Date(); + Date expiresDate = new Date(expiryTime * 1000L); + if (expiresDate.before(rightNow)) { + throw new JwtException("The token has expired"); } - if (issuedAtInSecs == null && claimsRequired - || issuedAtInSecs != null && (issuedAtInSecs - clockOffset > currentTimeInSecs || issuedAtRange > 0 - && issuedAtInSecs < currentTimeInSecs - issuedAtRange)) { - throw new JwtException("Invalid issuedAt"); + } + + public static void validateJwtNotBefore(JwtClaims claims, int futureTimeToLive, boolean claimRequired) { + Long notBeforeTime = claims.getNotBefore(); + + // If no NotBefore then just use the IssueAt if it exists + if (notBeforeTime == null && claims.getIssuedAt() != null) { + notBeforeTime = claims.getIssuedAt(); + } + + if (notBeforeTime == null && claimRequired) { + throw new JwtException("The token cannot be accepted yet"); + } + + Date validCreation = new Date(); + long currentTime = validCreation.getTime(); + if (futureTimeToLive > 0) { + validCreation.setTime(currentTime + (long)futureTimeToLive * 1000L); + } + Date createdDate = new Date(notBeforeTime * 1000L); + + // Check to see if the not before time is in the future + if (createdDate.after(validCreation)) { + throw new JwtException("The token cannot be accepted yet"); } } - public static void validateJwtTimeClaims(JwtClaims claims) { - validateJwtTimeClaims(claims, 0, 0, false); + public static void validateJwtTTL(JwtClaims claims, int timeToLive, boolean claimRequired) { + Long issuedAtInSecs = claims.getIssuedAt(); + if (issuedAtInSecs == null) { + if (claimRequired) { + throw new JwtException("Invalid issuedAt"); + } + return; + } + + Date validCreation = new Date(); + Date createdDate = new Date(issuedAtInSecs * 1000L); + + int ttl = timeToLive; + if (ttl <= 0) { + ttl = 300; + } + + // Calculate the time that is allowed for the message to travel + long currentTime = validCreation.getTime(); + currentTime -= (long)ttl * 1000L; + validCreation.setTime(currentTime); + + // Validate the time it took the message to travel + if (createdDate.before(validCreation)) { + throw new JwtException("Invalid issuedAt"); + } } }
