Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deterministic Base64 support #333

Closed
lhazlewood opened this issue Jul 5, 2018 · 20 comments
Closed

Deterministic Base64 support #333

lhazlewood opened this issue Jul 5, 2018 · 20 comments
Milestone

Comments

@lhazlewood
Copy link
Contributor

lhazlewood commented Jul 5, 2018

This issue is being created to supersede similarly reported issues and represent a canonical issue to enable the following:

  1. Check to see if java.util.Base64 is available at runtime using Classes.isAvailable (or similar), and if so, use that. This is the best/fastest one available to most default environments and should be used whenever possible.

    java.util.Base64 is available on JDK >= 8 and Android level >= 26

  2. If java.util.Base64 is not available:

    1. If the runtime enviornment is Android, use android.util.Base64. This is available on Android API level >= 8 (i.e. >= Android 2.2, "Froyo")

    2. Otherwise, assume < JDK8, and use javax.xml.bind.DatatypeConverter. JDK 7 has this included automatically.

  3. In any case, enable JwtBuilder and JwtParser methods that allow a user to specify their own Base64 TextCodec if/when desired, e.g. Jwts.parser().setBase64Codec(myBase64Codec) This last option would allow the user to enable Base64 support in any environment that doesn't match 1 and 2 above.

  4. Ensure any codec implementation will throw exceptions on invalid inputs when possible. See Consider using a different Base64 decoder #143 as an example. Exceptions should be thrown when possible to indicate invalid input, wrapping/propagating the original codec exception.

Implementation note: Perhaps instead of using javax.xml.bind.DatatypeConverter - which doesn't always fail on invalid inputs (see #143), we should embed the Commons Codec or MigBase64 implementation.

@lhazlewood
Copy link
Contributor Author

cc @RyanBard

@lhazlewood
Copy link
Contributor Author

@RyanBard I don't think we'll need the JAXB dependency in the pom now based on the above fallback logic? That is, javax.xml.bind.DatatypeConverter would only be used in a JDK 7 environment which is guaranteed to exist in that JDK. Do you agree? Or might I be missing something?

@RyanBard
Copy link
Contributor

RyanBard commented Jul 5, 2018

@lhazlewood I agree.

@lhazlewood
Copy link
Contributor Author

@RyanBard I'm just about to start work on this - I just wanted to make sure you haven't already so we're not stepping on each other's toes. Let me know.

@RyanBard
Copy link
Contributor

RyanBard commented Jul 5, 2018

@lhazlewood I haven't started anything yet. When you have something in a branch, if you want me to test non-android java, let me know (I have a laptop setup w/ 7, 8, and 9 that I can test on easily).

@azagniotov
Copy link

azagniotov commented Jul 5, 2018

@lhazlewood the approach sounds reasonable.
But, I do want to say that that I hope this won't set a precedent for having such conditional logic in other places when trying to support multiple JDKs/Platforms. It can easily get out of hand over time. Thoughts?

@lhazlewood
Copy link
Contributor Author

lhazlewood commented Jul 5, 2018

Thanks @RyanBard and @azagniotov - I'll cc you when a branch is created so you can review.

And I agree on getting out of hand for multiple JDKs/platforms, but I do tend to prefer usability over internal library complexity. In other words, if it's mildly complex for the JJWT dev team but makes 99% of users happy, I'll usually favor the slightly more complex approach.

That said, thankfully we're only checking 3 sources max here, so it's pretty trivial.

@RyanBard
Copy link
Contributor

RyanBard commented Jul 6, 2018

@lhazlewood I added some dockerfiles to test java 7, 8, & 10 in containers to that jjwt-consumer-example I used to show the runtime problems w/ the base64 code:
RyanBard/jjwt-consumer-example@50e2dbd

It can show the problem (and verify if a fix works), however, I haven't thought through how you would test local changes with it yet (it's just running mvnw test in the container, which is fetching from repo1).

So if you don't have a snapshot or 1-off version you feel comfortable publishing (or an artifactory/nexus/etc. you feel comfortable publishing to), I'll have to cook up a way to include in the jjwt source as well to test unpublished changes.

