Skip to content

Comments

X509#7

Open
jdahmubed wants to merge 5 commits intomasterfrom
x509
Open

X509#7
jdahmubed wants to merge 5 commits intomasterfrom
x509

Conversation

@jdahmubed
Copy link
Contributor

No description provided.

@jdahmubed jdahmubed requested a review from TMSCH December 20, 2017 22:49
}

@Override
public void verifyWithX509(DecodedJWT jwt, EncodeType encodeType, String jwksFile, String pemFile) throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this require a jwksFile and a pemFile?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's needed to get the x509 cert. why do you feel like it's unnecessary?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain what's in each that you need? I thought the jwksFile contained the key.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jwksFile contains the x5c value. pemFile is the public key.

}

@Override
public void verifyWithX509(DecodedJWT jwt, EncodeType encodeType, String jwksFile, String pemFile) throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you write a wrapper for this that infers its EncodingType? That will probably work better in most cases

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. i can have a constant that stores what type of encodingtype was used to sign the JWT with and use it accordingly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed locally

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool. Can you do anything for unknown JWTs? Can't we see what base something is in based off what characters are used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it that we have to run "decode" again on a "decoded jwt"? I think there could be better naming here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetchContentAndSignatureByteArrays()?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good


@Override
public void verify(DecodedJWT jwt, EncodeType encodeType) throws Exception {
List<byte[]> byteArrayList = decode(jwt, encodeType);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link

@lyzhang27 lyzhang27 Dec 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is never used, along with org.apache.commons.codec.Encoder, org.apache.commons.codec.binary.StringUtils,
java.net.URLDecoder, java.util.logging.Logger

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it that we have all these methods that shouldn't be called?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because they're implementing the interface method. they are required to implement it or declare the class abstract.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@lccodes lccodes requested a review from lbalmaceda January 10, 2018 17:25
private final Header header;
private final Payload payload;

private static final String FACEBOOK = "facebook";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prepend something that indicates where this could be used. i.e. "ISSUER_FACEBOOK". Same for the one below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


public static GoogleOrFbJwtCreator decodeJWT(DecodedJWT jwt) {
Map<String, Claim> claims = jwt.getClaims();
String issuer = claims.get(Claims.ISSUER).asString();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

javadoc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

import org.apache.commons.codec.binary.StringUtils;

import java.io.UnsupportedEncodingException;
import java.io.*;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to import only the required classes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


@Override
public void verifyWithX509(DecodedJWT jwt, String jwksFile, String pemFile) throws Exception {
throw new UnsupportedOperationException("X509 is not supported for HMAC");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"X509 is not supported for HMAC algorithm"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

if(!addedClaims.get(claim))
for(String claim : requiredClaims.keySet())
if(!requiredClaims.get(claim))
throw new Exception("Standard claim: " + claim + " has not been set");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we please throw something more descriptive than "Exception"? i.e. IllegalStateException/IllegalArgumentException or even a RequiredClaimException

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

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"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use Claims constants

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont see why its a prob for tests

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor Author

@jdahmubed jdahmubed Mar 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use Claims constants

assertThat(claims, is(notNullValue()));
exception.expect(UnsupportedOperationException.class);
claims.put("name", null);
claims.put(Claims.NAME, null);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use Claims constants

@Test
public void shouldSerializeStrings() throws Exception {
ClaimsHolder holder = holderFor("name", "Auth0 Inc");
ClaimsHolder holder = holderFor(Claims.NAME, "Auth0 Inc");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use Claims constants

Copy link
Collaborator

@lbalmaceda lbalmaceda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yet another JSON library?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this for and why is it here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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]));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why StringUtils.newStringUtf8() here and new String("", UTF8) above?. Keep 1. Normally, stick to classes provided in the JDK.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. changed.

@@ -55,13 +60,13 @@ public JWTDecoder(String jwt, EncodeType encodeType) throws Exception {
String payloadJson = null;
switch (encodeType) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the encode type doesn't match any value defined in the switch?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@TMSCH TMSCH removed their request for review May 23, 2018 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants