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

Chris Smith chris.smith at cmfirstgroup.com
Fri Sep 6 14:28:53 EDT 2019


I see that KeycloakContext has been made available in the LDAP and Kerberos storage providers in 7.0.0

Is there any reason for me to not use them? 

-----Original Message-----
From: Marek Posolda <mposolda at redhat.com> 
Sent: Monday, June 24, 2019 8:00 AM
To: Chris Smith <chris.smith at cmfirstgroup.com>; keycloak-dev at lists.jboss.org
Subject: Re: Putting a value into a custom Protocol mapper

ATM There is no nice way of doing this as method "validPassword" on UserStorageProvider is limited to return just boolean. No any additional state can be returned from the credential validation ATM.

One slightly better way than thread-local could be to use the KeycloakSession attributes. KeycloakSession is simply per-request object, so you can use methods like getAttribute/setAttribute.

For some other use-case, we recently discuss to add AuthenticationSessionModel as an additional thing to the KeycloakContext. That would allow to retrieve current AuthenticationSession inside the LDAPStorageProvider. However that is not available yet and there may be some issues with the future portability of this solution as LDAPStorageProvider (or any other
UserStorageProvider) shouldn't ideally have direct access to stuff like AuthenticationSessionModel... So not convinced about this solution, just mentioning for the completeness ;)

Marek

On 19/06/2019 18:12, Chris Smith wrote:
> 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