[keycloak-dev] Putting a value into a custom Protocol mapper

Chris Smith chris.smith at cmfirstgroup.com
Wed Jun 19 12:12:22 EDT 2019


OK, I have an ugly hack.  I hope someone might have a suggestion more aligned with Keycloak architecture
My hack is to use a ThreadLocal to hold the serialized Kerberos ticket in the LDAPStorageProvider

    public static final String KERBEROS_NOT_SET = "*NOT_SET";
    public static final ThreadLocal<String> SERILIZED_KERBEROS_TICKET = new ThreadLocal<String>() {
    	@Override
    	protected String initialValue() {
    		return KERBEROS_NOT_SET;
    	}
    };
    
    public boolean validPassword(RealmModel realm, UserModel user, String password) {
        if (kerberosConfig.isAllowKerberosAuthentication() && kerberosConfig.isUseKerberosForPasswordAuthentication()) {
            // Use Kerberos JAAS (Krb5LoginModule)
            KerberosUsernamePasswordAuthenticator authenticator = factory.createKerberosUsernamePasswordAuthenticator(kerberosConfig);
            boolean isValid = authenticator.validUser(user.getUsername(), password);
            if (isValid) {
                SERILIZED_KERBEROS_TICKET.set(authenticator.getSerializedKerberosTicket());
			}
            return isValid;
        } else {
            // Use Naming LDAP API
...

And in the UsernamePasswordForm 
   1:  override the validate password method
   2:  write the serialized ticket as a user session note to the AuthenticationFlowContext.

	@Override
	public boolean validatePassword(AuthenticationFlowContext context, UserModel user,
			MultivaluedMap<String, String> inputData) {
		boolean isValid = super.validatePassword(context, user, inputData);
		if (isValid) {
			saveKerbTicketClaim(context);
		}
		return isValid;
	}
    
	private void saveKerbTicketClaim(AuthenticationFlowContext context) {
		String kerbTicket = LDAPStorageProvider.SERILIZED_KERBEROS_TICKET.get();
		if (!kerbTicket.equals(LDAPStorageProvider.KERBEROS_NOT_SET)) {
			context.getAuthenticationSession()
				.setUserSessionNote(KerberosConstants.GSS_DELEGATION_CREDENTIAL, kerbTicket);
		}
		LDAPStorageProvider.SERILIZED_KERBEROS_TICKET.remove();
		return;
	}

This is bad, but it works for now until someone can point out to me what I missed.


-----Original Message-----
From: keycloak-dev-bounces at lists.jboss.org <keycloak-dev-bounces at lists.jboss.org> On Behalf Of Chris Smith
Sent: Monday, June 17, 2019 5:38 PM
To: keycloak-dev at lists.jboss.org; Marek Posolda <mposolda at redhat.com>
Subject: Re: [keycloak-dev] Putting a value into a custom Protocol mapper

correction

From: Chris Smith
Sent: Monday, June 17, 2019 5:15 PM
To: keycloak-dev at lists.jboss.org; Marek Posolda <mposolda at redhat.com>
Subject: RE: Putting a value into a custom Protocol mapper

I really need some help here
I can not find out how to add a Claim after a successful Kerberos User/Password login

This is as far as I know what is going on.
This is in the LDAPStorageProvider class.  The KerberosUsernamePasswordAuthenticator  has been updated to hold the serialized Kerberos ticket.  I now need to put this somewhere/somehow so that my custom Protocol mapper will put it into the Access Token

    public boolean validPassword(RealmModel realm, UserModel user, String password) {
        if (kerberosConfig.isAllowKerberosAuthentication() && kerberosConfig.isUseKerberosForPasswordAuthentication()) {
            // Use Kerberos JAAS (Krb5LoginModule)
           Map<String, String> state = new HashMap<>();
            KerberosUsernamePasswordAuthenticator authenticator = factory.createKerberosUsernamePasswordAuthenticator(kerberosConfig);
            boolean isValidUser = authenticator.validUser(user.getUsername(), password);
            if (isValidUser) {
               String delegationCredential = authenticator.getSerializedKerberosTicket();
                if (delegationCredential != null) {
                    state.put("odic-fms-sso-kerberos-ticket-mapper", delegationCredential);
                }


                << WHAT GOES HERE?? >>

            return isValidUser;

This is from my servlet

                  KeycloakPrincipal<KeycloakSecurityContext> kcp = (KeycloakPrincipal<KeycloakSecurityContext>)request.getUserPrincipal();
                  AccessToken at = kcp.getKeycloakSecurityContext().getToken();
                Map<String, Object> otherClaims = at.getOtherClaims();
                String otherClaim = (String)otherClaims.get("odic-fms-sso-kerberos-ticket-mapper");
                GSSCredential gssCredential = KerberosSerializationUtils.deserializeCredential(otherClaim);

From: Chris Smith
Sent: Friday, June 14, 2019 4:24 PM
To: keycloak-dev at lists.jboss.org<mailto:keycloak-dev at lists.jboss.org>
Subject: Putting a value into a custom Protocol mapper

I'm trying to have a custom protocol mapper provide a serialized Kerberos ticket as a claim

I have updated the KerberosUsernamePasswordAuthenticator so that it gets the ticket

    public Subject authenticateSubject(String username, String password) throws LoginException {
        String principal = getKerberosPrincipal(username);

        logger.debug("Validating password of principal: " + principal);
        loginContext = new LoginContext("does-not-matter", null,
                createJaasCallbackHandler(principal, password),
                createJaasConfiguration());

        loginContext.login();
        serializedKerberosTicket = serializeTicket();
        logger.debug("Principal " + principal + " authenticated succesfully");
        return loginContext.getSubject();
    }

    private String serializeTicket() {
        KerberosTicket kerberosTicket = loginContext.getSubject()
                .getPrivateCredentials(KerberosTicket.class)
                .stream().findFirst().get();
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
           ObjectOutputStream oos = new ObjectOutputStream(bos)){
                oos.writeObject(kerberosTicket);
                return Base64.getEncoder().encodeToString(bos.toByteArray());
           } catch (IOException e) {
                logger.error("Kerberos ticket serialization failed", e);
                return null;
           }
    }

I reviewed the SPNEGOAuthenticator and traced it's execution to see how it adds the Kerberos ticket and I do not see that as a workable approach as it is so different from the Kerberos User/Password authenticator.

Where can my custom KerberosUsernamePasswordAuthenticator put the serialized ticket so that my custom protocol mapper will get it and add it as a claim on my Access token?

I have looked and googled with no luck.
_______________________________________________
keycloak-dev mailing list
keycloak-dev at lists.jboss.org
https://lists.jboss.org/mailman/listinfo/keycloak-dev



More information about the keycloak-dev mailing list