Error building JWT with non-string audience claim type
arvindkrishnakumar-okta opened this issue ยท 4 comments
Describe the bug
If I try building a JWT with a "List" type of "aud" claim, then I see the below exception trace thrown during validation (this used to work in 0.11.x and started breaking since 0.12.x):
java.lang.IllegalArgumentException: Invalid Map 'aud' (Audience) value: [invalid-clientId, test-aud]. Unsupported value type. Expected: java.lang.String, found: java.util.ArrayList
at io.jsonwebtoken.impl.ParameterMap.apply(ParameterMap.java:193)
at io.jsonwebtoken.impl.ParameterMap.put(ParameterMap.java:139)
at io.jsonwebtoken.impl.ParameterMap.put(ParameterMap.java:149)
at io.jsonwebtoken.impl.ParameterMap.put(ParameterMap.java:36)
at io.jsonwebtoken.impl.lang.DelegatingMap.put(DelegatingMap.java:81)
at io.jsonwebtoken.impl.lang.DelegatingMapMutator.add(DelegatingMapMutator.java:45)
at io.jsonwebtoken.impl.DefaultJwtBuilder.claim(DefaultJwtBuilder.java:406)
at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
at com.okta.jwt.impl.jjwt.JjwtAccessTokenVerifierTest.validAudienceIds(JjwtAccessTokenVerifierTest.groovy:44)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:135)
at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:673)
at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:220)
at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:50)
at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:945)
at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:193)
at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:128)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at org.testng.TestRunner.privateRun(TestRunner.java:808)
at org.testng.TestRunner.run(TestRunner.java:603)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:429)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:423)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:383)
at org.testng.SuiteRunner.run(SuiteRunner.java:326)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:95)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1249)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1169)
at org.testng.TestNG.runSuites(TestNG.java:1092)
at org.testng.TestNG.run(TestNG.java:1060)
at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:109)
Caused by: java.lang.IllegalArgumentException: Unsupported value type. Expected: java.lang.String, found: java.util.ArrayList
at io.jsonwebtoken.impl.lang.RequiredTypeConverter.applyFrom(RequiredTypeConverter.java:44)
at io.jsonwebtoken.impl.lang.DefaultParameter.applyFrom(DefaultParameter.java:124)
at io.jsonwebtoken.impl.ParameterMap.apply(ParameterMap.java:176)
... 33 more
I am actually unable to supply any type other than "String" to the claims while building the JWT.
Can you please let me know if I am missing something here? Thanks!
Expected behavior
I should be able to set List type "aud" claim as before without any issues.
Screenshots
Because the RFC allows aud
values to be either a String
or a List
of Strings
, the JwtBuilder#setAudience
method has been deprecated and replaced in JJWT 0.12.0 with an audience sub-builder to support both string and list values. For example:
Jwts.builder().audience().add(list).and()...
But it appears that you're setting this value with the generic jwtBuilder.claim
method, is that correct?
I suppose we could check if the claim is named aud
and the value is a collection, that the claim
method delegate to the .audience().add(collection).and()
method automatically?
Just trying to understand your preferences/expectations.
As a workaround, is it possible to refactor your code to call the .audience().add....and()...
variant instead?
@arvindkrishnakumar-okta just letting you know that we're going to track work for a fix via #890, so I'm closing this issue in favor of that one. Please do feel free to comment here or in that issue if necessary. Thank you for a well-written issue (with screenshots!), that was super helpful!
@lhazlewood Thanks so much for your help with this one.
Actually Jwts.builder().audience().add(list).and()...
worked for me for setting a list of aud
claim values.
But, I want to note that I see the same issue while trying to set a Boolean type for aud
claim.
java.lang.IllegalArgumentException: Invalid Map 'aud' (Audience) value: true. Unsupported value type. Expected: java.lang.String, found: java.lang.Boolean
at io.jsonwebtoken.impl.ParameterMap.apply(ParameterMap.java:193)
at io.jsonwebtoken.impl.ParameterMap.put(ParameterMap.java:139)
at io.jsonwebtoken.impl.ParameterMap.put(ParameterMap.java:149)
at io.jsonwebtoken.impl.ParameterMap.put(ParameterMap.java:36)
at io.jsonwebtoken.impl.lang.DelegatingMap.put(DelegatingMap.java:81)
at io.jsonwebtoken.impl.lang.DelegatingMapMutator.add(DelegatingMapMutator.java:45)
at io.jsonwebtoken.impl.DefaultJwtBuilder.claim(DefaultJwtBuilder.java:406)
at org.codehaus.groovy.vmplugin.v8.IndyInterface.selectMethod(IndyInterface.java:355)
at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
at com.okta.jwt.impl.jjwt.JjwtAccessTokenVerifierTest$_invalidAudienceIds_closure1.doCall(JjwtAccessTokenVerifierTest.groovy:56)
at com.okta.jwt.impl.jjwt.JjwtAccessTokenVerifierTest$_invalidAudienceIds_closure1.doCall(JjwtAccessTokenVerifierTest.groovy)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:343)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:328)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:279)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1006)
at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
at com.okta.jwt.impl.TestUtil.expect(TestUtil.groovy:29)
at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
at com.okta.jwt.impl.jjwt.JjwtAccessTokenVerifierTest.invalidAudienceIds(JjwtAccessTokenVerifierTest.groovy:55)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at org.testng.internal.invokers.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:135)
at org.testng.internal.invokers.TestInvoker.invokeMethod(TestInvoker.java:673)
at org.testng.internal.invokers.TestInvoker.invokeTestMethod(TestInvoker.java:220)
at org.testng.internal.invokers.MethodRunner.runInSequence(MethodRunner.java:50)
at org.testng.internal.invokers.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:945)
at org.testng.internal.invokers.TestInvoker.invokeTestMethods(TestInvoker.java:193)
at org.testng.internal.invokers.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
at org.testng.internal.invokers.TestMethodWorker.run(TestMethodWorker.java:128)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
at org.testng.TestRunner.privateRun(TestRunner.java:808)
at org.testng.TestRunner.run(TestRunner.java:603)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:429)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:423)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:383)
at org.testng.SuiteRunner.run(SuiteRunner.java:326)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:95)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1249)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1169)
at org.testng.TestNG.runSuites(TestNG.java:1092)
at org.testng.TestNG.run(TestNG.java:1060)
at org.apache.maven.surefire.testng.TestNGExecutor.run(TestNGExecutor.java:155)
at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.executeMulti(TestNGDirectoryTestSuite.java:169)
at org.apache.maven.surefire.testng.TestNGDirectoryTestSuite.execute(TestNGDirectoryTestSuite.java:88)
at org.apache.maven.surefire.testng.TestNGProvider.invoke(TestNGProvider.java:137)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385)
at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162)
at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495)
Caused by: java.lang.IllegalArgumentException: Unsupported value type. Expected: java.lang.String, found: java.lang.Boolean
at io.jsonwebtoken.impl.lang.RequiredTypeConverter.applyFrom(RequiredTypeConverter.java:44)
at io.jsonwebtoken.impl.lang.DefaultParameter.applyFrom(DefaultParameter.java:124)
at io.jsonwebtoken.impl.ParameterMap.apply(ParameterMap.java:176)
... 51 more
@arvindkrishnakumar-okta that's expected behavior. The RFC specification requires that value to be a String or a List-of-Strings. Anything else should fail because it violates the specification.