I am hoping someone can give me a more concrete example than the one I found in the documentation.
Using SpringBoot/Spring Security 5.6.0. I am migrating the authentication process based on SpringSecurity/SAML to SAML2.
I need to add to the Authentication a UserDetails built from the responseToken information. Something like what we can read in the documentation: https://docs.spring.io/spring-security/reference/5.6.0-RC1/servlet/saml2/index.html#servlet-saml2login-opensamlauthenticationprovider-userdetailsservice
But I don't understand the third point: return a custom authentication that includes the user details: "return MySaml2Authentication(userDetails, authentication);"
In any case you would have to do "return new MySaml2Authentication(userDetails, authentication);" right?
In any case, when the process continues it is executed:
Authentication authenticate = provider.authenticate(authentication);
Which as we can see replaces the Details value with the original.
authenticationResponse.setDetails(authentication.getDetails());
/**
* @param authentication the authentication request object, must be of type
* {@link Saml2AuthenticationToken}
* @return {@link Saml2Authentication} if the assertion is valid
* @throws AuthenticationException if a validation exception occurs
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
try {
Saml2AuthenticationToken token = (Saml2AuthenticationToken) authentication;
String serializedResponse = token.getSaml2Response();
Response response = parse(serializedResponse);
process(token, response);
AbstractAuthenticationToken authenticationResponse = this.responseAuthenticationConverter
.convert(new ResponseToken(response, token));
if (authenticationResponse != null) {
authenticationResponse.setDetails(authentication.getDetails());
}
return authenticationResponse;
}
catch (Saml2AuthenticationException ex) {
throw ex;
}
catch (Exception ex) {
throw createAuthenticationException(Saml2ErrorCodes.INTERNAL_VALIDATION_ERROR, ex.getMessage(), ex);
}
}
How can you add a UserDetails that depends on the information obtained from the tokens? I can't extend OpenSaml4AuthenticationProvider because it is "final".
The only thing I can think of is the option: https://docs.spring.io/spring-security/reference/5.6.0-RC1/servlet/saml2/index.html#servlet-saml2login-authenticationmanager-custom
And in the MySaml2AuthenticationManager set the Details after executing the Authentication authenticate = provider.authenticate(authentication); but it doesn't seem right to me.
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider();
provider.setResponseAuthenticationConverter(responseToken -> {
Saml2Authentication auth = OpenSaml4AuthenticationProvider
.createDefaultResponseAuthenticationConverter()// First, call the default converter, which extracts attributes and authorities from the response
.convert(responseToken);
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
String role = getRole(auth.getName());
grantedAuthorities.add(new SimpleGrantedAuthority(role));
Saml2Authentication saml2Authentication = new Saml2Authentication((AuthenticatedPrincipal) auth.getPrincipal(), auth.getSaml2Response(), grantedAuthorities);
/*
//The details are replaced by the authentication.getDetails().
MyUserDetails userDetails = new MyUserDetails(authenticate.getName(),auth.getPrincipal());
saml2Authentication.setDetails(userDetails);
*/
return saml2Authentication;
});
Authentication authenticate = provider.authenticate(authentication);
//Doesn't sound like a good idea
if ((authenticate.getPrincipal() instanceof DefaultSaml2AuthenticatedPrincipal)) {
DefaultSaml2AuthenticatedPrincipal samlPrincipal = (DefaultSaml2AuthenticatedPrincipal) authenticate.getPrincipal();
MyUserDetails userDetails = new MyUserDetails(authenticate.getName(),
samlPrincipal.getFirstAttribute("SMFIRSTNAME")
, samlPrincipal.getFirstAttribute("SMLASTNAME")
, samlPrincipal.getFirstAttribute("SMEMAIL"));
((Saml2Authentication)authenticate).setDetails(defaultDISUserDetails);
}
return authenticate;
Any better option?
Thank you very much for your help
Best regards