Skip to content

Make name resolution configurable in OpenSamlLogoutRequestValidator #12128

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
palakova opened this issue Nov 2, 2022 · 4 comments
Open

Make name resolution configurable in OpenSamlLogoutRequestValidator #12128

palakova opened this issue Nov 2, 2022 · 4 comments
Assignees
Labels
in: saml2 An issue in SAML2 modules type: enhancement A general enhancement
Milestone

Comments

@palakova
Copy link
Contributor

palakova commented Nov 2, 2022

Describe the bug
Spring SAML considers NameID to hold username, populates Saml2AuthenticatedPrincipal#name with NameID value and later in Single Logout flow again populates/validates NameID value using Principal Name. This behaviour breaks our current integrations.

Username can be released by IdP in one of Assertion’s Attribute element instead. NameID, if present (as it is even optional, as also discussed in #11463), can be of different Format, holding different kind of values.

One example for all: According to SAML spec, NameID Format urn:oasis:names:tc:SAML:2.0:nameid-format:transient indicates that the content of the element is an identifier with transient semantics and SHOULD be treated as an opaque and temporary value by the relying party. This NameID element is also sent by IdP in LogoutRequest (causing validation against Principal Name in OpenSamlLogoutRequestValidator#validateNameId to fail) and is expected to be present in LogoutRequest sent from relying party (where it is populated with Principal Name in OpenSamlLogoutRequestResolver, making the IdP refuse the request).

Our workarounds:

  1. Login: Custom responseAuthenticationConverter to retrieve username from Attribute + storing NameID element for later use.
  2. Outbound LogoutRequest: Overriding NameID element with correct one stored during login.
  3. Inbound LogoutRequest: There is no easy or clean way to work around this, because OpenSamlLogoutRequestValidator is not much configurable.

To Reproduce
Reproduced when integrating with Shibboleth IdP, which uses transient NameID and an Attribute to release username.

Expected behavior
Whole NameID element could be stored in DefaultSaml2AuthenticatedPrincipal (similar as session indexes are stored already) during login. It would be used to construct NameID element in outbound LogoutRequest, and it’s value would be used for validation when handling inbound LogoutRequest.

Ideally could you consider also to configure where to retrieve the username from in the first place (NameID element versus providing a Name of an Assertion Attribute)?

@palakova palakova added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Nov 2, 2022
@jzheaux
Copy link
Contributor

jzheaux commented Nov 2, 2022

Thanks for the report, @palakova.

It sounds like there are two things happening here, and I want to see if we can separate them out.

First, you are saying that it would be nice to have the entire NameID available, at least so that the construction and validation of LogoutRequests can be customized, which I agree with worth looking at. Since transient is meant to be treated as opaque, I'm not sure that the framework can do better than that.

Second, you are asking for a way to provide a name-resolution strategy that is simpler than an entire converter.

If so, would you mind placing the second one on a separate ticket? Or if I haven't understood, would you help clarify your request?

@jzheaux jzheaux added type: enhancement A general enhancement in: saml2 An issue in SAML2 modules and removed status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Nov 2, 2022
@jzheaux jzheaux added this to the 6.1.0-M1 milestone Nov 2, 2022
@palakova
Copy link
Contributor Author

palakova commented Nov 3, 2022

Thank you for looking into this @jzheaux . I created a #12136 to separate enhancement for name-resolution strategy.

I agree, I don't think there is more to do with a NameID that does not hold username, other than using it in construction/validation of LogoutRequests.

@jzheaux jzheaux changed the title SAML NameID value should not be handled as Principal Name Make name resolution configurable in OpenSamlLogoutRequestValidator Jan 10, 2023
@jzheaux jzheaux self-assigned this Jan 10, 2023
@marcusdacoregio marcusdacoregio modified the milestones: 6.1.0-M1, 6.1.0-M2 Jan 16, 2023
@junytse
Copy link
Contributor

junytse commented Mar 7, 2023

Hi @jzheaux I also meet similar issues as @palakova when I was previously integrating SAML provider. It's great that I found this discussion.

Both former Spring SAML extension and Shibboleth SAML SP implementation will attempt to save NameID in the current session and later use it for logout. I workaround by subclassing DefaultSaml2AuthenticatedPrincipal to add a nameId field, store it on a ResponseAuthenticationConverter, and set it on a LogoutRequestResolverParametersConsumer:

public class MySaml2AuthenticatedPrincipal extends DefaultSaml2AuthenticatedPrincipal {
    public NameID getNameID() {
        return nameID;
    }

    private final NameID nameID; // the app support only NameID

    // in the early version, sessionIndexes are also saved here

    public MySaml2AuthenticatedPrincipal(NameID nameID, Map<String, List<Object>> attributes, List<String> sessionIndexes) {
        super(nameID.getValue(), attributes, sessionIndexes);
        this.nameID = nameID;
    }

}
public class MySaml2Util {
    public static Converter<OpenSaml4AuthenticationProvider.ResponseToken, Saml2Authentication> createMyResponseAuthenticationConverter() {
        return (responseToken) -> {
            Response response = responseToken.getResponse();
            Saml2AuthenticationToken token = responseToken.getToken();
            Assertion assertion = CollectionUtils.firstElement(response.getAssertions());
            RelyingPartyRegistration relyingPartyRegistration = token.getRelyingPartyRegistration();

            // add here
            NameID nameId = assertion.getSubject().getNameID(); // be aware of NPE
            Map<String, List<Object>> attributes = getAssertionAttributes(assertion); // copied from OpenSaml4AuthenticationProvider
            List<String> sessionIndexes = getSessionIndexes(assertion); // copied from OpenSaml4AuthenticationProvider

            DefaultSaml2AuthenticatedPrincipal principal = new MySaml2AuthenticatedPrincipal(nameId, attributes, sessionIndexes);
            principal.setRelyingPartyRegistrationId(relyingPartyRegistration.getRegistrationId());

            // customization codes
            // ...

            return new Saml2Authentication(principal, token.getSaml2Response(),
                    AuthorityUtils.createAuthorityList("ROLE_USER"));
        };
    }

    public static Consumer<LogoutRequestParameters> createMyLogoutRequestResolverParametersConsumer() {
        return (parameters) -> {
            //...
            LogoutRequest logoutRequest = parameters.getLogoutRequest();
            MySaml2AuthenticatedPrincipal principal = (MySaml2AuthenticatedPrincipal)parameters.getAuthentication().getPrincipal(); // be aware of exception
            NameID nameID = logoutRequest.getNameID();
            NameID principalNameID = principal.getNameID();
            //...
            // be aware of NPE...
            nameID.setFormat(principalNameID.getFormat());
            nameID.setFormat(principalNameID.getFormat());
            nameID.setNameQualifier(principalNameID.getNameQualifier());
            nameID.setSPNameQualifier(principalNameID.getSPNameQualifier());
            nameID.setSPProvidedID(principalNameID.getSPProvidedID());
            nameID.setValue(principalNameID.getValue());

            // in the early version, sessionIndexes are also handled here
        };
    }
}

My thoughts:
I think improvements could be made about standard SAML implementations:

  • It'd be good if Saml2AuthenticatedPrincipal holds NameID;
  • Since I overwritten responseAuthenticationConverter and customize the code about Saml2AuthenticatedPrincipal generation, I have to copy functions about attributes/sessionIndexes extration from OpenSaml4AuthenticationProvider; it'd be good if the population of Saml2AuthenticatedPrincipal, which holds SAML only contents, could be extracted to a dedicated method;

@jzheaux jzheaux removed this from the 6.1.0-M2 milestone Mar 20, 2023
@Extersky
Copy link

I came across this as well with inbound single logout requests when using a different principal than the incoming NameID value. Customizing logout request resolver parameter consumer allows changing the NameID fine. With inbound logout requests, Saml2LogoutConfigurer allows post processing Saml2LogoutRequestFilter but since it doesn't have a method for replacing/customizing the Saml2LogoutRequestValidatorParametersResolver, then a workaround would involve duplicating methods from Saml2LogoutConfigurer which would then allow replacing the Saml2LogoutRequestValidatorParametersResolver instance. As mentioned, at this moment this fails in BaseOpenSamlLogoutRequestValidator validateNameId method.

This would be better if Saml2LogoutRequestFilter had a method to just replace Saml2LogoutRequestValidatorParametersResolver (while using postProcess for Saml2LogoutRequestFilter), but there's also this more broad way for resolving the NameID that is being discussed here.

The #12136 issue focuses more on authentication responses, while this one has focused more on the inbound logout requests but I would like a better way to resolve the NameID here like with the logout request resolvers.

@jzheaux jzheaux added this to the 6.5.x milestone Nov 5, 2024
@jzheaux jzheaux modified the milestones: 6.5.x, 7.0.x Apr 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: saml2 An issue in SAML2 modules type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

5 participants