jwtk/jjwt

java.lang.NoSuchMethodError: org.json.JSONTokener.<init>(Ljava/io/Reader;)V

ejoseca opened this issue ยท 15 comments

Description
JWS cannot be deserialized in Android devices.

Steps to reproduce

  1. Add the required dependencies for an Android project as shown in the README.md
dependencies {
    api('io.jsonwebtoken:jjwt-api:0.12.3')
    runtimeOnly('io.jsonwebtoken:jjwt-impl:0.12.3') 
    runtimeOnly('io.jsonwebtoken:jjwt-orgjson:0.12.3') {
        exclude(group: 'org.json', module: 'json') //provided by Android natively
    }
}
  1. Create a parser
Jwts.parser()
  .verifyWith(publicKey)
  .build()
  .parseSignedClaims(jwsString);
  1. Try to deserialize a signed JWT in an Android device
  2. An error is thrown: java.lang.NoSuchMethodError: org.json.JSONTokener.<init>(Ljava/io/Reader;)V

Cause
Since 0.12.0, the class io.jsonwebtoken.orgjson.io.OrgJsonDeserializer, in its parse() method, is trying to create a JSONTokener passing a Reader as argument to the constructor.
This constructor is not available in the Android implementation of JSONTokener: https://android.googlesource.com/platform/libcore/+/refs/heads/main/json/src/main/java/org/json/JSONTokener.java

This appears to be duplicate of #867. Could you please try the workaround listed there and let us know if that doesn't work?

We'll need to change the implementation on a future release to detect if this method is available on Android and then fallback to older logic if not.

@lhazlewood I ran into the same issue described. I tried the proposed solution (not excluding the org.json dependency), and I still run into the same issue. It looks like it might of worked for someone else in #867, but it didn't solve it for me.

@DanielGalarza thanks for trying the proposed solution. What version of Android are you using?

@phamhongphong what version of Android are you using since it worked for you?

@DanielGalarza just to confirm, your dependencies looked like this?

dependencies {
    api('io.jsonwebtoken:jjwt-api:0.12.3')
    runtimeOnly('io.jsonwebtoken:jjwt-impl:0.12.3') 
    runtimeOnly('io.jsonwebtoken:jjwt-orgjson:0.12.3')
}

@lhazlewood Not excluding org.json works, but I don't want to include it in our project because of maintainability.
Overriding the Android's JSON implementation with a transitive dependency could lead to use methods that will not be available when it is removed.

For now, we are going to stay in version 0.11.5.

@ejoseca that's helpful, thanks for the feedback. We'll use this issue to track the work for a fix.

@ejoseca which version of Android are you using? I'd like to ensure a fix is compatible with at least that (and potentially others).

Actually, Android version probably doesn't matter, it appears that even the latest only supports a String constructor:

https://developer.android.com/reference/org/json/JSONTokener#JSONTokener(java.lang.String)

We support from Android 5 (SDK 21) onwards, but the JSON implementation is almost the same for all Android versions.

JSONTokener in Android 2.3: https://android.googlesource.com/platform/libcore/+/refs/tags/android-2.3_r1/json/src/main/java/org/json/JSONTokener.java

@DanielGalarza just to confirm, your dependencies looked like this?

dependencies {
    api('io.jsonwebtoken:jjwt-api:0.12.3')
    runtimeOnly('io.jsonwebtoken:jjwt-impl:0.12.3') 
    runtimeOnly('io.jsonwebtoken:jjwt-orgjson:0.12.3')
}

@lhazlewood Correct, that's what I tried to use, but still ran into issues. As far as the Android versions we are running, It's Android 8 (API 26) and through Android 14 (API 34)

I will also be staying on v11.5 for now, but I am looking forward to a fix soon, so that I can migrate over to 12+

As updated by the PR merge, I've fixed this issue AFAICT. If anyone in this thread has the ability to build the latest master from source and test, I'd appreciate it! Please report back if you experience any problems.

This will be released in an upcoming 0.12.4 release.

As updated by the PR merge, I've fixed this issue AFAICT. If anyone in this thread has the ability to build the latest master from source and test, I'd appreciate it! Please report back if you experience any problems.

@lhazlewood I'm happy to test this, but I'm not sure how to test you master branch. Is there a specific way I can do that from my Android project? Would this work:

    api("io.jsonwebtoken:jjwt-api:master-SNAPSHOT")
    runtimeOnly("io.jsonwebtoken:jjwt-impl:master-SNAPSHOT")
    runtimeOnly("io.jsonwebtoken:jjwt-orgjson:master-SNAPSHOT") {
        exclude group: "org.json", module: "json" //provided by Android natively
    }

@DanielGalarza you'd have to git clone and then build JJWT yourself on your own machine, and then use the snapshots .jars created by the JJWT build in your Android project.

@DanielGalarza I believe it would look something like this after building JJWT locally via mvn clean install:

    api("io.jsonwebtoken:jjwt-api:0.12.4-SNAPSHOT")
    runtimeOnly("io.jsonwebtoken:jjwt-impl:0.12.4-SNAPSHOT")
    runtimeOnly("io.jsonwebtoken:jjwt-orgjson:0.12.4-SNAPSHOT") {
        exclude group: "org.json", module: "json" //provided by Android natively
    }