Skip to content

Commit 0a70f79

Browse files
authored
Merge pull request #138 from auth0/add-kid-setter
Add Key Id setter and set JWT Type after signing
2 parents 2ed581e + d5ce251 commit 0a70f79

5 files changed

Lines changed: 274 additions & 53 deletions

File tree

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,18 @@ Additional Claims defined in the token's Header can be obtained by calling `getH
206206
Claim claim = jwt.getHeaderClaim("owner");
207207
```
208208

209+
When creating a Token with the `JWT.create()` you can specify header Claims by calling `withHeader()` and passing both the map of claims.
210+
211+
```java
212+
Map<String, Object> headerClaims = new HashMap();
213+
headerclaims.put("owner", "auth0");
214+
JWT.create()
215+
.withHeader(headerClaims)
216+
.sign(Algorithm.HMAC256("secret"));
217+
```
218+
219+
> The `alg` and `typ` values will always be included in the Header after the signing process.
220+
209221

210222
### Payload Claims
211223

@@ -273,14 +285,14 @@ Additional Claims defined in the token's Payload can be obtained by calling `get
273285
Map<String, Claim> claims = jwt.getClaims(); //Key is the Claim name
274286
Claim claim = claims.get("isAdmin");
275287
```
276-
al
288+
277289
or
278290

279291
```java
280292
Claim claim = jwt.getClaim("isAdmin");
281293
```
282294

283-
When creating a Token with the `JWT.create()` you can specify a custom Claim by calling `withClaim()` and passing both the name and the value.
295+
When creating a Token with the `JWT.create()` you can specify a custom Claim by calling `withClaim()` and passing both the name and the value.
284296

285297
```java
286298
JWT.create()

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

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
import com.auth0.jwt.impl.PayloadSerializer;
88
import com.auth0.jwt.impl.PublicClaims;
99
import com.fasterxml.jackson.core.JsonProcessingException;
10+
import com.fasterxml.jackson.databind.MapperFeature;
1011
import com.fasterxml.jackson.databind.ObjectMapper;
12+
import com.fasterxml.jackson.databind.SerializationConfig;
1113
import com.fasterxml.jackson.databind.module.SimpleModule;
1214
import org.apache.commons.codec.binary.Base64;
1315

