How should the SecretQuestion example configure the authentication flow

I’m learning KeyCloak, and I’m currently integrating official secret question examples.

I have placed the compiled JAR package in the /provider directory and executed the ${kc.home.dir}/bin/kc.sh build command.I then reopened KeyCloak and was able to select secret question from the authentication stream.

But when I connect my client link and automatically jump to the Keycloak authentication page, I get the following error:
Invalid username or password.

server error logs:

2022-08-11 23:34:54,669 WARN  [org.keycloak.authentication.DefaultAuthenticationFlow] (executor-thread-44) REQUIRED and ALTERNATIVE elements at same level! Those alternative executions will be ignored: [auth-cookie, identity-provider-redirector]
2022-08-11 23:34:54,670 WARN  [org.keycloak.services] (executor-thread-44) KC-SERVICES0013: Failed authentication: org.keycloak.authentication.AuthenticationFlowException: authenticator: secret-question-authenticator
	at org.keycloak.authentication.DefaultAuthenticationFlow.processSingleFlowExecutionModel(DefaultAuthenticationFlow.java:423)
	at org.keycloak.authentication.DefaultAuthenticationFlow.processFlow(DefaultAuthenticationFlow.java:250)
	at org.keycloak.authentication.AuthenticationProcessor.authenticateOnly(AuthenticationProcessor.java:1017)
	at org.keycloak.authentication.AuthenticationProcessor.authenticate(AuthenticationProcessor.java:879)
	at org.keycloak.protocol.AuthorizationEndpointBase.handleBrowserAuthenticationRequest(AuthorizationEndpointBase.java:151)
	at org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint.buildAuthorizationCodeAuthorizationResponse(AuthorizationEndpoint.java:338)
	at org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint.process(AuthorizationEndpoint.java:194)
	at org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint.buildGet(AuthorizationEndpoint.java:112)
	at jdk.internal.reflect.GeneratedMethodAccessor479.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:90)
	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:545)
	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-08-11 23:34:54,679 WARN  [org.keycloak.events] (executor-thread-44) type=LOGIN_ERROR, realmId=ca7ca487-f63a-4ba1-ab7f-c95b9f4f04a3, clientId=app-authz-vanilla, userId=null, ipAddress=172.20.25.22, error=invalid_user_credentials, auth_method=openid-connect, auth_type=code, response_type=code, redirect_uri=http://172.20.25.22:8081/getUserInfo, code_id=827be7e0-b798-4008-8eb0-561ff1e6288f, response_mode=query, authSessionParentId=827be7e0-b798-4008-8eb0-561ff1e6288f, authSessionTabId=kS_EC6JoUdY

  • My authentication flow is configured as follows

  • My client configuration is as follows

keycloak:
#  public-client: true
  realm: hello-world-auth-test
  auth-server-url: http://10.30.51.30:8080/
  ssl-required: none
  resource: app-authz-vanilla
  verify-token-audience: true
#  use-resource-role-mappings: true
  credentials:
    secret: YTeBZRQyYuXYN5wEI1vkl6XNrconYo1b
  confidential-port: 0
  security-constraints:
    -  #logout
      securityCollections:
        - name: logout
          patterns:
            - /logout
    - # roles
      authRoles:
        - admin
      securityCollections:
        -
          name: admin
          patterns:
            - /admin/*
            - /system/*
    - # test
      authRoles:
        - admin
        - user
      securityCollections:
        - name: user
          patterns:
            - /test/*
            - /getUserInfo
            - /custom-resource/*
  • My Keycloak version is 19.0.1

KeyCloak Version

19.0.1

compile environment

  • openjdk 11.0.2
  • windows10
  • idea
  • KeyCloak V19.0.1

runtime environment

  • centos7
  • openjdk 11.0.16
  • Client spring-boot 2.3.5

Hi.

I’m pretty sure the Secret Question authenticator needs a successful authenticated user first. That is: you put it bellow a User Password Form or other authenticator which will pass an authenticated user to Secret Question.

Note that authenticated user means that some authenticator decided, based on user provided information (it could be a x509 authenticator, cookie authenticator and others) that the user is X.

Later authenticators can then get this User is X information to implement extra checks and decided that being user X is not enough. In Secret Question, the user should provided a correct answer.

If the last authenticator in your flow returns sucess, than the flow is sucessful, if now, user is denied access, even if it completed successfully the initial authenticators.

Hi,
I modified my authentication flow as you said, but how should I set the user’s secret question?

I’m not very familiar with that example, but if I recall correcly, besides the authenticator, it creates a new kind of REQUIRED_ACTION. So you can toggle it on for a particular user.