4

I'm working with spring-security and spring-security-saml2-service-provider with versions 5.2.0.RELEASE. I'm trying after authentication by IDP to obtain the current Assertion in order to map it to a user in our local system. I use this code to obtain the Saml2Authentication object

@Component
@Log4j
public class EventListener implements ApplicationListener<InteractiveAuthenticationSuccessEvent> {
    @Override
    public void onApplicationEvent(InteractiveAuthenticationSuccessEvent interactiveAuthenticationSuccessEvent) {
        Object principal = interactiveAuthenticationSuccessEvent.getAuthentication().getPrincipal();
        Saml2Authentication authentication = (Saml2Authentication)interactiveAuthenticationSuccessEvent.getAuthentication()

But I cannot get the user. I can get the saml2 response (authentication.getSaml2Response) but I'm not sure how to obtain the assertion with the id of the user.

Really, I would like to do Retrieve Attributes and NameID from a SAML Response (XML) in the Java Code. Not sure if there is something in spring-security that can help me.

Update So after a bit of work, I get the attributes using the OpenSAML library and parsing the SAMLv2 response. I don't know if this is the correct way to do it

@Override
    public void onApplicationEvent(InteractiveAuthenticationSuccessEvent interactiveAuthenticationSuccessEvent) {
        Saml2Authentication authentication = (Saml2Authentication) interactiveAuthenticationSuccessEvent.getAuthentication();
        try {

            Document messageDoc;
            BasicParserPool basicParserPool = new BasicParserPool();
            basicParserPool.initialize();
            InputStream inputStream = new ByteArrayInputStream(authentication.getSaml2Response().getBytes());
            messageDoc = basicParserPool.parse(inputStream);
            Element messageElem = messageDoc.getDocumentElement();
            Unmarshaller unmarshaller = XMLObjectProviderRegistrySupport.getUnmarshallerFactory().getUnmarshaller(messageElem);
            XMLObject samlObject = unmarshaller.unmarshall(messageElem);
            Response response = (Response) samlObject;
            response.getAssertions().forEach(assertion -> {
                assertion.getAttributeStatements().forEach(attributeStatement ->
                {
                    attributeStatement.getAttributes().forEach(attribute -> {
                        log.error("Names:" + attribute.getName() + getAttributesList(attribute.getAttributeValues()));
                    });
                });
            });
        } catch (Exception e) {
            log.error(e);
        }
    }

    private List<String> getAttributesList(Collection<XMLObject> collection) {
        return collection.stream().map(this::getAttributeValue)
                .collect(Collectors.toList());
    }

    private String getAttributeValue(XMLObject attributeValue) {
        return attributeValue == null ?
                null :
                attributeValue instanceof XSString ?
                        getStringAttributeValue((XSString) attributeValue) :
                        attributeValue instanceof XSAnyImpl ?
                                getAnyAttributeValue((XSAnyImpl) attributeValue) :
                                attributeValue.toString();
    }

    private String getStringAttributeValue(XSString attributeValue) {
        return attributeValue.getValue();
    }

    private String getAnyAttributeValue(XSAnyImpl attributeValue) {
        return attributeValue.getTextContent();
    }
drizzt.dourden
  • 683
  • 8
  • 20
  • What does 'user' mean to you? A SAML Assertion can include a Subject (which has a NameID format) and it can include SAML Attributes in the AttributeStatement. If 'transient NameID format' is being used, the subject would be a unique string. Please refine your question. – Bernhard Thalmayr Oct 16 '19 at 07:12
  • I'll like to get some attribute in order to identify the user that has logged. For example, I can get the NameID in the response, but I change with each login (I'm using simplesamlphp as IdP),. I would like to get some attribute that it's unique per user so I can identified it in the system. Thanks for you response! – drizzt.dourden Oct 16 '19 at 09:16
  • @drizzt.dourden once your IDP authenticate the request, it will send you some attributes in response based on the contract. Didn't you get those attributes? – K.D Oct 16 '19 at 11:25
  • Yes, I get the attributes. The problem is that I don't find any method that gives me all the attributes in the saml-security. So at the end, I get the attributes parsing the XML response. – drizzt.dourden Oct 16 '19 at 13:33
  • @drizzt.dourden https://stackoverflow.com/a/62456724/10479742 – kostic017 Jun 19 '20 at 06:52

1 Answers1

2

By default Spring SAML Security is using the value of the NameID in the Subject element to set the 'username'. If 'transient NameID format', the value has to be a different/unique one for every SSO flow per SAML spec.

You can implement your own UserDetailsService which implements org.springframework.security.saml.userdetails.SAMLUserDetailsService in method loadUserBySAML you can extract the SAML attributes

@Override
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
  final List<Attribute> attributesFromAttributeStatement = credential.getAttributes();

  final String userName = getUserNameValueFromAttribute(attributesFromAttributeStatement);

  // if needed calculate authorities
  final List<GrantedAuthority> grantedAuthorities = getGrantedAuthorities();
  final User authenticatedUser = new User(userName, "", grantedAuthorities);
  return authenticatedUser;
}
Bernhard Thalmayr
  • 2,674
  • 1
  • 11
  • 7
  • Thanks for post; after, in my app, how can i retrieve the User generate in loadUserBySAML method (in this case User authenticatedUser)? I think with Spring (and Spring Boot) it possibile use an annotation... – Giampiero Poggi Feb 10 '21 at 12:28