Skip to content

Commit e24ae1d

Browse files
author
Justin Dahmubed
committed
Add support for json encoding for implicit jwt/excuse commented code
1 parent 8da0a0a commit e24ae1d

6 files changed

Lines changed: 211 additions & 62 deletions

File tree

‎lib/src/main/java/com/auth0/jwt/JWTDecoder.java‎

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

33
import com.auth0.jwt.creators.EncodeType;
4+
import com.auth0.jwt.creators.JWTCreator;
45
import com.auth0.jwt.exceptions.JWTDecodeException;
56
import com.auth0.jwt.impl.JWTParser;
67
import com.auth0.jwt.interfaces.Claim;
78
import com.auth0.jwt.interfaces.DecodedJWT;
89
import com.auth0.jwt.interfaces.Header;
910
import com.auth0.jwt.interfaces.Payload;
11+
import org.apache.avro.Schema;
12+
import org.apache.avro.SchemaBuilder;
1013
import org.apache.commons.codec.binary.Base32;
1114
import org.apache.commons.codec.binary.Base64;
1215
import org.apache.commons.codec.binary.Hex;
@@ -46,14 +49,52 @@ public JWTDecoder(String jwt, EncodeType encodeType) throws Exception {
4649
headerJson = StringUtils.newStringUtf8(Base64.decodeBase64(parts[0]));
4750
payloadJson = StringUtils.newStringUtf8(Base64.decodeBase64(parts[1]));
4851
break;
49-
case JsonEncode:
52+
case JsonEncode: {
53+
Schema schemaForHeader = SchemaBuilder
54+
.record("record").namespace("namespace")
55+
.fields()
56+
.name("alg").type().stringType().noDefault()
57+
.name("typ").type().stringType().noDefault()
58+
.endRecord();
59+
60+
Schema schemaForPayload = SchemaBuilder
61+
.record("record").namespace("namespace")
62+
.fields()
63+
.name("sub").type().array().items().stringType().noDefault()
64+
.name("iss").type().array().items().stringType().noDefault()
65+
.name("aud").type().stringType().noDefault()
66+
.name("iat").type().intType().noDefault()
67+
.endRecord();
68+
headerJson = JWTCreator.avroToJson(JWTCreator.schemaToHeaderAndPayloadByteArray.get(schemaForHeader), schemaForHeader);
69+
payloadJson = JWTCreator.avroToJson(JWTCreator.schemaToHeaderAndPayloadByteArray.get(schemaForPayload), schemaForPayload);
5070
break;
51-
//token = jwtCreator.signJsonEncode();
71+
}
5272
}
53-
//headerJson = StringUtils.newStringUtf8(Base64.decodeBase64(parts[0]));
54-
//headerJson = StringUtils.newStringUtf8(Hex.decodeHex(parts[0]));
55-
//payloadJson = StringUtils.newStringUtf8(Base64.decodeBase64(parts[1]));
56-
//payloadJson = StringUtils.newStringUtf8(Hex.decodeHex(parts[1]));
73+
header = converter.parseHeader(headerJson);
74+
payload = converter.parsePayload(payloadJson);
75+
}
76+
77+
public JWTDecoder(String jwt, Schema schemaForHeader, Schema schemaForPayload) throws Exception {
78+
parts = TokenUtils.splitToken(jwt);
79+
final JWTParser converter = new JWTParser();
80+
/*Schema schemaForHeader = SchemaBuilder
81+
.record("record").namespace("namespace")
82+
.fields()
83+
.name("alg").type().stringType().noDefault()
84+
.name("typ").type().stringType().noDefault()
85+
.endRecord();
86+
87+
Schema schemaForPayload = SchemaBuilder
88+
.record("record").namespace("namespace")
89+
.fields()
90+
.name("sub").type().array().items().stringType().noDefault()
91+
.name("iss").type().array().items().stringType().noDefault()
92+
.name("aud").type().stringType().noDefault()
93+
.name("iat").type().intType().noDefault()
94+
.endRecord();*/
95+
String headerJson = JWTCreator.avroToJson(JWTCreator.schemaToHeaderAndPayloadByteArray.get(schemaForHeader), schemaForHeader);
96+
String payloadJson = JWTCreator.avroToJson(JWTCreator.schemaToHeaderAndPayloadByteArray.get(schemaForPayload), schemaForPayload);
97+
5798
header = converter.parseHeader(headerJson);
5899
payload = converter.parsePayload(payloadJson);
59100
}

‎lib/src/main/java/com/auth0/jwt/algorithms/HMACAlgorithm.java‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package com.auth0.jwt.algorithms;
22

33
import com.auth0.jwt.creators.EncodeType;
4+
import com.auth0.jwt.creators.JWTCreator;
45
import com.auth0.jwt.exceptions.SignatureGenerationException;
56
import com.auth0.jwt.exceptions.SignatureVerificationException;
67
import com.auth0.jwt.interfaces.DecodedJWT;
8+
import org.apache.avro.Schema;
9+
import org.apache.avro.SchemaBuilder;
710
import org.apache.commons.codec.CharEncoding;
811
import org.apache.commons.codec.binary.Base32;
912
import org.apache.commons.codec.binary.Base64;
@@ -62,9 +65,10 @@ public void verify(DecodedJWT jwt, EncodeType encodeType) throws Exception {
6265
case Base64:
6366
signatureBytes = Base64.decodeBase64(signature);
6467
break;
65-
case JsonEncode:
68+
case JsonEncode: {
69+
signatureBytes = Base64.decodeBase64(signature);
6670
break;
67-
//token = jwtCreator.signJsonEncode();
71+
}
6872
}
6973

7074
try {

‎lib/src/main/java/com/auth0/jwt/creators/ImplicitJwtCreator.java‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.auth0.jwt.exceptions.JWTCreationException;
55
import com.auth0.jwt.impl.PublicClaims;
66
import com.auth0.jwt.jwts.JWT;
7+
import org.apache.avro.Schema;
78

89
import java.util.Date;
910
import java.util.HashMap;
@@ -252,11 +253,11 @@ public String signBase32Encoding(Algorithm algorithm) throws Exception {
252253
* @throws IllegalArgumentException if the provided algorithm is null.
253254
* @throws JWTCreationException if the claims could not be converted to a valid JSON or there was a problem with the signing key.
254255
*/
255-
public String signJSONEncoding(Algorithm algorithm) throws Exception {
256+
public String signJSONEncoding(Algorithm algorithm, Schema schemaHeader, Schema schemaPayload) throws Exception {
256257
if(!jwt.getIsNoneAlgorithmAllowed() && algorithm.equals(Algorithm.none())) {
257258
throw new IllegalAccessException("None algorithm isn't allowed");
258259
}
259-
String JWS = jwt.sign(algorithm, EncodeType.JsonEncode);
260+
String JWS = jwt.signJSON(algorithm, schemaHeader, schemaPayload);
260261
verifyClaims();
261262
return JWS;
262263
}

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

Lines changed: 82 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.apache.avro.Schema;
1414
import org.apache.avro.SchemaBuilder;
1515
import org.apache.avro.file.DataFileStream;
16+
import org.apache.avro.file.DataFileWriter;
1617
import org.apache.avro.generic.GenericDatumReader;
1718
import org.apache.avro.generic.GenericDatumWriter;
1819
import org.apache.avro.generic.GenericRecord;
@@ -39,6 +40,7 @@ public final class JWTCreator {
3940
private final Algorithm algorithm;
4041
private final String headerJson;
4142
private final String payloadJson;
43+
public static Map<Schema, byte[]> schemaToHeaderAndPayloadByteArray;
4244

4345
private JWTCreator(Algorithm algorithm, Map<String, Object> headerClaims, Map<String, Object> payloadClaims) throws JWTCreationException {
4446
this.algorithm = algorithm;
@@ -50,6 +52,7 @@ private JWTCreator(Algorithm algorithm, Map<String, Object> headerClaims, Map<St
5052
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
5153
headerJson = mapper.writeValueAsString(headerClaims);
5254
payloadJson = mapper.writeValueAsString(new ClaimsHolder(payloadClaims));
55+
schemaToHeaderAndPayloadByteArray = new HashMap<>();
5356
} catch (JsonProcessingException e) {
5457
throw new JWTCreationException("Some of the Claims couldn't be converted to a valid JSON format.", e);
5558
}
@@ -371,13 +374,26 @@ public String sign(Algorithm algorithm, EncodeType encodeType) throws Exception
371374
case Base64:
372375
token = jwtCreator.defaultSign();
373376
break;
374-
case JsonEncode:
375-
token = jwtCreator.signJsonEncode();
376377
}
377378

378379
return token;
379380
}
380381

382+
public String signJSON(Algorithm algorithm, Schema schemaHeader, Schema schemaPayload) throws Exception {
383+
if (algorithm == null) {
384+
throw new IllegalArgumentException("The Algorithm cannot be null.");
385+
}
386+
headerClaims.put(PublicClaims.ALGORITHM, algorithm.getName());
387+
headerClaims.put(PublicClaims.TYPE, "JWT");
388+
String signingKeyId = algorithm.getSigningKeyId();
389+
if (signingKeyId != null) {
390+
withKeyId(signingKeyId);
391+
}
392+
JWTCreator jwtCreator = new JWTCreator(algorithm, headerClaims, payloadClaims);
393+
394+
return jwtCreator.signJsonEncode(schemaHeader, schemaPayload);
395+
}
396+
381397
protected void assertNonNull(String name) {
382398
if (name == null) {
383399
throw new IllegalArgumentException("The Custom Claim's name can't be null.");
@@ -393,13 +409,13 @@ private void addClaim(String name, Object value) {
393409
}
394410
}
395411

396-
private String signJsonEncode() throws Exception {
397-
Schema schema = SchemaBuilder
412+
private String signJsonEncode(Schema schemaForHeader, Schema schemaForPayload) throws Exception {
413+
/*Schema schemaForHeader = SchemaBuilder
398414
.record("record").namespace("namespace")
399415
.fields()
400416
.name("alg").type().stringType().noDefault()
401417
.name("typ").type().stringType().noDefault()
402-
.endRecord();
418+
.endRecord();*/
403419
/*String schema = "{" +
404420
"\"type\":\"record\"," +
405421
"\"namespace\":\"foo\"," +
@@ -410,23 +426,73 @@ private String signJsonEncode() throws Exception {
410426
"\"typ\":\"string\"" +
411427
"}" +
412428
"}";*/
413-
byte[] header = jsonToAvro(headerJson, schema.toString());
429+
byte[] header = jsonToAvro(headerJson, schemaForHeader.toString());
430+
schemaToHeaderAndPayloadByteArray.put(schemaForHeader, header);
431+
System.out.println(avroToJson(header, schemaForHeader));
414432
//.name("aud").type().array().items().stringType().noDefault()
415-
schema = SchemaBuilder
433+
/*Schema schemaForPayload = SchemaBuilder
416434
.record("record").namespace("namespace")
417435
.fields()
418436
.name("sub").type().array().items().stringType().noDefault()
419437
.name("iss").type().array().items().stringType().noDefault()
420-
//.name("aud").type().array().items().stringType().noDefault()
438+
.name("aud").type().stringType().noDefault()
421439
.name("iat").type().intType().noDefault()
422-
.endRecord();
423-
byte[] payload = jsonToAvro(payloadJson, schema.toString());
424-
String content = String.format("%s.%s", header, payload);
440+
.endRecord();*/
441+
byte[] payload = jsonToAvro(payloadJson, schemaForPayload.toString());
442+
schemaToHeaderAndPayloadByteArray.put(schemaForPayload, payload);
443+
System.out.println(avroToJson(payload, schemaForPayload));
444+
String content = String.format("%s.%s", new String(header), new String(payload));
425445

426446
byte[] signatureBytes = algorithm.sign(content.getBytes(StandardCharsets.UTF_8));
427-
//String signature = Hex.encodeHexString((signatureBytes));
428-
///String signature = avroToJson(signatureBytes); ???
429-
return String.format("%s.%s", content, new String(signatureBytes)); //for now
447+
String signature = Base64.encodeBase64URLSafeString(signatureBytes);
448+
System.out.println(signature);
449+
System.out.println(Base64.decodeBase64(signature));
450+
451+
return String.format("%s.%s", content, signature); //for now
452+
}
453+
454+
/*public static String avroToJson(byte[] avro, String schemaStr) throws IOException {
455+
boolean pretty = false;
456+
GenericDatumReader<GenericRecord> reader = null;
457+
JsonEncoder encoder = null;
458+
ByteArrayOutputStream output = null;
459+
try {
460+
Schema schema = new Schema.Parser().parse(schemaStr);
461+
reader = new GenericDatumReader<GenericRecord>(schema);
462+
InputStream input = new ByteArrayInputStream(avro);
463+
output = new ByteArrayOutputStream();
464+
DatumWriter<GenericRecord> writer = new GenericDatumWriter<GenericRecord>(schema);
465+
encoder = EncoderFactory.get().jsonEncoder(schema, output, pretty);
466+
Decoder decoder = DecoderFactory.get().binaryDecoder(input, null);
467+
GenericRecord datum;
468+
while (true) {
469+
try {
470+
datum = reader.read(null, decoder);
471+
} catch (EOFException eofe) {
472+
break;
473+
}
474+
writer.write(datum, encoder);
475+
}
476+
encoder.flush();
477+
output.flush();
478+
return new String(output.toByteArray());
479+
} finally {
480+
try { if (output != null) output.close(); } catch (Exception e) { }
481+
}
482+
}*/
483+
484+
public static String avroToJson(byte[] avro, Schema schema) throws IOException {
485+
boolean pretty = false;
486+
GenericDatumReader<Object> reader = new GenericDatumReader<>(schema);
487+
DatumWriter<Object> writer = new GenericDatumWriter<>(schema);
488+
ByteArrayOutputStream output = new ByteArrayOutputStream();
489+
JsonEncoder encoder = EncoderFactory.get().jsonEncoder(schema, output, pretty);
490+
Decoder decoder = DecoderFactory.get().binaryDecoder(avro, null);
491+
Object datum = reader.read(null, decoder);
492+
writer.write(datum, encoder);
493+
encoder.flush();
494+
output.flush();
495+
return new String(output.toByteArray(), "UTF-8");
430496
}
431497

432498
public static String avroToJson(byte[] avro) throws IOException {
@@ -501,7 +567,7 @@ private String signBase32Encoding() {
501567
String content = String.format("%s.%s", header, payload);
502568

503569
byte[] signatureBytes = algorithm.sign(content.getBytes(StandardCharsets.UTF_8));
504-
String signature = base32.encodeAsString((signatureBytes));
570+
String signature = base32.encodeAsString(signatureBytes);
505571

506572
return String.format("%s.%s", content, signature);
507573
}
@@ -512,7 +578,7 @@ private String defaultSign() throws SignatureGenerationException {
512578
String content = String.format("%s.%s", header, payload);
513579

514580
byte[] signatureBytes = algorithm.sign(content.getBytes(StandardCharsets.UTF_8));
515-
String signature = Base64.encodeBase64URLSafeString((signatureBytes));
581+
String signature = Base64.encodeBase64URLSafeString(signatureBytes);
516582

517583
return String.format("%s.%s", content, signature);
518584
}

‎lib/src/main/java/com/auth0/jwt/jwts/JWT.java‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.auth0.jwt.interfaces.DecodedJWT;
1212
import com.auth0.jwt.interfaces.Verification;
1313
import com.auth0.jwt.verification.VerificationAndAssertion;
14+
import org.apache.avro.Schema;
1415

1516
import java.util.*;
1617

@@ -87,6 +88,26 @@ public DecodedJWT decode32Bytes(String token) throws Exception {
8788
return jwt;
8889
}
8990

91+
/**
92+
* Convert the given token to a DecodedJWT
93+
* <p>
94+
* 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.
95+
*
96+
* @param token with jwt format as string.
97+
* @return a decoded JWT.
98+
* @throws AlgorithmMismatchException if the algorithm stated in the token's header it's not equal to the one defined in the {@link JWT}.
99+
* @throws SignatureVerificationException if the signature is invalid.
100+
* @throws TokenExpiredException if the token has expired.
101+
* @throws InvalidClaimException if a claim contained a different value than the expected one.
102+
*/
103+
public DecodedJWT decodeJSON(String token, Schema headerSchema, Schema payloadSchema) throws Exception {
104+
DecodedJWT jwt = new JWTDecoder(token, headerSchema, payloadSchema);
105+
VerificationAndAssertion.verifyAlgorithm(jwt, algorithm);
106+
algorithm.verify(jwt, EncodeType.JsonEncode);
107+
VerificationAndAssertion.verifyClaims(clock, jwt, claims);
108+
return jwt;
109+
}
110+
90111
/**
91112
* Returns a {Verification} to be used to validate token signature.
92113
*

0 commit comments

Comments
 (0)