PAR Request fails when using Keycloak
oidcc version
3.5.1
Erlang version
27.3.3
Elixir version
1.18.3
Summary
Under certain circumstances, the PAR request to Keycloak will fail. This seems to be partially related to #391, or at least that issue pointed me in the right direction.
Keycloak 26.2.0 seems to include the fix to the linked issue (and is the version I run), so I'm not sure if this is a Keycloak issue or an oidcc issue, but I'm hoping the maintainers would have a better idea of which system the issue lies in.
Current behavior
Keycloak refuses the initial PAR request with an error:
Invalid request: java.lang.RuntimeException: Request object encrypted with different algorithm than client requested algorithm
How to reproduce
I've narrowed it down to a very specific setting in the Keycloak client configuration.
When the advanced setting "Request Object Encryption Algorithm" is not set (i.e. doesn't appear in the attributes list of an export) everything works fine.
If the setting is set to "Any", or any of the available options, the PAR request is rejected and oidcc returns the dreaded Redirect URI Generation Failed error.
In the client export from Keycloak, the related setting is request.object.encryption.alg in the attributes object. I found while fiddling with the client settings it would randomly start failing, and apparently some unrelated options being saved will cause this setting to be added to the list with a value of any, and causes the error described.
Expected behavior
There should be an agreement between the requested encryption algorithm, and the actual encryption algorithm used in the PAR request.
@dabaer Are you able to debug the keycloak error further?
Specifically: What is present in the encryptionAlg and jwt.getHeader().getRawAlgorithm() variables?
https://github.com/keycloak/keycloak/blob/08704df6516078cb31246861bb5858ef51838690/services/src/main/java/org/keycloak/protocol/oidc/endpoints/request/AuthzEndpointRequestObjectParser.java#L101-L107
Sorry for the delay, setting up Keyloak for dev was more involved than I had expected. Here's the output of those variables:
**********[encryptionAlg]: any **********[rawAlgorithm]: RSA-OAEP
Interestingly when I set the algorithm setting to RSA-OAEP I get a deserialization error:
Invalid request: java.lang.RuntimeException: Failed to deserialize JWT
at org.keycloak.jose.jws.DefaultTokenManager.decodeClientJWT(DefaultTokenManager.java:169)
at org.keycloak.protocol.oidc.endpoints.request.AuthzEndpointRequestObjectParser.<init>(AuthzEndpointRequestObjectParser.java:45)
at org.keycloak.protocol.oidc.par.endpoints.request.ParEndpointRequestObjectParser.<init>(ParEndpointRequestObjectParser.java:33)
at org.keycloak.protocol.oidc.par.endpoints.request.ParEndpointRequestParserProcessor.parseRequest(ParEndpointRequestParserProcessor.java:82)
at org.keycloak.protocol.oidc.par.endpoints.ParEndpoint.request(ParEndpoint.java:109)
at org.keycloak.protocol.oidc.par.endpoints.ParEndpoint$quarkusrestinvoker$request_af906d5d7425914e86d7ccc25bd818affb15f104.invoke(Unknown Source)
at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$15.runWith(VertxCoreRecorder.java:638)
at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654)
at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'eyJhbGciOiJIUzI1NiJ9': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 22]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2584)
at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2610)
at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2618)
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:825)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._reportInvalidToken(UTF8StreamJsonParser.java:3662)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._handleUnexpectedValue(UTF8StreamJsonParser.java:2749)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._nextTokenNotInObject(UTF8StreamJsonParser.java:867)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.nextToken(UTF8StreamJsonParser.java:753)
at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:5004)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4910)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3921)
at org.keycloak.util.JsonSerialization.readValue(JsonSerialization.java:78)
at org.keycloak.jose.jws.DefaultTokenManager.decodeClientJWT(DefaultTokenManager.java:167)
... 17 more
Not sure where that deserialization error came from but I tried again and inspected some more of the variables in the code path:
**********[encryptionAlg]: RSA-OAEP
**********[rawAlgorithm]: RSA-OAEP
**********[encryptionEncAlg]: any
**********[headerEncAlg]: A256GCM
So the encryption encoding is also getting hung up. Seems like it's a Keycloak issue then? I imagine they should be handling the "any" case in these two places.
The plot thickens, deserialization error returns:
**********[encryptionAlg]: RSA-OAEP
**********[rawAlgorithm]: RSA-OAEP
**********[encryptionEncAlg]: A256GCM
**********[headerEncAlg]: A256GCM
2025-04-27 13:51:04,906 WARN [org.keycloak.services] (executor-thread-1) KC-SERVICES0097: Invalid request: java.lang.RuntimeException: Failed to deserialize JWT
at org.keycloak.jose.jws.DefaultTokenManager.decodeClientJWT(DefaultTokenManager.java:169)
at org.keycloak.protocol.oidc.endpoints.request.AuthzEndpointRequestObjectParser.<init>(AuthzEndpointRequestObjectParser.java:45)
at org.keycloak.protocol.oidc.par.endpoints.request.ParEndpointRequestObjectParser.<init>(ParEndpointRequestObjectParser.java:33)
at org.keycloak.protocol.oidc.par.endpoints.request.ParEndpointRequestParserProcessor.parseRequest(ParEndpointRequestParserProcessor.java:82)
at org.keycloak.protocol.oidc.par.endpoints.ParEndpoint.request(ParEndpoint.java:109)
at org.keycloak.protocol.oidc.par.endpoints.ParEndpoint$quarkusrestinvoker$request_af906d5d7425914e86d7ccc25bd818affb15f104.invoke(Unknown Source)
at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$15.runWith(VertxCoreRecorder.java:638)
at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654)
at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'eyJhbGciOiJIUzI1NiJ9': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 22]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2584)
at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2610)
at com.fasterxml.jackson.core.JsonParser._constructReadException(JsonParser.java:2618)
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:825)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._reportInvalidToken(UTF8StreamJsonParser.java:3662)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._handleUnexpectedValue(UTF8StreamJsonParser.java:2749)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser._nextTokenNotInObject(UTF8StreamJsonParser.java:867)
at com.fasterxml.jackson.core.json.UTF8StreamJsonParser.nextToken(UTF8StreamJsonParser.java:753)
at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:5004)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4910)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3921)
at org.keycloak.util.JsonSerialization.readValue(JsonSerialization.java:78)
at org.keycloak.jose.jws.DefaultTokenManager.decodeClientJWT(DefaultTokenManager.java:167)
And here is the output of those variables when those attributes are removed in Keycloak:
**********[encryptionAlg]: null
**********[rawAlgorithm]: RSA-OAEP
**********[encryptionEncAlg]: null
**********[encryptionAlg]: null
**********[rawAlgorithm]: RSA-OAEP
**********[encryptionEncAlg]: null
Thanks for tracking this down. This sure sounds like a Keycloak issue. Would you mind reporting that issue there?
I ran into this issue with Keycloak.
To bypass this any issue, I ran:
docker exec -it <container> /opt/keycloak/bin/kcadm.sh \
update clients/$CID -r cb \
-s 'attributes."request.object.signature.alg"=' \
-s 'attributes."request.object.encryption.alg"=' \
-s 'attributes."request.object.encryption.enc"='