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(a)redhat.com>
Sent: Monday, June 24, 2019 8:00 AM
To: Chris Smith <chris.smith(a)cmfirstgroup.com>; keycloak-dev(a)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(a)lists.jboss.org
<keycloak-dev-bounces(a)lists.jboss.org> On Behalf Of Chris Smith
Sent: Monday, June 17, 2019 5:38 PM
To: keycloak-dev(a)lists.jboss.org; Marek Posolda <mposolda(a)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(a)lists.jboss.org; Marek Posolda <mposolda(a)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@lists.jboss.org<mailto:keycloak-dev@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(a)lists.jboss.org
https://lists.jboss.org/mailman/listinfo/keycloak-dev