Skip to content

Commit 92fa94f

Browse files
committed
add creator tests. update readme
1 parent f7d5cf2 commit 92fa94f

5 files changed

Lines changed: 218 additions & 22 deletions

File tree

README.md

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,22 @@ An implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-ietf-o
1616
compile 'com.auth0:java-jwt:3.0.+'
1717
```
1818

19+
## Available Algorithms
20+
21+
The library implements JWT Verification and Signing using the following algorithms:
22+
23+
| JWS | Algorithm | Description |
24+
| :-------------: | :-------------: | :----- |
25+
| HS256 | HMAC256 | HMAC with SHA-256 |
26+
| HS384 | HMAC384 | HMAC with SHA-384 |
27+
| HS512 | HMAC512 | HMAC with SHA-512 |
28+
| RS256 | RSA256 | RSASSA-PKCS1-v1_5 with SHA-256 |
29+
| RS384 | RSA384 | RSASSA-PKCS1-v1_5 with SHA-384 |
30+
| RS512 | RSA512 | RSASSA-PKCS1-v1_5 with SHA-512 |
31+
| ES256 | ECDSA256 | ECDSA with curve P-256 and SHA-256 |
32+
| ES384 | ECDSA384 | ECDSA with curve P-384 and SHA-384 |
33+
| ES512 | ECDSA512 | ECDSA with curve P-521 and SHA-512 |
34+
1935
## Usage
2036

2137
### Decode a Token
@@ -31,22 +47,40 @@ try {
3147

3248
If the token has an invalid syntax or the header or payload are not JSONs, a `JWTDecodeException` will raise.
3349

34-
### Verify a Token
3550

51+
### Create and Sign a Token
3652

37-
The library implements JWT Verification using the following algorithms:
53+
You'll first need to create a `JWTCreator` instance by calling `JWT.create()` and passing the Algorithm instance. Use the builder to define the custom Claims your token needs to have. Finally to get the String token call `sign()`.
3854

39-
| JWS | Algorithm | Description |
40-
| :-------------: | :-------------: | :----- |
41-
| HS256 | HMAC256 | HMAC with SHA-256 |
42-
| HS384 | HMAC384 | HMAC with SHA-384 |
43-
| HS512 | HMAC512 | HMAC with SHA-512 |
44-
| RS256 | RSA256 | RSASSA-PKCS1-v1_5 with SHA-256 |
45-
| RS384 | RSA384 | RSASSA-PKCS1-v1_5 with SHA-384 |
46-
| RS512 | RSA512 | RSASSA-PKCS1-v1_5 with SHA-512 |
47-
| ES256 | ECDSA256 | ECDSA with curve P-256 and SHA-256 |
48-
| ES384 | ECDSA384 | ECDSA with curve P-384 and SHA-384 |
49-
| ES512 | ECDSA512 | ECDSA with curve P-521 and SHA-512 |
55+
* Example using `HS256`
56+
57+
```java
58+
try {
59+
String token = JWT.create(Algorithm.HMAC256("secret"))
60+
.withIssuer("auth0")
61+
.sign();
62+
} catch (JWTCreationException exception){
63+
//Invalid Signing configuration / Couldn't convert Claims.
64+
}
65+
```
66+
67+
* Example using `RS256`
68+
69+
```java
70+
PrivateKey key = //Get the key instance
71+
try {
72+
String token = JWT.create(Algorithm.RSA256(key))
73+
.withIssuer("auth0")
74+
.sign();
75+
} catch (JWTCreationException exception){
76+
//Invalid Signing configuration / Couldn't convert Claims.
77+
}
78+
```
79+
80+
If a Claim couldn't be converted to JSON or the Key used in the signing process was invalid a `JWTCreationException` will raise.
81+
82+
83+
### Verify a Token
5084

5185
You'll first need to create a `JWTVerifier` instance by calling `JWT.require()` and passing the Algorithm instance. If you require the token to have specific Claim values, use the builder to define them. The instance returned by the method `build()` is reusable, so you can define it once and use it to verify different tokens. Finally call `verifier.verify()` passing the token.
5286

lib/src/main/java/com/auth0/jwt/JWTCreator.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
import com.fasterxml.jackson.core.JsonProcessingException;
88
import com.fasterxml.jackson.databind.ObjectMapper;
99

10-
import java.util.Collections;
1110
import java.util.Date;
1211
import java.util.HashMap;
1312
import java.util.Map;
1413

14+
/**
15+
* The JWTCreator class holds the sign method to generate a complete JWT (with Signature) from a given Header and Payload content.
16+
*/
1517
class JWTCreator {
1618

1719
private final Algorithm algorithm;
@@ -20,7 +22,6 @@ class JWTCreator {
2022

2123
private JWTCreator(Algorithm algorithm, Map<String, Object> headerClaims, Map<String, Object> payloadClaims) throws JWTCreationException {
2224
this.algorithm = algorithm;
23-
headerClaims.put(PublicClaims.ALGORITHM, algorithm.getName());
2425
try {
2526
headerJson = toSafeJson(headerClaims);
2627
payloadJson = toSafeJson(payloadClaims);
@@ -42,7 +43,7 @@ static JWTCreator.Builder init(Algorithm algorithm) throws IllegalArgumentExcept
4243
}
4344

4445
/**
45-
* The Builder class holds the Claims required by a JWT to be valid.
46+
* The Builder class holds the Claims that defines the JWT to be created.
4647
*/
4748
static class Builder {
4849
private final Algorithm algorithm;
@@ -65,7 +66,7 @@ static class Builder {
6566
* @return this same Builder instance.
6667
*/
6768
public Builder withHeader(Map<String, Object> headerClaims) {
68-
this.headerClaims = Collections.unmodifiableMap(headerClaims);
69+
this.headerClaims = new HashMap<>(headerClaims);
6970
return this;
7071
}
7172

@@ -95,7 +96,12 @@ public Builder withSubject(String subject) {
9596
* @return this same Builder instance.
9697
*/
9798
public Builder withAudience(String[] audience) {
98-
addClaim(PublicClaims.AUDIENCE, audience);
99+
//FIXME: Use a custom Serializer
100+
if (audience.length == 1) {
101+
addClaim(PublicClaims.AUDIENCE, audience[0]);
102+
} else if (audience.length > 1) {
103+
addClaim(PublicClaims.AUDIENCE, audience);
104+
}
99105
return this;
100106
}
101107

@@ -105,7 +111,7 @@ public Builder withAudience(String[] audience) {
105111
* @return this same Builder instance.
106112
*/
107113
public Builder withExpiresAt(Date expiresAt) {
108-
addClaim(PublicClaims.EXPIRES_AT, expiresAt);
114+
addClaim(PublicClaims.EXPIRES_AT, dateToSeconds(expiresAt));
109115
return this;
110116
}
111117

@@ -115,7 +121,7 @@ public Builder withExpiresAt(Date expiresAt) {
115121
* @return this same Builder instance.
116122
*/
117123
public Builder withNotBefore(Date notBefore) {
118-
addClaim(PublicClaims.NOT_BEFORE, notBefore);
124+
addClaim(PublicClaims.NOT_BEFORE, dateToSeconds(notBefore));
119125
return this;
120126
}
121127

@@ -125,7 +131,7 @@ public Builder withNotBefore(Date notBefore) {
125131
* @return this same Builder instance.
126132
*/
127133
public Builder withIssuedAt(Date issuedAt) {
128-
addClaim(PublicClaims.ISSUED_AT, issuedAt);
134+
addClaim(PublicClaims.ISSUED_AT, dateToSeconds(issuedAt));
129135
return this;
130136
}
131137

@@ -143,11 +149,18 @@ public Builder withJWTId(String jwtId) {
143149
* Creates a new instance of the JWT with the specified payloadClaims.
144150
*
145151
* @return a new JWT instance.
152+
* @throws JWTCreationException if the Claims coudln't be converted to a valid JSON or there was a problem with the signing key.
146153
*/
147154
public String sign() throws JWTCreationException {
155+
headerClaims.put(PublicClaims.ALGORITHM, algorithm.getName());
148156
return new JWTCreator(algorithm, headerClaims, payloadClaims).sign();
149157
}
150158

159+
private int dateToSeconds(Date date) {
160+
//FIXME: Use a custom Serializer
161+
return (int) (date.getTime() / 1000);
162+
}
163+
151164
private void addClaim(String name, Object value) {
152165
if (value == null) {
153166
payloadClaims.remove(name);

lib/src/main/java/com/auth0/jwt/JWTVerifier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
/**
1010
* The JWTVerifier class holds the verify method to assert that a given Token has not only a proper JWT format, but also it's signature matches.
1111
*/
12-
class JWTVerifier {
12+
final class JWTVerifier {
1313
private final Algorithm algorithm;
1414
final Map<String, Object> claims;
1515
private final Clock clock;
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package com.auth0.jwt;
2+
3+
import com.auth0.jwt.algorithms.Algorithm;
4+
import org.junit.Rule;
5+
import org.junit.Test;
6+
import org.junit.rules.ExpectedException;
7+
8+
import java.util.Date;
9+
import java.util.HashMap;
10+
import java.util.Map;
11+
12+
import static org.hamcrest.Matchers.is;
13+
import static org.hamcrest.Matchers.notNullValue;
14+
import static org.junit.Assert.assertThat;
15+
16+
public class JWTCreatorTest {
17+
18+
@Rule
19+
public ExpectedException exception = ExpectedException.none();
20+
21+
@Test
22+
public void shouldThrowWhenInitializedWithoutAlgorithm() throws Exception {
23+
exception.expect(IllegalArgumentException.class);
24+
exception.expectMessage("The Algorithm cannot be null");
25+
JWTCreator.init(null);
26+
}
27+
28+
@SuppressWarnings("Convert2Diamond")
29+
@Test
30+
public void shouldAddHeader() throws Exception {
31+
Map<String, Object> header = new HashMap<String, Object>();
32+
header.put("asd", 123);
33+
String signed = JWTCreator.init(Algorithm.HMAC256("secret"))
34+
.withHeader(header)
35+
.sign();
36+
37+
assertThat(signed, is(notNullValue()));
38+
assertThat(SignUtils.splitToken(signed)[0], is("eyJhbGciOiJIUzI1NiIsImFzZCI6MTIzfQ"));
39+
}
40+
41+
@Test
42+
public void shouldAddIssuer() throws Exception {
43+
String signed = JWTCreator.init(Algorithm.HMAC256("secret"))
44+
.withIssuer("auth0")
45+
.sign();
46+
47+
assertThat(signed, is(notNullValue()));
48+
assertThat(SignUtils.splitToken(signed)[1], is("eyJpc3MiOiJhdXRoMCJ9"));
49+
}
50+
51+
@Test
52+
public void shouldAddSubject() throws Exception {
53+
String signed = JWTCreator.init(Algorithm.HMAC256("secret"))
54+
.withSubject("1234567890")
55+
.sign();
56+
57+
assertThat(signed, is(notNullValue()));
58+
assertThat(SignUtils.splitToken(signed)[1], is("eyJzdWIiOiIxMjM0NTY3ODkwIn0"));
59+
}
60+
61+
@Test
62+
public void shouldAddAudience() throws Exception {
63+
String signed = JWTCreator.init(Algorithm.HMAC256("secret"))
64+
.withAudience(new String[]{"Mark"})
65+
.sign();
66+
67+
assertThat(signed, is(notNullValue()));
68+
assertThat(SignUtils.splitToken(signed)[1], is("eyJhdWQiOiJNYXJrIn0"));
69+
70+
71+
String signedArr = JWTCreator.init(Algorithm.HMAC256("secret"))
72+
.withAudience(new String[]{"Mark", "David"})
73+
.sign();
74+
75+
assertThat(signedArr, is(notNullValue()));
76+
assertThat(SignUtils.splitToken(signedArr)[1], is("eyJhdWQiOlsiTWFyayIsIkRhdmlkIl19"));
77+
}
78+
79+
@Test
80+
public void shouldAddExpiresAt() throws Exception {
81+
String signed = JWTCreator.init(Algorithm.HMAC256("secret"))
82+
.withExpiresAt(new Date(1477592000))
83+
.sign();
84+
85+
assertThat(signed, is(notNullValue()));
86+
assertThat(SignUtils.splitToken(signed)[1], is("eyJleHAiOjE0Nzc1OTJ9"));
87+
}
88+
89+
@Test
90+
public void shouldAddNotBefore() throws Exception {
91+
String signed = JWTCreator.init(Algorithm.HMAC256("secret"))
92+
.withNotBefore(new Date(1477592000))
93+
.sign();
94+
95+
assertThat(signed, is(notNullValue()));
96+
assertThat(SignUtils.splitToken(signed)[1], is("eyJuYmYiOjE0Nzc1OTJ9"));
97+
}
98+
99+
@Test
100+
public void shouldAddIssuedAt() throws Exception {
101+
String signed = JWTCreator.init(Algorithm.HMAC256("secret"))
102+
.withIssuedAt(new Date(1477592000))
103+
.sign();
104+
105+
assertThat(signed, is(notNullValue()));
106+
assertThat(SignUtils.splitToken(signed)[1], is("eyJpYXQiOjE0Nzc1OTJ9"));
107+
}
108+
109+
@Test
110+
public void shouldAddJWTId() throws Exception {
111+
String signed = JWTCreator.init(Algorithm.HMAC256("secret"))
112+
.withJWTId("jwt_id_123")
113+
.sign();
114+
115+
assertThat(signed, is(notNullValue()));
116+
assertThat(SignUtils.splitToken(signed)[1], is("eyJqdGkiOiJqd3RfaWRfMTIzIn0"));
117+
}
118+
119+
120+
@Test
121+
public void shouldRemoveClaimWhenPassingNull() throws Exception {
122+
String signed = JWTCreator.init(Algorithm.HMAC256("secret"))
123+
.withIssuer("iss")
124+
.withIssuer(null)
125+
.sign();
126+
127+
assertThat(signed, is(notNullValue()));
128+
assertThat(SignUtils.splitToken(signed)[1], is("e30"));
129+
}
130+
131+
@Test
132+
public void shouldSetCorrectAlgorithmInTheHeader() throws Exception {
133+
String signed = JWTCreator.init(Algorithm.HMAC256("secret"))
134+
.sign();
135+
136+
assertThat(signed, is(notNullValue()));
137+
assertThat(SignUtils.splitToken(signed)[0], is("eyJhbGciOiJIUzI1NiJ9"));
138+
}
139+
140+
@Test
141+
public void shouldSetEmptySignatureIfAlgorithmIsNone() throws Exception {
142+
String signed = JWTCreator.init(Algorithm.none())
143+
.sign();
144+
assertThat(signed, is(notNullValue()));
145+
assertThat(SignUtils.splitToken(signed)[2], is(""));
146+
}
147+
148+
}

lib/src/test/java/com/auth0/jwt/JWTTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ public void shouldGetType() throws Exception {
301301

302302
// *********************************************** //
303303
// Creation / Signing
304+
// *********************************************** //
304305

305306
@Test
306307
public void shouldCreateAnEmptyHMAC256SignedToken() throws Exception {

0 commit comments

Comments
 (0)