Issue Denying access with Authentication Flows

See original GitHub issue

Describe the bug

Hey there.

I’m currently trying to deny access to users that are not associated with a specific client role. Following the documentation on https://www.keycloak.org/docs/latest/server_admin/index.html#explicitly-deny-allow-access-in-conditional-flows the flow that I’m trying to use is the following (basically a copy of Direct Grant changing just the OTP conditional):

Username Validation   [REQUIRED]
Password   [REQUIRED]
Check Role   [CONDITIONAL]
  Condition - User Role(User MUST HAVE ROLE)   [REQUIRED]
  Deny Access   [REQUIRED]

When I try to authenticate a user that has the role, everything works as expected, however when I try to authenticate a user that doesn’t have the role associated I get an unknown_error and the logs are as follows:

2022-07-27 12:03:59,948 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-54) Uncaught server error: java.lang.IllegalArgumentException: RESTEASY003715: path was null
        at org.jboss.resteasy.specimpl.ResteasyUriBuilderImpl.path(ResteasyUriBuilderImpl.java:382)
        at org.keycloak.authentication.AuthenticationProcessor$Result.getActionUrl(AuthenticationProcessor.java:562)
        at org.keycloak.authentication.AuthenticationProcessor$Result.form(AuthenticationProcessor.java:536)
        at org.keycloak.authentication.authenticators.access.DenyAccessAuthenticator.authenticate(DenyAccessAuthenticator.java:49)
        at org.keycloak.authentication.DefaultAuthenticationFlow.processSingleFlowExecutionModel(DefaultAuthenticationFlow.java:460)
        at org.keycloak.authentication.DefaultAuthenticationFlow.processFlow(DefaultAuthenticationFlow.java:264)
        at org.keycloak.authentication.DefaultAuthenticationFlow.processSingleFlowExecutionModel(DefaultAuthenticationFlow.java:395)
        at org.keycloak.authentication.DefaultAuthenticationFlow.processFlow(DefaultAuthenticationFlow.java:264)
        at org.keycloak.authentication.AuthenticationProcessor.authenticateOnly(AuthenticationProcessor.java:1030)
        at org.keycloak.protocol.oidc.endpoints.TokenEndpoint.resourceOwnerPasswordCredentialsGrant(TokenEndpoint.java:603)
        at org.keycloak.protocol.oidc.endpoints.TokenEndpoint.processGrantRequest(TokenEndpoint.java:200)
        at jdk.internal.reflect.GeneratedMethodAccessor343.invoke(Unknown Source)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
        at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524)
        at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:192)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:152)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:183)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:141)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:32)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
        at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
        at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:82)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:42)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
        at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:67)
        at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:55)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:380)
        at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:358)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1212)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:163)
        at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
        at org.keycloak.quarkus.runtime.integration.web.QuarkusRequestFilter.lambda$createBlockingHandler$1(QuarkusRequestFilter.java:71)
        at io.vertx.core.impl.ContextImpl.lambda$null$0(ContextImpl.java:159)
        at io.vertx.core.impl.AbstractContext.dispatch(AbstractContext.java:100)
        at io.vertx.core.impl.ContextImpl.lambda$executeBlocking$1(ContextImpl.java:157)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$13.runWith(VertxCoreRecorder.java:543)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:829)

2022-07-27 12:03:59,951 WARN  [io.agroal.pool] (executor-thread-54) Datasource '<default>': JDBC resources leaked: 2 ResultSet(s) and 2 Statement(s)

Is this a bug or am I missing something?

Version

18.0.2

Expected behavior

Getting an unauthorized error when trying to authenticate a user that doesn’t have the required role.

Actual behavior

Getting a server error, with an exception on the logs.

How to Reproduce?

  • Create a copy of direct grant authentication flow, replace the OTP step with a conditional that checks if the user doesn’t have the role and then denies access.
  • Add this flow to a client
  • create a user without the specified role and authenticate it.

Anything else?

No response

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:1
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
filipetavarescommented, Jul 27, 2022

@lexcao Oh sorry, you are correct, I did forgot to mention the authentication method. I thought I mentioned it somewhere but what I said was that I copied the Direct Grant Authentication Flow, my bad.

Also, thank you very much for the in depth explanation, that is way out of my league, but glad to know this is a bug and not something that I was missing (also good to know that it works on browser).

1reaction
lexcaocommented, Jul 27, 2022

Hi @filipetavares I reproduced this from my local, it should be an issue, I think. And I want to say, you forgot to mention what authentication method you’re using. Finally, I found you are using Direct Grant Flow from the given log. (TokenEndpoint.resourceOwnerPasswordCredentialsGrant) It works normally when I use Browser Flow from my side.


Here is my investigation:

From the log, I found the path was null happens here.

https://github.com/keycloak/keycloak/blob/8ed9ce29d159d10728ada8da708dab111921b024/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java#L564

And for the direct grant flow, the AuthenticationProcessor.this.flowPath is not initialized.

https://github.com/keycloak/keycloak/blob/8ed9ce29d159d10728ada8da708dab111921b024/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java#L594-L605

IMO, it makes sense for direct grant flow not initializing the flow path, if not it could mislead the user to the front page when an error happens in token API.

I think it should respond the error properly with the related content type (like JSON) when calling token API with direct grant.


How to fix it? I have some ideas and need the Keycloak team to check.

Proposal 1

Modify the DenyAccessAuthenticator to support JSON response type.

https://github.com/keycloak/keycloak/blob/650f3a8367ea5d5f818d36b4c5fecf50d0576154/services/src/main/java/org/keycloak/authentication/authenticators/access/DenyAccessAuthenticator.java#L48-L52

Proposal 2

Modify the challenge of direct grant flow, which makes it respond JSON instead of HTML.

https://github.com/keycloak/keycloak/blob/650f3a8367ea5d5f818d36b4c5fecf50d0576154/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java#L605-L611

Read more comments on GitHub >

github_iconTop Results From Across the Web

Logged into Account but Flow says Access Denied
I'm in my O365 account, and actually just set up one Flow to save my email attachments to OneDrive. However, I went to...
Read more >
Browser flow - conditional access denied for IdP login
Hi, I want to restrict login to a service based on whether the user has a role or not. I followed this SO...
Read more >
Login flow fault blocks access to Salesforce
Trigger failure causes login flow exception. Problem is that I have only one admin user in this development sandbox.
Read more >
Authentication flows - React Navigation
Authentication flows. Most apps require that a user authenticate in some way to have access to data associated with a user or other...
Read more >
OAuth authorization code grant flow : Access Denied Error
No issue in getting the authorization code, the only problem is while getting the access token and refresh token using the authorization code....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found