I'm using Spring Security SAML 1.0.1. My application uses this XML to configure the SAMLAuthenticationProvider
bean :
<!-- SAML Authentication Provider responsible for validating of received SAML messages -->
<b:bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider">
<!-- OPTIONAL property: can be used to store/load user data after login -->
<b:property name="userDetails">
<b:bean class="eu.ueb.acem.services.auth.SamlAuthenticationUserDetailsService"/>
</b:property>
<b:property name="forcePrincipalAsString" value="false"/>
</b:bean>
The class "SamlAuthenticationUserDetailsService" implements the loadUserBySAML(SAMLCredential)
method. If the user doesn't exist in the database, it is created.
@Override
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
logger.info("entering loadUserBySAML({})", credential);
String userID = credential.getNameID().getValue();
logger.info("{} is logged in", userID);
Map<String, String> mapOfAttributesFriendlyNamesAndValues = new HashMap<String, String>();
mapOfAttributesFriendlyNamesAndValues.put("eduPersonAffiliation", null);
mapOfAttributesFriendlyNamesAndValues.put("eduPersonPrincipalName", null);
mapOfAttributesFriendlyNamesAndValues.put("eduPersonPrimaryAffiliation", null);
mapOfAttributesFriendlyNamesAndValues.put("supannEtablissement", null);
mapOfAttributesFriendlyNamesAndValues.put("supannEntiteAffectationPrincipale", null);
mapOfAttributesFriendlyNamesAndValues.put("supannOrganisme", null);
mapOfAttributesFriendlyNamesAndValues.put("displayName", null);
mapOfAttributesFriendlyNamesAndValues.put("mail", null);
mapOfAttributesFriendlyNamesAndValues.put("givenName", null);
mapOfAttributesFriendlyNamesAndValues.put("sn", null);
mapOfAttributesFriendlyNamesAndValues.put("uid", null);
for (Attribute attribute : credential.getAttributes()) {
logger.info("attribute friendly name={}", attribute.getFriendlyName());
if (mapOfAttributesFriendlyNamesAndValues.containsKey(attribute.getFriendlyName())) {
// We set the values of the property
for (XMLObject attributeValueXMLObject : credential.getAttribute(attribute.getName()).getAttributeValues()) {
logger.info("We care about this attribute, getAttributeValue={}", getAttributeValue(attributeValueXMLObject));
mapOfAttributesFriendlyNamesAndValues.put(attribute.getFriendlyName(), getAttributeValue(attributeValueXMLObject));
}
}
else {
logger.info("We don't care about this attribute");
}
}
Person user = usersService.getUser(mapOfAttributesFriendlyNamesAndValues.get("eduPersonPrincipalName"));
user.setLogin(mapOfAttributesFriendlyNamesAndValues.get("eduPersonPrincipalName"));
user.setEmail(mapOfAttributesFriendlyNamesAndValues.get("mail"));
user.setName(mapOfAttributesFriendlyNamesAndValues.get("displayName"));
user.setAdministrator(true);
user = usersService.updatePerson(user);
logger.info("leaving loadUserBySAML");
return loadUserByUser(user);
}
private UserDetails loadUserByUser(Person targetUser) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
// Roles
if (targetUser.isAdministrator()) {
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
}
else {
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
}
return new User(targetUser.getLogin(), targetUser.getPassword(),
true, // enabled
true, // account not expired
true, // credentials not expired
true, // account not locked
authorities);
}
I have the following behaviour :
loadUserBySAML
method is calledcredential.getNameID().getValue()
(e.g. "_246558c0d7c514447292d750df577b6b").Question : how can I set the "NameID" attribute of the credential to be the e-mail address?
I have read multiple times the section "9.4 Authentication object" of the documentation but I still don't understand how to tell Spring Security that my UserDetails
object should be referenced with the email address and not with the NameID value.
I think that the problem might be that SAMLAuthenticationProvider
by default always uses NameID
, even when UserDetails
gets returned from the SAMLUserDetailsService
. It's done so for backwards compatibility with previous versions, although I'm considering changing it to avoid confusion.
In order to use values returned from your UserDetails
set property forcePrincipalAsString
to false on bean SAMLAuthenticationProvider
.