Refresh loop and REFRESH_TOKEN_ERROR

Hi,

I’m trying to setup Keycloak behind my BunkerWeb reverse proxy (based on Nginx) but I’m facing an issue that I can’t figure out how to solve. I can correctly access the Keycloak welcome page behind my reverse proxy. Then, when I try to access the admin ui, it asks for my password. I enter it. But then it is stuck on loading the admin panel. The page refreshes indefinitely.

Here my keycloak logs : PrivateBin

I’ve tried setting the environment variable KC_PROXY to various values (edge, passthrough, reencrypt) but it didn’t fix the issue. I also made my reverse proxy pass the X-Forwarded-For header. But it doesn’t seem to fix the issue. It appears to come from Keycloak and not from BunkerWeb on my end because every other reverse proxy app I’m using with BunkerWeb works perfectly.

What could be the problem ? I can’t figure out what it is.
Thanks in advance for your help, have a great day

Same issue here.

Keycloak 19.0.2 behind nginx ingress controller. TLS is terminated@ingress

        containers:
        - command:
          - /opt/keycloak/bin/docker-entrypoint.sh
          - start
          - --http-enabled=true
          - --http-port=8080
          - --hostname-strict=false
          - --hostname-strict-https=false
          env:
          - name: KC_HTTP_RELATIVE_PATH
            value: /
          - name: KC_CACHE
            value: ispn
          - name: KC_CACHE_STACK
            value: kubernetes
          - name: KC_PROXY
            value: edge
          - name: KC_METRICS_ENABLED
            value: "true"
          - name: KC_HEALTH_ENABLED
            value: "true"
          - name: JAVA_OPTS
            value: -XX:+UseContainerSupport -XX:MaxRAMPercentage=50.0 -Djava.net.preferIPv4Stack=true
              -Djava.awt.headless=true
          - name: JAVA_OPTS_APPEND
            value: -Djgroups.dns.query=devsso-keycloakx-headless

ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: sectigo
    nginx.ingress.kubernetes.io/proxy-buffer-size: 128k
  labels:
    app.kubernetes.io/instance: devsso
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: keycloakx
    app.kubernetes.io/version: 19.0.2
    helm.sh/chart: keycloakx-1.6.0
  name: devsso-keycloakx-console
  namespace: devsso
spec:
  ingressClassName: nginx-private
  rules:
  - host: devsso-admin.example.com
    http:
      paths:
      - backend:
          service:
            name: devsso-keycloakx-http
            port:
              name: http
        path: /
        pathType: Prefix
  tls:
  - hosts:
    - devsso-admin.example.com
    secretName: devsso-admin-tls

nginx config:

    config:
      use-http2: "true"
      use-gzip: "true"
      gzip-level: "5"
      ssl-redirect: "true"
      ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"
      ssl-protocols: "TLSv1.2 TLSv1.3"
      ssl-session-tickets: "false"
      server-tokens: "false"
      hide-headers: "X-Powered-By,Server"
      server-snippet: |
        location ~ /\. {
            deny all;
            return 404;
        }
      proxy-cookie-path: "/ \"/; SameSite=None; HTTPOnly; Secure\""
      use-proxy-protocol: "true"
      annotation-value-word-blocklist: "load_module,lua_package,_by_lua,root,proxy_pass,serviceaccount,',\""
    proxySetHeaders:
      X-Frame-Options: "SAMEORIGIN"
      X-Content-Type-Options: "nosniff"
      X-XSS-Protection: "1; mode=block"

login bypassing ingress (kubectl port-forward svc/devsso-keycloakx-http -n devsso 8893:8080 --address 0.0.0.0) works

decode token error:

2022-09-20 12:34:05,626 DEBUG [org.keycloak.authentication.AuthenticationProcessor] (executor-thread-78) AUTHENTICATE CLIENT
2022-09-20 12:34:05,626 DEBUG [org.keycloak.authentication.ClientAuthenticationFlow] (executor-thread-78) client authenticator: client-secret
2022-09-20 12:34:05,627 DEBUG [org.keycloak.authentication.ClientAuthenticationFlow] (executor-thread-78) client authenticator SUCCESS: client-secret
2022-09-20 12:34:05,627 DEBUG [org.keycloak.authentication.ClientAuthenticationFlow] (executor-thread-78) Client security-admin-console authenticated by client-secret
2022-09-20 12:34:05,627 DEBUG [org.keycloak.protocol.oidc.utils.RedirectUtils] (executor-thread-78) replacing relative valid redirect with: https://devsso-admin.example.com/admin/master/console/*
2022-09-20 12:34:05,627 DEBUG [org.keycloak.protocol.oidc.utils.RedirectUtils] (executor-thread-78) replacing relative valid redirect with: https://devsso-admin.example.com/admin/master/console/*
2022-09-20 12:34:05,627 DEBUG [org.keycloak.jose.jws.DefaultTokenManager] (executor-thread-78) Failed to decode token: org.keycloak.jose.jws.JWSInputException: java.lang.IllegalArgumentException: Parsing error
	at org.keycloak.jose.jws.JWSInput.<init>(JWSInput.java:59)
	at org.keycloak.jose.jws.DefaultTokenManager.decode(DefaultTokenManager.java:90)
	at org.keycloak.protocol.oidc.TokenManager.toRefreshToken(TokenManager.java:501)
	at org.keycloak.protocol.oidc.TokenManager.verifyRefreshToken(TokenManager.java:467)
	at org.keycloak.protocol.oidc.TokenManager.refreshAccessToken(TokenManager.java:363)
	at org.keycloak.protocol.oidc.endpoints.TokenEndpoint.refreshTokenGrant(TokenEndpoint.java:507)
	at org.keycloak.protocol.oidc.endpoints.TokenEndpoint.processGrantRequest(TokenEndpoint.java:200)
	at jdk.internal.reflect.GeneratedMethodAccessor133.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)
Caused by: java.lang.IllegalArgumentException: Parsing error
	at org.keycloak.jose.jws.JWSInput.<init>(JWSInput.java:46)
	... 56 more

2022-09-20 12:34:05,627 DEBUG [org.keycloak.services.resources.Cors] (executor-thread-78) Added CORS headers to response
2022-09-20 12:34:05,628 DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (executor-thread-78) JtaTransactionWrapper  commit
2022-09-20 12:34:05,628 DEBUG [org.keycloak.transaction.JtaTransactionWrapper] (executor-thread-78) JtaTransactionWrapper end
2022-09-20 12:34:05,628 WARN  [org.keycloak.events] (executor-thread-78) type=REFRESH_TOKEN_ERROR, realmId=2238fd6f-7284-45a7-9f6e-06fafbfdf4e1, clientId=security-admin-console, userId=null, ipAddress=*************, error=invalid_token, grant_type=refresh_token, client_auth_method=client-secret

The problem was the HTTPOnly/Secure cookie flag.

More info:

If HTTPOnly/Secure flag is set globally using proxy_cookie_path (see previous answer) you need to turn it off for keycloak location and let keycloak itself to manage it.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: sectigo
    nginx.ingress.kubernetes.io/proxy-buffer-size: 128k
    nginx.ingress.kubernetes.io/proxy-cookie-path: "off"