Skip to content

Commit

Permalink
Merge pull request #125 from auth0/clock-test-accessible
Browse files Browse the repository at this point in the history
Make Clock customization accessible for verification
  • Loading branch information
hzalaz authored Jan 4, 2017
2 parents 24901e2 + 545f5c4 commit 9a79032
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 40 deletions.
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

0 comments on commit 9a79032

Please sign in to comment.