X.509 smartcard authentication, ask for certificate only on authentication?

I have been setting up X.509 ID card authentication with Keycloak 22.0.5, and finally got it working on my setup (Kubernetes, nginx ingress controller with TLS termination, Open Service Mesh on mTLS mode between nginx and Keycloak, Keycloak in ‘edge’ proxy mode).

I was wondering if it would be possible to only require mTLS when actually logging in through X.509? Now the browser is always asking for certificate selection no matter what I’m trying to access. For example, if I have realms A and B, where only B has X.509 active, accessing realm A also asks for the certificate. This is because these nginx ingress annotations are ingress-wide:

    nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
    nginx.ingress.kubernetes.io/auth-tls-secret: demo/keycloak-ca-secret
    nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"

What I’m looking for is something similar to our national authentication portal, where ID chip card authentication step has a button for activating the mTLS-based authentication. Only when you press the ‘Identify yourself’ button, the browser asks you to select the ID card certificate.

Just short answer here:
In the x509 authentication scenario with nginx, one possible case is the following:

user -(mTLS) → nginx --(TLS + HTTP header with user certificate) → Keycloak

Therefore, nginx is triggering the certificate selection due to the configuration of ssl_verify_client on. Meaning that in this case, the “problem” is related to nginx because it does not support ssl_verify_client on in a specific location. One possible workaround is to expose the platform on different subdomains, and on one of them, nginx can be configured to force user certificate selection.

You can also experiment with ssl_verify_client optional and then, in a specific location such as /realms/govID , check if the user has selected a user certificate with a conditional statement like if ($ssl_client_verify != SUCCESS) { magic here } . However, the challenge lies in prompting the user again for certificate selection once it’s already been made.

Thank you. I actually faced yet another challenge, which is that Azure Application Gateway (which we are using in front of Keycloak ingress running in Kubernetes) does not support optional mTLS. So you either have mTLS, or you don’t. And it’s a site-wide thing, affecting all the realms.

Actually, it’s the mTLS handshake on the HTTP protocol which does not support different paths. So mTLS is always site-wide, regardless of which HTTP server you are using.

The only workaround I can think of for Azure Application Gateway is to set up a second Keycloak instance with AppGW listener for the mTLS-enabled site. So let’s say you have login.company.com without mTLS and login2.company.com with mTLS. You could then set up login2.company.com to be an OIDC IdP for login.company.com, and use kc_idp_hint on the application side to tell you want mTLS login.

Additionally you need to be able to set up mTLS so that Keycloaks on login.company.com and login2.company.com would trust each other. The other alternative is to play with AppGW’s public and internal IP addresses, listeners and Keycloak POD HostAliases, so that you’d have a second AppGW listener with internal IP address, without mTLS requirement.