@lhazlewood
Copy link
Contributor Author

@RyanBard in this particular case, I'm thinking of using reflection (and memoizing results for performance) so that it builds on JDK7 and runs as expected on JDK8 (with test cases to verify this of course). JJWT travis builds currently run on OpenJDK 7, and Oracle JDK 8 and 9. We can add 10 easily.

@RyanBard
Copy link
Contributor

RyanBard commented Jul 6, 2018

@lhazlewood , see #282 for an explanation of the gap of testing in your travis builds.

You are testing that jjwt builds w/ various versions of java, however, the target jdk isn't the newer versions of the jdk (so you're using java 9 to build java 7 compliant byte code for example).

This is the reason why #267 didn't completely fix #259 (and I created #282).

@lhazlewood
Copy link
Contributor Author

@RyanBard I don't see the rationale here. If you build on JDK 9 it must succeed on JDK 9 because all of the tests exercise 100% of functionality. The 1.7 in the pom is only for bytecode compatibility - not library/API availability.

I think the reason you saw #282 was because Oracle completely hosed JDK 9 and how it handled JAXB (among other things). This is one of the biggest reasons why JDK 9 was abandoned and has already reached its end-of-(very-short)-life.

In other words, we have no reason not to trust Travis and it's JDK builds - no need for Docker-based tests. Those were only needed for JDK9 because it was an anomaly, but we don't have to worry about that anymore.

@RyanBard
Copy link
Contributor

RyanBard commented Jul 7, 2018

When I saw #267 as the fix + verification of java 9, I assumed that the source compatibility was affecting what modules maven chose to run tests under or something. That was a misconception on my part. I just tinkered with my source/target and it didn't pass the tests -- which I thought I had tested for at some point, but probably overlooked it.

Was the gap in TravisCI that the oraclejdk was including the jaxb module, but openjdk was not (and the fix on your side is to test with both jdks)?

@lhazlewood
Copy link
Contributor Author

Yep, you got it - oraclejdk included it but openjdk apparently did not. The solution to detect this was to have travis build against both oracle and openjdk.

@lhazlewood
Copy link
Contributor Author

Just an update on this ticket - I'm avoiding the "if on platform X, use Base64 decoder Y" logic entirely. I decided to fork (and directly embed) a modified/stripped-down version of MigBase64 (and included proper license attribution in JJWT per Mig's license requirements).

This means JJWT is guaranteed to have deterministic Base64 support on all platforms - different JDK versions, Android, whatever. Thankfully it only added a single class, and was very lightweight/minimal.

@azagniotov
Copy link

@lhazlewood I agree with your intentions to avoid if-else mess.

Just to point something out - the https://github.com/brsanthu/migbase64 does not have any functional tests, only some speed test and one more test which is commented out. I hope that the code does what it suppose to do

@lhazlewood
Copy link
Contributor Author

@azagniotov I've used migbase64 in production environments before, so I'm not too worried about that. That said, I'm adding tests for it anyway to ensure we still keep 100% coverage.

RyanBard added a commit to RyanBard/jjwt that referenced this issue Jul 8, 2018
* Adds test vector to base64 & base64url for many ascii chars

Signed-off-by: Ryan Bard <[email protected]>
lhazlewood added a commit that referenced this issue Jul 8, 2018
…_vector

tests #333: Add ascii base64 test vector
@lhazlewood lhazlewood changed the title Elegant Base64 support Deterministic Base64 support Jul 9, 2018
@lhazlewood
Copy link
Contributor Author

This has been released in 0.10.0.

@jainsahab
Copy link

Hi, is it available on mvnrepository? I can't find it over there.

@azagniotov
Copy link

@jainsahab the real Maven Central is https://search.maven.org/ . Everything else are just mirrors of the real thing.

@lhazlewood
Copy link
Contributor Author

Hi, is it available on mvnrepository? I can't find it over there.

@jainsahab it is available. 0.10.0 introduced separate artifacts, so there is no longer a single jjwt .jar. See https://search.maven.org/search?q=g:io.jsonwebtoken%20a:jjwt-*

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants