Conversation
| } | ||
|
|
||
| @Override | ||
| public void verifyWithX509(DecodedJWT jwt, EncodeType encodeType, String jwksFile, String pemFile) throws Exception { |
There was a problem hiding this comment.
Why does this require a jwksFile and a pemFile?
There was a problem hiding this comment.
It's needed to get the x509 cert. why do you feel like it's unnecessary?
There was a problem hiding this comment.
Can you explain what's in each that you need? I thought the jwksFile contained the key.
There was a problem hiding this comment.
jwksFile contains the x5c value. pemFile is the public key.
| } | ||
|
|
||
| @Override | ||
| public void verifyWithX509(DecodedJWT jwt, EncodeType encodeType, String jwksFile, String pemFile) throws Exception { |
There was a problem hiding this comment.
Can you write a wrapper for this that infers its EncodingType? That will probably work better in most cases
There was a problem hiding this comment.
good point. i can have a constant that stores what type of encodingtype was used to sign the JWT with and use it accordingly.
There was a problem hiding this comment.
Cool. Can you do anything for unknown JWTs? Can't we see what base something is in based off what characters are used?
There was a problem hiding this comment.
i dont think there's any other way. i tried this, which keeps returning UTF-8: https://code.google.com/archive/p/juniversalchardet/
what i can try to do is run the decode() method for each of the base encodings for the particular string and whichever one doesnt throw an exception is the one. is that what you want?
|
|
||
| @Override | ||
| public void verifyWithX509(DecodedJWT jwt, EncodeType encodeType, String jwksFile, String pemFile) throws Exception { | ||
| List<byte[]> byteArrayList = decode(jwt, encodeType); |
There was a problem hiding this comment.
Why is it that we have to run "decode" again on a "decoded jwt"? I think there could be better naming here.
There was a problem hiding this comment.
fetchContentAndSignatureByteArrays()?
|
|
||
| @Override | ||
| public void verify(DecodedJWT jwt, EncodeType encodeType) throws Exception { | ||
| List<byte[]> byteArrayList = decode(jwt, encodeType); |
There was a problem hiding this comment.
Why is it that we have to run "decode" again on a "decoded jwt"? I think there could be better naming here.
| import java.net.URLDecoder; | ||
| import java.net.URLEncoder; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.util.Arrays; |
There was a problem hiding this comment.
This is never used, along with org.apache.commons.codec.Encoder, org.apache.commons.codec.binary.StringUtils,
java.net.URLDecoder, java.util.logging.Logger
There was a problem hiding this comment.
removed locally. i was thinking of cleaning these up in a separate PR later.
| @Override | ||
| public Verification withNbf(long nbf) { | ||
| throw new UnsupportedOperationException("you shouldn't be calling this method"); | ||
| throw new UnsupportedOperationException("this method has not been implemented"); |
There was a problem hiding this comment.
Why is it that we have all these methods that shouldn't be called?
There was a problem hiding this comment.
because they're implementing the interface method. they are required to implement it or declare the class abstract.
There was a problem hiding this comment.
The comment from @lyzhang27 is correct. You should not expose methods you don't want your users to call. A good inheritance design (parent/child classes) can prevent this.
| private final Header header; | ||
| private final Payload payload; | ||
|
|
||
| private static final String FACEBOOK = "facebook"; |
There was a problem hiding this comment.
Prepend something that indicates where this could be used. i.e. "ISSUER_FACEBOOK". Same for the one below.
|
|
||
| public static GoogleOrFbJwtCreator decodeJWT(DecodedJWT jwt) { | ||
| Map<String, Claim> claims = jwt.getClaims(); | ||
| String issuer = claims.get(Claims.ISSUER).asString(); |
There was a problem hiding this comment.
Claims.asString might return null. Can the issuer be null? Probably it can. Better flip the conditionals to avoid NPE. We know in advance that the constants are not null:
if(issuer.contains(FACEBOOK)) { ---> if(FACEBOOK.contains(issuer)) {
There was a problem hiding this comment.
but then they're 2 diff meanings. i will do a null/empty check on issuer upstream though
| */ | ||
| public abstract void verify(DecodedJWT jwt, EncodeType encodeType) throws Exception; | ||
|
|
||
| public abstract void verifyWithX509(DecodedJWT jwt, String jwksFile, String pemFile) throws Exception; |
| import org.apache.commons.codec.binary.StringUtils; | ||
|
|
||
| import java.io.UnsupportedEncodingException; | ||
| import java.io.*; |
There was a problem hiding this comment.
Try to import only the required classes.
|
|
||
| @Override | ||
| public void verifyWithX509(DecodedJWT jwt, String jwksFile, String pemFile) throws Exception { | ||
| throw new UnsupportedOperationException("X509 is not supported for HMAC"); |
There was a problem hiding this comment.
"X509 is not supported for HMAC algorithm"
| if(!addedClaims.get(claim)) | ||
| for(String claim : requiredClaims.keySet()) | ||
| if(!requiredClaims.get(claim)) | ||
| throw new Exception("Standard claim: " + claim + " has not been set"); |
There was a problem hiding this comment.
Can we please throw something more descriptive than "Exception"? i.e. IllegalStateException/IllegalArgumentException or even a RequiredClaimException
| assertThat(claim, is(notNullValue())); | ||
| Map map = claim.as(Map.class); | ||
| assertThat(((Map<String, Object>) map.get("key")), hasEntry("name", (Object) "john")); | ||
| assertThat(((Map<String, Object>) map.get("key")), hasEntry(Claims.NAME, (Object) "john")); |
There was a problem hiding this comment.
Don't use Claims constants
There was a problem hiding this comment.
i dont see why its a prob for tests
There was a problem hiding this comment.
Simple put:
//constants (values source)
class Claims {
NAME="name"
//...
}
//library code
class Foo {
void Bar {
//...
message.addClaim(Claims.NAME, "John Doe");
}
}
//test code
class FooTest {
//...
assert(message.getClaim(Claims.NAME), is("John Doe"));
}Now go and change Claims.NAME to "namee".
Will the test pass?
There was a problem hiding this comment.
there are many more failing tests after its changed to "namee", which is expected, right? thats why i dont get why you're against using constants in tests.
| assertThat(payload.getClaim("exp").asDouble(), is(11111111D)); | ||
| assertThat(payload.getClaim("nbf").asDouble(), is(10101011D)); | ||
| assertThat(payload.getClaim("jti").asString(), is("idid")); | ||
| assertThat(payload.getClaim(Claims.JWT_ID).asString(), is("idid")); |
There was a problem hiding this comment.
Don't use Claims constants
| assertThat(claims, is(notNullValue())); | ||
| exception.expect(UnsupportedOperationException.class); | ||
| claims.put("name", null); | ||
| claims.put(Claims.NAME, null); |
There was a problem hiding this comment.
Don't use Claims constants
| @Test | ||
| public void shouldSerializeStrings() throws Exception { | ||
| ClaimsHolder holder = holderFor("name", "Auth0 Inc"); | ||
| ClaimsHolder holder = holderFor(Claims.NAME, "Auth0 Inc"); |
There was a problem hiding this comment.
Don't use Claims constants
lbalmaceda
left a comment
There was a problem hiding this comment.
General things:
- Keep the PR short. This is already long and needs to be shrinked.
- Avoid pressing the hotkey to reorder imports as they generate excessive and unnecessary diff. Rollback those changes if possible.
- Don't change the logic of the existing tests. They are there for a reason.
- Either use the constants on the tests or on the exported classes, not in both.
- Don't export files you don't want people to have
- Don't add methods that throw an exception because "they shouldn't be called".
Important one: Any public API changes are to be discussed with the team. Pause the coding and create a doc to discuss this.
| compile 'commons-codec:commons-codec:1.11' | ||
| compile 'com.google.code.gson:gson:2.8.2' | ||
| compile 'com.auth0:jwks-rsa:0.3.0' | ||
| compile 'com.nimbusds:nimbus-jose-jwt:2.19.1' |
There was a problem hiding this comment.
It feels wrong that a jwt library is using another jwt library. Where is this used for and can it be coded in this same library rather than depending on them?
There was a problem hiding this comment.
its for the PemReader object used in these 2 lines:
PemReader reader = new PemReader(file);
X509EncodedKeySpec caKeySpec = new X509EncodedKeySpec(reader.readPemObject().getContent());
| compile 'org.apache.httpcomponents:httpcore:4.4.1' | ||
| compile 'org.apache.httpcomponents:httpclient:4.5' | ||
| compile group: 'org.slf4j', name:'slf4j-api', version: '1.7.2' | ||
| compile group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1' |
There was a problem hiding this comment.
Yet another JSON library?
There was a problem hiding this comment.
probably was an unused import. removed.
| @@ -0,0 +1 @@ | |||
| {"keys":[{"alg":"RS256","kty":"RSA","use":"sig","x5c":["MIIDDTCCAfWgAwIBAgIJAJVkuSv2H8mDMA0GCSqGSIb3DQEBBQUAMB0xGzAZBgNVBAMMEnNhbmRyaW5vLmF1dGgwLmNvbTAeFw0xNDA1MTQyMTIyMjZaFw0yODAxMjEyMTIyMjZaMB0xGzAZBgNVBAMMEnNhbmRyaW5vLmF1dGgwLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL6jWASkHhXz5Ug6t5BsYBrXDIgrWu05f3oq2fE+5J5REKJiY0Ddc+Kda34ZwOptnUoef3JwKPDAckTJQDugweNNZPwOmFMRKj4xqEpxEkIX8C+zHs41Q6x54ZZy0xU+WvTGcdjzyZTZ/h0iOYisswFQT/s6750tZG0BOBtZ5qS/80tmWH7xFitgewdWteJaASE/eO1qMtdNsp9fxOtN5U/pZDUyFm3YRfOcODzVqp3wOz+dcKb7cdZN11EYGZOkjEekpcedzHCo9H4aOmdKCpytqL/9FXoihcBMg39s1OW3cfwfgf5/kvOJdcqR4PoATQTfsDVoeMWVB4XLGR6SC5kCAwEAAaNQME4wHQYDVR0OBBYEFHDYn9BQdup1CoeoFi0Rmf5xn/W9MB8GA1UdIwQYMBaAFHDYn9BQdup1CoeoFi0Rmf5xn/W9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGLpQZdd2ICVnGjc6CYfT3VNoujKYWk7E0shGaCXFXptrZ8yaryfo6WAizTfgOpQNJH+Jz+QsCjvkRt6PBSYX/hb5OUDU2zNJN48/VOw57nzWdjI70H2Ar4oJLck36xkIRs/+QX+mSNCjZboRwh0LxanXeALHSbCgJkbzWbjVnfJEQUP9P/7NGf0MkO5I95C/Pz9g91y8gU+R3imGppLy9Zx+OwADFwKAEJak4JrNgcjHBQenakAXnXP6HG4hHH4MzO8LnLiKv8ZkKVL67da/80PcpO0miMNPaqBBMd2Cy6GzQYE0ag6k0nk+DMIFn7K+o21gjUuOEJqIbAvhbf2KcM="],"n":"vqNYBKQeFfPlSDq3kGxgGtcMiCta7Tl_eirZ8T7knlEQomJjQN1z4p1rfhnA6m2dSh5_cnAo8MByRMlAO6DB401k_A6YUxEqPjGoSnESQhfwL7MezjVDrHnhlnLTFT5a9MZx2PPJlNn-HSI5iKyzAVBP-zrvnS1kbQE4G1nmpL_zS2ZYfvEWK2B7B1a14loBIT947Woy102yn1_E603lT-lkNTIWbdhF85w4PNWqnfA7P51wpvtx1k3XURgZk6SMR6Slx53McKj0fho6Z0oKnK2ov_0VeiKFwEyDf2zU5bdx_B-B_n-S84l1ypHg-gBNBN-wNWh4xZUHhcsZHpILmQ","e":"AQAB","kid":"8RGoVdVjD8fItyR3FFo0hVNaZYtPGwoP6xKi9e_V7bI","x5t":"RkI5MjI5OUY5ODc1N0Q4QzM0OUYzNkVGMTJDOUEzQkFCOTU3NjE2Rg"}]} No newline at end of file | |||
There was a problem hiding this comment.
what is this for and why is it here?
There was a problem hiding this comment.
its for the tests to test x509 functionality
| payloadJson = URLDecoder.decode(new String(base32.decode(parts[1]), StandardCharsets.UTF_8.name())); | ||
| break; | ||
| case Base64: | ||
| headerJson = StringUtils.newStringUtf8(Base64.decodeBase64(parts[0])); |
There was a problem hiding this comment.
why StringUtils.newStringUtf8() here and new String("", UTF8) above?. Keep 1. Normally, stick to classes provided in the JDK.
There was a problem hiding this comment.
good point. changed.
| @@ -55,13 +60,13 @@ public JWTDecoder(String jwt, EncodeType encodeType) throws Exception { | |||
| String payloadJson = null; | |||
| switch (encodeType) { | |||
There was a problem hiding this comment.
What if the encode type doesn't match any value defined in the switch?
There was a problem hiding this comment.
i provide the values myself, so theres no way it cant be one of the 3
| @Override | ||
| public Verification createVerifierForExtended(String picture, String email, List<String> issuer, List<String> audience, String name, long nbf, long expLeeway, long iatLeeway) { | ||
| throw new UnsupportedOperationException("you shouldn't be calling this method"); | ||
| throw new UnsupportedOperationException("this method has not been implemented"); |
There was a problem hiding this comment.
Sure. Share in the slack channel a doc with all the classes you want to have and the methods/claims each one should have to begin with.
| @Override | ||
| public Verification createVerifierForAccess(List<String> issuer, List<String> audience, long expLeeway, long iatLeeway) { | ||
| throw new UnsupportedOperationException("you shouldn't be calling this method"); | ||
| throw new UnsupportedOperationException("this method has not been implemented"); |
There was a problem hiding this comment.
The only reason I asked you to change the message to "It hasn't been implemented" is if the method is eventually going to be available. If this class is not supposed to have the methods to begin with, remove them.
|
|
||
| private static void assertValidIssuerClaim(List<String> issuer, List<String> value) { | ||
| if (issuer == null || !issuer.containsAll(value) || issuer.size() != value.size()) { | ||
| private static void assertValidIssuerClaim(List<String> actual, List<String> expected) { |
There was a problem hiding this comment.
| String token = "eyJhbGciOiJIUzI1NiIsImN0eSI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.mZ0m_N1J4PgeqWmi903JuUoDRZDBPB7HwkS4nVyWH1M"; | ||
| Algorithm algorithmString = Algorithm.HMAC256("secret"); | ||
| Algorithm algorithmBytes = Algorithm.HMAC256("secret".getBytes(StandardCharsets.UTF_8)); | ||
| JWT jwt = JWT.require(algorithmString).withIssuer("auth0").build(); |
There was a problem hiding this comment.
You changed all the tests to use JWT.require instead. Please roll back all these changes. Those tests are already available here.
This comment applies to all XAlgorithmTest classes
| assertTrue(claims.get(Claims.ISSUER).asString().equals("issuer")); | ||
| assertTrue(claims.get(Claims.SUBJECT).asString().equals("subject")); | ||
| assertTrue(claims.get(Claims.AUDIENCE).asString().equals("audience")); | ||
| assertTrue(claims.get(Claims.EXPIRES_AT).asDate().toString().equals(exp.toString())); |
There was a problem hiding this comment.
what do you mean by "doesn't work"? If two classes are of the same type and have valid values I don't see why equals won't work.
No description provided.