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

Make Clock customization accessible for verification #125

Merged
merged 2 commits into from
Jan 4, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,15 @@ JWTVerifier verifier = JWT.require(Algorithm.RSA256(key))
.build();
```

If you need to test this behaviour in your lib/app cast the `Verification` instance to a `BaseVerification` to gain visibility of the `verification.build()` method that accepts a custom `Clock`. e.g.:

```java
BaseVerification verification = (BaseVerification) JWT.require(Algorithm.RSA256(key))
.acceptLeeway(1)
.acceptExpiresAt(5);
Clock clock = new CustomClock(); //Must implement Clock interface
JWTVerifier verifier = verification.build(clock);
```

### Header Claims

Expand Down
16 changes: 16 additions & 0 deletions lib/src/main/java/com/auth0/jwt/ClockImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.auth0.jwt;

import com.auth0.jwt.interfaces.Clock;

import java.util.Date;

final class ClockImpl implements Clock {

ClockImpl() {
}

@Override
public Date getToday() {
return new Date();
}
}
9 changes: 3 additions & 6 deletions lib/src/main/java/com/auth0/jwt/JWT.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@

import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Date;
import java.util.List;
import com.auth0.jwt.interfaces.Verification;

@SuppressWarnings("WeakerAccess")
public abstract class JWT implements DecodedJWT {

/**
* Decode a given JWT token.
*
* <p>
* Note that this method <b>doesn't verify the token's signature!</b> Use it only if you trust the token or you already verified it.
*
* @param token with jwt format as string.
Expand All @@ -31,7 +28,7 @@ public static JWT decode(String token) throws JWTDecodeException {
* @return {@link JWTVerifier} builder
* @throws IllegalArgumentException if the provided algorithm is null.
*/
public static JWTVerifier.Verification require(Algorithm algorithm) {
public static Verification require(Algorithm algorithm) {
return JWTVerifier.init(algorithm);
}

Expand Down
30 changes: 24 additions & 6 deletions lib/src/main/java/com/auth0/jwt/JWTVerifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.impl.PublicClaims;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.Clock;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.Verification;
import org.apache.commons.codec.binary.Base64;

import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -35,19 +37,19 @@ public final class JWTVerifier {
* @return a JWTVerifier.Verification instance to configure.
* @throws IllegalArgumentException if the provided algorithm is null.
*/
static JWTVerifier.Verification init(Algorithm algorithm) throws IllegalArgumentException {
return new Verification(algorithm);
static Verification init(Algorithm algorithm) throws IllegalArgumentException {
return new BaseVerification(algorithm);
}

/**
* The Verification class holds the Claims required by a JWT to be valid.
*/
public static class Verification {
public static class BaseVerification implements Verification {
private final Algorithm algorithm;
private final Map<String, Object> claims;
private long defaultLeeway;

Verification(Algorithm algorithm) throws IllegalArgumentException {
BaseVerification(Algorithm algorithm) throws IllegalArgumentException {
if (algorithm == null) {
throw new IllegalArgumentException("The Algorithm cannot be null.");
}
Expand All @@ -63,6 +65,7 @@ public static class Verification {
* @param issuer the required Issuer value
* @return this same Verification instance.
*/
@Override
public Verification withIssuer(String issuer) {
requireClaim(PublicClaims.ISSUER, issuer);
return this;
Expand All @@ -74,6 +77,7 @@ public Verification withIssuer(String issuer) {
* @param subject the required Subject value
* @return this same Verification instance.
*/
@Override
public Verification withSubject(String subject) {
requireClaim(PublicClaims.SUBJECT, subject);
return this;
Expand All @@ -85,6 +89,7 @@ public Verification withSubject(String subject) {
* @param audience the required Audience value
* @return this same Verification instance.
*/
@Override
public Verification withAudience(String... audience) {
requireClaim(PublicClaims.AUDIENCE, Arrays.asList(audience));
return this;
Expand All @@ -98,6 +103,7 @@ public Verification withAudience(String... audience) {
* @return this same Verification instance.
* @throws IllegalArgumentException if leeway is negative.
*/
@Override
public Verification acceptLeeway(long leeway) throws IllegalArgumentException {
assertPositive(leeway);
this.defaultLeeway = leeway;
Expand All @@ -112,6 +118,7 @@ public Verification acceptLeeway(long leeway) throws IllegalArgumentException {
* @return this same Verification instance.
* @throws IllegalArgumentException if leeway is negative.
*/
@Override
public Verification acceptExpiresAt(long leeway) throws IllegalArgumentException {
assertPositive(leeway);
requireClaim(PublicClaims.EXPIRES_AT, leeway);
Expand All @@ -126,6 +133,7 @@ public Verification acceptExpiresAt(long leeway) throws IllegalArgumentException
* @return this same Verification instance.
* @throws IllegalArgumentException if leeway is negative.
*/
@Override
public Verification acceptNotBefore(long leeway) throws IllegalArgumentException {
assertPositive(leeway);
requireClaim(PublicClaims.NOT_BEFORE, leeway);
Expand All @@ -140,6 +148,7 @@ public Verification acceptNotBefore(long leeway) throws IllegalArgumentException
* @return this same Verification instance.
* @throws IllegalArgumentException if leeway is negative.
*/
@Override
public Verification acceptIssuedAt(long leeway) throws IllegalArgumentException {
assertPositive(leeway);
requireClaim(PublicClaims.ISSUED_AT, leeway);
Expand All @@ -152,6 +161,7 @@ public Verification acceptIssuedAt(long leeway) throws IllegalArgumentException
* @param jwtId the required Id value
* @return this same Verification instance.
*/
@Override
public Verification withJWTId(String jwtId) {
requireClaim(PublicClaims.JWT_ID, jwtId);
return this;
Expand All @@ -165,6 +175,7 @@ public Verification withJWTId(String jwtId) {
* @return this same Verification instance.
* @throws IllegalArgumentException if the name is null.
*/
@Override
public Verification withClaim(String name, Boolean value) throws IllegalArgumentException {
assertNonNull(name);
requireClaim(name, value);
Expand All @@ -179,6 +190,7 @@ public Verification withClaim(String name, Boolean value) throws IllegalArgument
* @return this same Verification instance.
* @throws IllegalArgumentException if the name is null.
*/
@Override
public Verification withClaim(String name, Integer value) throws IllegalArgumentException {
assertNonNull(name);
requireClaim(name, value);
Expand All @@ -193,6 +205,7 @@ public Verification withClaim(String name, Integer value) throws IllegalArgument
* @return this same Verification instance.
* @throws IllegalArgumentException if the name is null.
*/
@Override
public Verification withClaim(String name, Double value) throws IllegalArgumentException {
assertNonNull(name);
requireClaim(name, value);
Expand All @@ -207,6 +220,7 @@ public Verification withClaim(String name, Double value) throws IllegalArgumentE
* @return this same Verification instance.
* @throws IllegalArgumentException if the name is null.
*/
@Override
public Verification withClaim(String name, String value) throws IllegalArgumentException {
assertNonNull(name);
requireClaim(name, value);
Expand All @@ -221,6 +235,7 @@ public Verification withClaim(String name, String value) throws IllegalArgumentE
* @return this same Verification instance.
* @throws IllegalArgumentException if the name is null.
*/
@Override
public Verification withClaim(String name, Date value) throws IllegalArgumentException {
assertNonNull(name);
requireClaim(name, value);
Expand All @@ -235,6 +250,7 @@ public Verification withClaim(String name, Date value) throws IllegalArgumentExc
* @return this same Verification instance.
* @throws IllegalArgumentException if the name is null.
*/
@Override
public Verification withArrayClaim(String name, String... items) throws IllegalArgumentException {
assertNonNull(name);
requireClaim(name, items);
Expand All @@ -249,6 +265,7 @@ public Verification withArrayClaim(String name, String... items) throws IllegalA
* @return this same Verification instance.
* @throws IllegalArgumentException if the name is null.
*/
@Override
public Verification withArrayClaim(String name, Integer... items) throws IllegalArgumentException {
assertNonNull(name);
requireClaim(name, items);
Expand All @@ -260,8 +277,9 @@ public Verification withArrayClaim(String name, Integer... items) throws Illegal
*
* @return a new JWTVerifier instance.
*/
@Override
public JWTVerifier build() {
return this.build(new Clock());
return this.build(new ClockImpl());
}

/**
Expand All @@ -271,7 +289,7 @@ public JWTVerifier build() {
* @param clock the instance that will handle the current time.
* @return a new JWTVerifier instance with a custom Clock.
*/
JWTVerifier build(Clock clock) {
public JWTVerifier build(Clock clock) {
addLeewayToDateClaims();
return new JWTVerifier(algorithm, claims, clock);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
package com.auth0.jwt;
package com.auth0.jwt.interfaces;

import java.util.Date;

/**
* The Clock class is used to wrap calls to Date class.
*/
class Clock {

Clock() {
}
public interface Clock {

/**
* Returns a new Date representing Today's time.
*
* @return a new Date representing Today's time.
*/
Date getToday() {
return new Date();
}
Date getToday();
}
39 changes: 39 additions & 0 deletions lib/src/main/java/com/auth0/jwt/interfaces/Verification.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.auth0.jwt.interfaces;

import com.auth0.jwt.JWTVerifier;

import java.util.Date;

public interface Verification {
Verification withIssuer(String issuer);

Verification withSubject(String subject);

Verification withAudience(String... audience);

Verification acceptLeeway(long leeway) throws IllegalArgumentException;

Verification acceptExpiresAt(long leeway) throws IllegalArgumentException;

Verification acceptNotBefore(long leeway) throws IllegalArgumentException;

Verification acceptIssuedAt(long leeway) throws IllegalArgumentException;

Verification withJWTId(String jwtId);

Verification withClaim(String name, Boolean value) throws IllegalArgumentException;

Verification withClaim(String name, Integer value) throws IllegalArgumentException;

Verification withClaim(String name, Double value) throws IllegalArgumentException;

Verification withClaim(String name, String value) throws IllegalArgumentException;

Verification withClaim(String name, Date value) throws IllegalArgumentException;

Verification withArrayClaim(String name, String... items) throws IllegalArgumentException;

Verification withArrayClaim(String name, Integer... items) throws IllegalArgumentException;

JWTVerifier build();
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package com.auth0.jwt;

import com.auth0.jwt.interfaces.Clock;
import org.junit.Test;

import java.util.Date;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.*;

public class ClockTest {
public class ClockImplTest {

@Test
public void shouldGetToday() throws Exception{
Clock clock = new Clock();
Clock clock = new ClockImpl();
Date clockToday = clock.getToday();
assertThat(clockToday, is(notNullValue()));
}
Expand Down
13 changes: 9 additions & 4 deletions lib/src/test/java/com/auth0/jwt/JWTTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.auth0.jwt;

import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Clock;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.hamcrest.collection.IsCollectionWithSize;
import org.hamcrest.core.IsCollectionContaining;
Expand Down Expand Up @@ -242,7 +243,8 @@ public void shouldGetExpirationTime() throws Exception {
when(clock.getToday()).thenReturn(expectedDate);

String token = "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0Nzc1OTJ9.x_ZjkPkKYUV5tdvc0l8go6D_z2kez1MQcOxokXrDc3k";
DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret"))
JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(Algorithm.HMAC256("secret"));
DecodedJWT jwt = verification
.build(clock)
.verify(token);

Expand All @@ -259,7 +261,8 @@ public void shouldGetNotBefore() throws Exception {
when(clock.getToday()).thenReturn(expectedDate);

String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYmYiOjE0Nzc1OTJ9.mWYSOPoNXstjKbZkKrqgkwPOQWEx3F3gMm6PMcfuJd8";
DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret"))
JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(Algorithm.HMAC256("secret"));
DecodedJWT jwt = verification
.build(clock)
.verify(token);

Expand All @@ -276,7 +279,8 @@ public void shouldGetIssuedAt() throws Exception {
when(clock.getToday()).thenReturn(expectedDate);

String token = "eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0Nzc1OTJ9.5o1CKlLFjKKcddZzoarQ37pq7qZqNPav3sdZ_bsZaD4";
DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret"))
JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(Algorithm.HMAC256("secret"));
DecodedJWT jwt = verification
.build(clock)
.verify(token);

Expand All @@ -289,7 +293,8 @@ public void shouldGetIssuedAt() throws Exception {
@Test
public void shouldGetId() throws Exception {
String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxMjM0NTY3ODkwIn0.m3zgEfVUFOd-CvL3xG5BuOWLzb0zMQZCqiVNQQOPOvA";
DecodedJWT jwt = JWT.require(Algorithm.HMAC256("secret"))
JWTVerifier.BaseVerification verification = (JWTVerifier.BaseVerification) JWT.require(Algorithm.HMAC256("secret"));
DecodedJWT jwt = verification
.build()
.verify(token);

Expand Down
Loading