client-policies: editing secure-redirect-uris-enforcer setting in admin ui breaks client policy/profile
#49,252 创建于 2026年5月22日
描述
Before reporting an issue
- I have read and understood the above terms for submitting issues, and I understand that my issue may be closed without action if I do not follow them.
Area
admin/ui
Describe the bug
When editing the settings of the secure-redirect-uris-enforcer executor in the admin UI and leaving the Allow permitted domains field empty (to allow all domains), the profile JSON then contains "allow-permitted-domains": [ null ]. When a client is updated for which a client policy with this profile exists, the update fails with:
Client could not be updated: unknown_error
For more on this error consult the server log.
In in the server log an NullPointerException is logged:
[org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-59) Uncaught server error: java.lang.NullPointerException: Cannot invoke "String.isEmpty()" because "this.pattern" is null
at java.base/java.util.regex.Pattern.<init>(Pattern.java:1574)
at java.base/java.util.regex.Pattern.compile(Pattern.java:1101)
at java.base/java.util.regex.Pattern.matches(Pattern.java:1220)
at java.base/java.lang.String.matches(String.java:2958)
at org.keycloak.services.clientpolicy.executor.SecureRedirectUrisEnforcerExecutor$UriValidation.matchDomain(SecureRedirectUrisEnforcerExecutor.java:501)
at java.base/java.util.stream.MatchOps$1MatchSink.accept(MatchOps.java:90)
at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1685)
at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129)
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230)
at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:632)
at org.keycloak.services.clientpolicy.executor.SecureRedirectUrisEnforcerExecutor$UriValidation.matchDomains(SecureRedirectUrisEnforcerExecutor.java:505)
at org.keycloak.services.clientpolicy.executor.SecureRedirectUrisEnforcerExecutor$UriValidation.isValidNormalUri(SecureRedirectUrisEnforcerExecutor.java:474)
at org.keycloak.services.clientpolicy.executor.SecureRedirectUrisEnforcerExecutor$UriValidation.validate(SecureRedirectUrisEnforcerExecutor.java:286)
at org.keycloak.services.clientpolicy.executor.SecureRedirectUrisEnforcerExecutor.verifyRedirectUri(SecureRedirectUrisEnforcerExecutor.java:254)
at org.keycloak.services.clientpolicy.executor.SecureRedirectUrisEnforcerExecutor.verifyRedirectUris(SecureRedirectUrisEnforcerExecutor.java:240)
at org.keycloak.services.clientpolicy.executor.SecureRedirectUrisEnforcerExecutor.verifyRedirectUris(SecureRedirectUrisEnforcerExecutor.java:211)
at org.keycloak.services.clientpolicy.executor.SecureRedirectUrisEnforcerExecutor.executeOnEvent(SecureRedirectUrisEnforcerExecutor.java:183)
at org.keycloak.services.clientpolicy.DefaultClientPolicyManager.lambda$triggerOnEvent$1(DefaultClientPolicyManager.java:59)
at org.keycloak.services.clientpolicy.DefaultClientPolicyManager.execute(DefaultClientPolicyManager.java:157)
at org.keycloak.services.clientpolicy.DefaultClientPolicyManager.doPolicyOperation(DefaultClientPolicyManager.java:80)
at org.keycloak.services.clientpolicy.DefaultClientPolicyManager.triggerOnEvent(DefaultClientPolicyManager.java:57)
at org.keycloak.services.resources.admin.ClientResource.update(ClientResource.java:159)
at org.keycloak.services.resources.admin.ClientResource$quarkusrestinvoker$update_ce017e4b2823ce9327deccf447c672318503a2ff.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:190)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$15.runWith(VertxCoreRecorder.java:677)
at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2651)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2630)
at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1622)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1589)
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)
Version
26.6.2
Regression
- The issue is a regression
Expected behavior
The client policy should be evaluated correctly, and an already compliant client should be updated without any errors. If the client is not compliant, a useful error message should be shown.
Actual behavior
In the UI, the user only gets an unknown_error because a NullPointerException happens and cannot update the client, even when it does not violate any client policy.
How to Reproduce?
- Create a Client Profile with the
secure-redirect-uris-enforcerExecutor - Leave the "Allow permitted domains" filed empty in the settings of the Executor e.g.:
- Save the profile
- The JSON of the Executor show then like:
{
"executor": "secure-redirect-uris-enforcer",
"configuration": {
"allow-ipv4-loopback-address": "true",
"allow-ipv6-loopback-address": "true",
"allow-private-use-uri-scheme": "true",
"oauth-2-1-compliant": "false",
"allow-http-scheme": true,
"allow-wildcard-context-path": false,
"allow-open-redirect": false,
"allow-permitted-domains": [
null
]
}
}
- When the profile is used in a client policy and the policy is evaluated, the null entry in the JSON list leads to a NullPointerException.
Anything else?
The workaround is to edit the JSON of the client profile and remove the null value from the list or remove the allow-permitted-domains property completely from the profile. But every time the executor is edited through the UI, the JSON must be corrected again.