I am attempting to implement an Authenticator interface with the purpose of having a user with an x509 certificate card to have a user created in Keycloak and then just allowing the user to login since we trust the username and other information given from the certificate.

We are set up in Keycloak like so:

enter image description here

enter image description here

We are using the following to gather this information from the cert (which btw, does well when we use it get the cert information for a form if we use normal registration):

package com.mycompany.pki.pkihelper;

...

import org.jboss.resteasy.spi.HttpRequest;
...
import org.jboss.logging.Logger;

public class CertHelper {
    private static final Logger logger = Logger.getLogger(CertHelper.class);

    private static String removeBeginEndPem(String pem) {
        pem = pem.replace("-----BEGIN CERTIFICATE-----", "");
        pem = pem.replace("-----END CERTIFICATE-----", "");
        pem = pem.replace("\r\n", "");
        pem = pem.replace("\n", "");
        return pem.trim();
    }

    ...

    public static String getCertificateUserName(HttpRequest httpRequest) {
        
        String firstName = getCertificateFirstName(httpRequest);
        String lastName = getCertificateLastName(httpRequest);
        String userName = firstName+ "." + lastName;

        if(firstName == ""){
            return "";
        }else{
            return userName;
        }
        
    }

    ...
    
}

and then we are using this authenticator class:

package com.mycomponay.authentication;

import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.authenticators.browser.UsernamePasswordForm;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
import com.mycompoany.pki.pkihelper.CertHelper;


public class InternalAuthenticator implements Authenticator {

    @Override
    public void authenticate(AuthenticationFlowContext context) {
        String username = context.getHttpRequest().getDecodedFormParameters().getFirst(UsernamePasswordForm.ATTEMPTED_USERNAME);
        RealmModel realm = context.getRealm();

        UserProvider userProvider = context.getSession().userStorageManager();
        UserModel userModel = userProvider.getUserByUsername(username, realm);

        if (userModel == null) {
            // If the user doesn't exist, create a new user
            username = CertHelper.getCertificateUserName(context.getHttpRequest());
            userModel = createUser(context, realm, username);
            userModel.setFirstName(CertHelper.getCertificateFirstName(context.getHttpRequest()));
            userModel.setLastName(CertHelper.getCertificateLastName(context.getHttpRequest()));
            userModel.setEmail("[email protected]");
            userModel.setEnabled(true);
        }

        context.setUser(userModel);
        context.success();
    }

    private UserModel createUser(AuthenticationFlowContext context, RealmModel realm, String username) {
        KeycloakSession session = context.getSession();
        UserProvider userProvider = session.userStorageManager();
    
        UserModel newUser = userProvider.addUser(realm, username);
        newUser.setSingleAttribute("username", username);
        session.getTransactionManager().commit();
    
        return newUser;
    }
    

    @Override
    ... // other overridden methods

}

When we do this, we seem to get an error message:

20:48:32,938 WARN  [org.keycloak.services] (default task-3) KC-SERVICES0013: Failed authentication: org.keycloak.authentication.AuthenticationFlowException: Not found serialized context in clientSession
    at [email protected]//org.keycloak.authentication.authenticators.broker.AbstractIdpAuthenticator.authenticate(AbstractIdpAuthenticator.java:68)
    at [email protected]//org.keycloak.authentication.DefaultAuthenticationFlow.processSingleFlowExecutionModel(DefaultAuthenticationFlow.java:446)
    at [email protected]//org.keycloak.authentication.DefaultAuthenticationFlow.processFlow(DefaultAuthenticationFlow.java:250)
    at [email protected]//org.keycloak.authentication.AuthenticationProcessor.authenticateOnly(AuthenticationProcessor.java:1017)
    at [email protected]//org.keycloak.authentication.AuthenticationProcessor.authenticate(AuthenticationProcessor.java:879)
    at [email protected]//org.keycloak.protocol.AuthorizationEndpointBase.handleBrowserAuthenticationRequest(AuthorizationEndpointBase.java:151)
    at [email protected]//org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint.buildAuthorizationCodeAuthorizationResponse(AuthorizationEndpoint.java:338)
    at [email protected]//org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint.process(AuthorizationEndpoint.java:194)
    at [email protected]//org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint.buildGet(AuthorizationEndpoint.java:112)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

My question I think is how can we get a proper session so that we can make use of our CertHelper class from within an Authenticator interface?

0

There are 0 best solutions below