@@ -33,6 +35,7 @@ private JWTCreator(Algorithm algorithm, Map<String, Object> headerClaims, Map<St
3335
SimpleModule module = new SimpleModule();
3436
module.addSerializer(ClaimsHolder.class, new PayloadSerializer());
3537
mapper.registerModule(module);
38+
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
3639
headerJson = mapper.writeValueAsString(headerClaims);
3740
payloadJson = mapper.writeValueAsString(new ClaimsHolder(payloadClaims));
3841
} catch (JsonProcessingException e) {
@@ -74,7 +77,18 @@ public Builder withHeader(Map<String, Object> headerClaims) {
7477
}
7578

7679
/**
77-
* Add a specific Issuer ("iss") claim.
80+
* Add a specific Key Id ("kid") claim to the Header.
81+
*
82+
* @param keyId the Key Id value.
83+
* @return this same Builder instance.
84+
*/
85+
public Builder withKeyId(String keyId) {
86+
this.headerClaims.put(PublicClaims.KEY_ID, keyId);
87+
return this;
88+
}
89+
90+
/**
91+
* Add a specific Issuer ("iss") claim to the Payload.
7892
*
7993
* @param issuer the Issuer value.
8094
* @return this same Builder instance.
@@ -85,7 +99,7 @@ public Builder withIssuer(String issuer) {
8599
}
86100

87101
/**
88-
* Add a specific Subject ("sub") claim.
102+
* Add a specific Subject ("sub") claim to the Payload.
89103
*
90104
* @param subject the Subject value.
91105
* @return this same Builder instance.
@@ -96,7 +110,7 @@ public Builder withSubject(String subject) {
96110
}
97111

98112
/**
99-
* Add a specific Audience ("aud") claim.
113+
* Add a specific Audience ("aud") claim to the Payload.
100114
*
101115
* @param audience the Audience value.
102116
* @return this same Builder instance.
@@ -107,7 +121,7 @@ public Builder withAudience(String... audience) {
107121
}
108122

109123
/**
110-
* Add a specific Expires At ("exp") claim.
124+
* Add a specific Expires At ("exp") claim to the Payload.
111125
*
112126
* @param expiresAt the Expires At value.
113127
* @return this same Builder instance.
@@ -118,7 +132,7 @@ public Builder withExpiresAt(Date expiresAt) {
118132
}
119133

120134
/**
121-
* Add a specific Not Before ("nbf") claim.
135+
* Add a specific Not Before ("nbf") claim to the Payload.
122136
*
123137
* @param notBefore the Not Before value.
124138
* @return this same Builder instance.
@@ -129,7 +143,7 @@ public Builder withNotBefore(Date notBefore) {
129143
}
130144

131145
/**
132-
* Add a specific Issued At ("iat") claim.
146+
* Add a specific Issued At ("iat") claim to the Payload.
133147
*
134148
* @param issuedAt the Issued At value.
135149
* @return this same Builder instance.
@@ -140,7 +154,7 @@ public Builder withIssuedAt(Date issuedAt) {
140154
}
141155

142156
/**
143-
* Add a specific JWT Id ("jti") claim.
157+
* Add a specific JWT Id ("jti") claim to the Payload.
144158
*
145159
* @param jwtId the Token Id value.
146160
* @return this same Builder instance.
@@ -261,6 +275,7 @@ public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCrea
261275
throw new IllegalArgumentException("The Algorithm cannot be null.");
262276
}
263277
headerClaims.put(PublicClaims.ALGORITHM, algorithm.getName());
278+
headerClaims.put(PublicClaims.TYPE, "JWT");
264279
return new JWTCreator(algorithm, headerClaims, payloadClaims).sign();
265280
}
266281

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

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package com.auth0.jwt;
22

33
import com.auth0.jwt.algorithms.Algorithm;
4+
import org.apache.commons.codec.binary.Base64;
45
import org.junit.Rule;
56
import org.junit.Test;
67
import org.junit.rules.ExpectedException;
78

9+
import java.nio.charset.StandardCharsets;
810
import java.util.Date;
911
import java.util.HashMap;
1012
import java.util.Map;
@@ -28,15 +30,29 @@ public void shouldThrowWhenRequestingSignWithoutAlgorithm() throws Exception {
2830

2931
@SuppressWarnings("Convert2Diamond")
3032
@Test
31-
public void shouldAddHeader() throws Exception {
33+
public void shouldAddHeaderClaim() throws Exception {
3234
Map<String, Object> header = new HashMap<String, Object>();
3335
header.put("asd", 123);
3436
String signed = JWTCreator.init()
3537
.withHeader(header)
3638
.sign(Algorithm.HMAC256("secret"));
3739

3840
assertThat(signed, is(notNullValue()));
39-
assertThat(TokenUtils.splitToken(signed)[0], is("eyJhbGciOiJIUzI1NiIsImFzZCI6MTIzfQ"));
41+
String[] parts = signed.split("\\.");
42+
String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8);
43+
assertThat(headerJson, JsonMatcher.hasEntry("asd", 123));
44+
}
45+
46+
@Test
47+
public void shouldAddKeyId() throws Exception {
48+
String signed = JWTCreator.init()
49+
.withKeyId("56a8bd44da435300010000015f5ed")
50+
.sign(Algorithm.HMAC256("secret"));
51+
52+
assertThat(signed, is(notNullValue()));
53+
String[] parts = signed.split("\\.");
54+
String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8);
55+
assertThat(headerJson, JsonMatcher.hasEntry("kid", "56a8bd44da435300010000015f5ed"));
4056
}
4157

4258
@Test
@@ -134,7 +150,20 @@ public void shouldSetCorrectAlgorithmInTheHeader() throws Exception {
134150
.sign(Algorithm.HMAC256("secret"));
135151

136152
assertThat(signed, is(notNullValue()));
137-
assertThat(TokenUtils.splitToken(signed)[0], is("eyJhbGciOiJIUzI1NiJ9"));
153+
String[] parts = signed.split("\\.");
154+
String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8);
155+
assertThat(headerJson, JsonMatcher.hasEntry("alg", "HS256"));
156+
}
157+
158+
@Test
159+
public void shouldSetCorrectTypeInTheHeader() throws Exception {
160+
String signed = JWTCreator.init()
161+
.sign(Algorithm.HMAC256("secret"));
162+
163+
assertThat(signed, is(notNullValue()));
164+
String[] parts = signed.split("\\.");
165+
String headerJson = new String(Base64.decodeBase64(parts[0]), StandardCharsets.UTF_8);
166+
assertThat(headerJson, JsonMatcher.hasEntry("typ", "JWT"));
138167
}
139168

140169
@Test
@@ -158,43 +187,43 @@ public void shouldAcceptCustomClaimOfTypeString() throws Exception {
158187
String jwt = JWTCreator.init()
159188
.withClaim("name", "value")
160189
.sign(Algorithm.HMAC256("secret"));
161-
String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidmFsdWUifQ.4qDWJcNQHDVDW1iAcIgZNiu-qqJQ0RIq8X3ETijBx5k";
162190

163191
assertThat(jwt, is(notNullValue()));
164-
assertThat(jwt, is(token));
192+
String[] parts = jwt.split("\\.");
193+
assertThat(parts[1], is("eyJuYW1lIjoidmFsdWUifQ"));
165194
}
166195

167196
@Test
168197
public void shouldAcceptCustomClaimOfTypeInteger() throws Exception {
169198
String jwt = JWTCreator.init()
170199
.withClaim("name", 123)
171200
.sign(Algorithm.HMAC256("secret"));
172-
String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoxMjN9.5i6ga8YMteicIeZrFZgJyW4OnI_2jpMaUXcDt-_jme4";
173201

174202
assertThat(jwt, is(notNullValue()));
175-
assertThat(jwt, is(token));
203+
String[] parts = jwt.split("\\.");
204+
assertThat(parts[1], is("eyJuYW1lIjoxMjN9"));
176205
}
177206

178207
@Test
179208
public void shouldAcceptCustomClaimOfTypeDouble() throws Exception {
180209
String jwt = JWTCreator.init()
181210
.withClaim("name", 23.45)
182211
.sign(Algorithm.HMAC256("secret"));
183-
String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoyMy40NX0.aFNlMk3WiikukJq1jo4Tf8ztR180wjTfSpqec0xKKqU";
184212

185213
assertThat(jwt, is(notNullValue()));
186-
assertThat(jwt, is(token));
214+
String[] parts = jwt.split("\\.");
215+
assertThat(parts[1], is("eyJuYW1lIjoyMy40NX0"));
187216
}
188217

189218
@Test
190219
public void shouldAcceptCustomClaimOfTypeBoolean() throws Exception {
191220
String jwt = JWTCreator.init()
192221
.withClaim("name", true)
193222
.sign(Algorithm.HMAC256("secret"));
194-
String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjp0cnVlfQ.jseAYuhVmT1boYrHQfn9wXmomWq_tdGfphLtG_2tj_M";
195223

196224
assertThat(jwt, is(notNullValue()));
197-
assertThat(jwt, is(token));
225+
String[] parts = jwt.split("\\.");
226+
assertThat(parts[1], is("eyJuYW1lIjp0cnVlfQ"));
198227
}
199228

200229
@Test
@@ -203,31 +232,31 @@ public void shouldAcceptCustomClaimOfTypeDate() throws Exception {
203232
String jwt = JWTCreator.init()
204233
.withClaim("name", date)
205234
.sign(Algorithm.HMAC256("secret"));
206-
String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoxNDc4ODkxNTIxfQ.ZU1B1pDLYoJZhWD8h3_QsK5dViolxvL5Q43Yz9QIxL4";
207235

208236
assertThat(jwt, is(notNullValue()));
209-
assertThat(jwt, is(token));
237+
String[] parts = jwt.split("\\.");
238+
assertThat(parts[1], is("eyJuYW1lIjoxNDc4ODkxNTIxfQ"));
210239
}
211240

212241
@Test
213242
public void shouldAcceptCustomArrayClaimOfTypeString() throws Exception {
214243
String jwt = JWTCreator.init()
215244
.withArrayClaim("name", new String[]{"text", "123", "true"})
216245
.sign(Algorithm.HMAC256("secret"));
217-
String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjpbInRleHQiLCIxMjMiLCJ0cnVlIl19.lxM8EcmK1uSZRAPd0HUhXGZJdauRmZmLjoeqz4J9yAA";
218246

219247
assertThat(jwt, is(notNullValue()));
220-
assertThat(jwt, is(token));
248+
String[] parts = jwt.split("\\.");
249+
assertThat(parts[1], is("eyJuYW1lIjpbInRleHQiLCIxMjMiLCJ0cnVlIl19"));
221250
}
222251

223252
@Test
224253
public void shouldAcceptCustomArrayClaimOfTypeInteger() throws Exception {
225254
String jwt = JWTCreator.init()
226255
.withArrayClaim("name", new Integer[]{1, 2, 3})
227256
.sign(Algorithm.HMAC256("secret"));
228-
String token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjpbMSwyLDNdfQ.UEuMKRQYrzKAiPpPLhIVawWkKWA1zj0_GderrWUIyFE";
229257

230258
assertThat(jwt, is(notNullValue()));
231-
assertThat(jwt, is(token));
259+
String[] parts = jwt.split("\\.");
260+
assertThat(parts[1], is("eyJuYW1lIjpbMSwyLDNdfQ"));
232261
}
233262
}

0 commit comments

Comments
 (0)