Rework Saml2 Authentication Statement
This commit separates the authentication principal, the assertion details, and the relying party tenant into separate components. This allows the principal to be completely decoupled from how Spring Security triggers and processes SLO. Specifically, it adds Saml2AssertionAuthentication, a new authentication implementation that allows an Object principal and a Saml2ResponseAssertionAccessor credential. It also moves the relying party registration id from Saml2AuthenticatedPrincipal to Saml2AssertionAuthentication. As such, Saml2AuthenticatedPrincipal is now deprecated in favor of placing its assertion components in Saml2ResponseAssertionAccessor and the relying party registration id in Saml2AssertionAuthentication. Closes gh-10820
This commit is contained in:
parent
759c88998a
commit
c160801e2d
|
@ -33,7 +33,9 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt
|
|||
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutRequestValidator;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutResponseValidator;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutRequestValidator;
|
||||
|
@ -531,7 +533,16 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
|
|||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
|
||||
return Saml2AuthenticationInfo.fromAuthentication(authentication) != null;
|
||||
if (authentication == null) {
|
||||
return false;
|
||||
}
|
||||
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {
|
||||
return true;
|
||||
}
|
||||
if (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor) {
|
||||
return true;
|
||||
}
|
||||
return authentication instanceof Saml2Authentication;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,7 +31,9 @@ import org.springframework.beans.factory.xml.ParserContext;
|
|||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;
|
||||
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;
|
||||
|
@ -236,7 +238,16 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
|
|||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
|
||||
return Saml2AuthenticationInfo.fromAuthentication(authentication) != null;
|
||||
if (authentication == null) {
|
||||
return false;
|
||||
}
|
||||
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal) {
|
||||
return true;
|
||||
}
|
||||
if (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor) {
|
||||
return true;
|
||||
}
|
||||
return authentication instanceof Saml2Authentication;
|
||||
}
|
||||
|
||||
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
||||
|
|
|
@ -170,11 +170,14 @@ import org.springframework.security.saml2.core.Saml2Error;
|
|||
import org.springframework.security.saml2.core.Saml2X509Credential;
|
||||
import org.springframework.security.saml2.credentials.TestSaml2X509Credentials;
|
||||
import org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertion;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;
|
||||
import org.springframework.security.saml2.provider.service.authentication.TestSaml2AuthenticationTokens;
|
||||
import org.springframework.security.saml2.provider.service.authentication.TestSaml2Authentications;
|
||||
import org.springframework.security.saml2.provider.service.authentication.TestSaml2LogoutRequests;
|
||||
|
@ -520,8 +523,16 @@ final class SerializationSamples {
|
|||
generatorByClassName.put(Saml2Exception.class, (r) -> new Saml2Exception("message", new IOException("fail")));
|
||||
generatorByClassName.put(DefaultSaml2AuthenticatedPrincipal.class,
|
||||
(r) -> TestSaml2Authentications.authentication().getPrincipal());
|
||||
generatorByClassName.put(Saml2Authentication.class,
|
||||
(r) -> applyDetails(TestSaml2Authentications.authentication()));
|
||||
Saml2Authentication saml2 = TestSaml2Authentications.authentication();
|
||||
generatorByClassName.put(Saml2Authentication.class, (r) -> applyDetails(saml2));
|
||||
Saml2ResponseAssertionAccessor assertion = Saml2ResponseAssertion.withResponseValue("response")
|
||||
.nameId("name")
|
||||
.sessionIndexes(List.of("id"))
|
||||
.attributes(Map.of("key", List.of("value")))
|
||||
.build();
|
||||
generatorByClassName.put(Saml2ResponseAssertion.class, (r) -> assertion);
|
||||
generatorByClassName.put(Saml2AssertionAuthentication.class, (r) -> applyDetails(
|
||||
new Saml2AssertionAuthentication(assertion, authentication.getAuthorities(), "id")));
|
||||
generatorByClassName.put(Saml2PostAuthenticationRequest.class,
|
||||
(r) -> TestSaml2PostAuthenticationRequests.create());
|
||||
generatorByClassName.put(Saml2RedirectAuthenticationRequest.class,
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -54,3 +54,57 @@ fun logoutResponseResolver(registrations: RelyingPartyRegistrationRepository?):
|
|||
----
|
||||
======
|
||||
|
||||
== Favor `Saml2ResponseAuthenticationAccessor` over `Saml2AuthenticatedPrincipal`
|
||||
|
||||
Spring Security 7 separates `<saml2:Assertion>` details from the principal.
|
||||
This allows Spring Security to retrieve needed assertion details to perform Single Logout.
|
||||
|
||||
This deprecates `Saml2AuthenticatedPrincipal`.
|
||||
You no longer need to implement it to use `Saml2Authentication`.
|
||||
|
||||
Instead, the credential implements `Saml2ResponseAssertionAccessor`, which Spring Security 7 favors when determining the appropriate action based on the authentication.
|
||||
|
||||
This change is made automatically for you when using the defaults.
|
||||
|
||||
If this causes you trouble when upgrading, you can publish a custom `ResponseAuhenticationConverter` to return a `Saml2Authentication` instead of returning a `Saml2AssertionAuthentication` like so:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
OpenSaml5AuthenticationProvider authenticationProvider() {
|
||||
OpenSaml5AuthenticationProvider authenticationProvider =
|
||||
new OpenSaml5AuthenticationProvider();
|
||||
ResponseAuthenticationConverter defaults = new ResponseAuthenticationConverter();
|
||||
authenticationProvider.setResponseAuthenticationConverter(
|
||||
defaults.andThen((authentication) -> new Saml2Authentication(
|
||||
authentication.getPrincipal(),
|
||||
authentication.getSaml2Response(),
|
||||
authentication.getAuthorities())));
|
||||
return authenticationProvider;
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
fun authenticationProvider(): OpenSaml5AuthenticationProvider {
|
||||
val authenticationProvider = OpenSaml5AuthenticationProvider()
|
||||
val defaults = ResponseAuthenticationConverter()
|
||||
authenticationProvider.setResponseAuthenticationConverter(
|
||||
defaults.andThen { authentication ->
|
||||
Saml2Authentication(authentication.getPrincipal(),
|
||||
authentication.getSaml2Response(),
|
||||
authentication.getAuthorities())
|
||||
})
|
||||
return authenticationProvider
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
If you are constructing a `Saml2Authentication` instance yourself, consider changing to `Saml2AssertionAuthentication` to get the same benefit as the current default.
|
||||
|
|
|
@ -341,8 +341,10 @@ class MyUserDetailsResponseAuthenticationConverter implements Converter<Response
|
|||
Saml2Authentication authentication = this.delegate.convert(responseToken); <1>
|
||||
UserDetails principal = this.userDetailsService.loadByUsername(username); <2>
|
||||
String saml2Response = authentication.getSaml2Response();
|
||||
Saml2ResponseAssertionAccessor assertion = new OpenSamlResponseAssertionAccessor(
|
||||
saml2Response, CollectionUtils.getFirst(response.getAssertions()));
|
||||
Collection<GrantedAuthority> authorities = principal.getAuthorities();
|
||||
return new Saml2Authentication((AuthenticatedPrincipal) userDetails, saml2Response, authorities); <3>
|
||||
return new Saml2AssertionAuthentication(userDetails, assertion, authorities); <3>
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -361,8 +363,10 @@ open class MyUserDetailsResponseAuthenticationConverter(val delegate: ResponseAu
|
|||
val authentication = this.delegate.convert(responseToken) <1>
|
||||
val principal = this.userDetailsService.loadByUsername(username) <2>
|
||||
val saml2Response = authentication.getSaml2Response()
|
||||
val assertion = OpenSamlResponseAssertionAccessor(
|
||||
saml2Response, CollectionUtils.getFirst(response.getAssertions()))
|
||||
val authorities = principal.getAuthorities()
|
||||
return Saml2Authentication(userDetails as AuthenticatedPrincipal, saml2Response, authorities) <3>
|
||||
return Saml2AssertionAuthentication(userDetails, assertion, authorities) <3>
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.saml2.jackson2;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.jackson2.SecurityJackson2Modules;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;
|
||||
|
||||
/**
|
||||
* Jackson Mixin class helps in serialize/deserialize
|
||||
* {@link Saml2AssertionAuthentication}.
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new Saml2Jackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 7.0
|
||||
* @see Saml2Jackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties(value = { "authenticated" }, ignoreUnknown = true)
|
||||
class Saml2AssertionAuthenticationMixin {
|
||||
|
||||
@JsonCreator
|
||||
Saml2AssertionAuthenticationMixin(@JsonProperty("principal") Object principal,
|
||||
@JsonProperty("assertion") Saml2ResponseAssertionAccessor assertion,
|
||||
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
|
||||
@JsonProperty("relyingPartyRegistrationId") String relyingPartyRegistrationId) {
|
||||
}
|
||||
|
||||
}
|
|
@ -22,10 +22,12 @@ import com.fasterxml.jackson.databind.module.SimpleModule;
|
|||
import org.springframework.security.jackson2.SecurityJackson2Modules;
|
||||
import org.springframework.security.saml2.core.Saml2Error;
|
||||
import org.springframework.security.saml2.provider.service.authentication.DefaultSaml2AuthenticatedPrincipal;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertion;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
|
||||
|
||||
/**
|
||||
|
@ -49,6 +51,8 @@ public class Saml2Jackson2Module extends SimpleModule {
|
|||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
context.setMixInAnnotations(Saml2Authentication.class, Saml2AuthenticationMixin.class);
|
||||
context.setMixInAnnotations(Saml2AssertionAuthentication.class, Saml2AssertionAuthenticationMixin.class);
|
||||
context.setMixInAnnotations(Saml2ResponseAssertion.class, SimpleSaml2ResponseAssertionAccessorMixin.class);
|
||||
context.setMixInAnnotations(DefaultSaml2AuthenticatedPrincipal.class,
|
||||
DefaultSaml2AuthenticatedPrincipalMixin.class);
|
||||
context.setMixInAnnotations(Saml2LogoutRequest.class, Saml2LogoutRequestMixin.class);
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.saml2.jackson2;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
|
||||
import org.springframework.security.jackson2.SecurityJackson2Modules;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertion;
|
||||
|
||||
/**
|
||||
* Jackson Mixin class helps in serialize/deserialize {@link Saml2ResponseAssertion}.
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new Saml2Jackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 7.0
|
||||
* @see Saml2Jackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties(value = { "authenticated" }, ignoreUnknown = true)
|
||||
class SimpleSaml2ResponseAssertionAccessorMixin {
|
||||
|
||||
@JsonCreator
|
||||
SimpleSaml2ResponseAssertionAccessorMixin(@JsonProperty("responseValue") String responseValue,
|
||||
@JsonProperty("nameId") String nameId, @JsonProperty("sessionIndexes") List<String> sessionIndexes,
|
||||
@JsonProperty("attributes") Map<String, List<Object>> attributes) {
|
||||
}
|
||||
|
||||
}
|
|
@ -30,7 +30,9 @@ import org.springframework.util.Assert;
|
|||
*
|
||||
* @author Clement Stoquart
|
||||
* @since 5.4
|
||||
* @deprecated Please use {@link Saml2ResponseAssertionAccessor}
|
||||
*/
|
||||
@Deprecated
|
||||
public class DefaultSaml2AuthenticatedPrincipal implements Saml2AuthenticatedPrincipal, Serializable {
|
||||
|
||||
@Serial
|
||||
|
@ -58,6 +60,12 @@ public class DefaultSaml2AuthenticatedPrincipal implements Saml2AuthenticatedPri
|
|||
this.sessionIndexes = sessionIndexes;
|
||||
}
|
||||
|
||||
public DefaultSaml2AuthenticatedPrincipal(String name, Saml2ResponseAssertionAccessor assertion) {
|
||||
this.name = name;
|
||||
this.attributes = assertion.getAttributes();
|
||||
this.sessionIndexes = assertion.getSessionIndexes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.saml2.provider.service.authentication;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* An authentication based off of a SAML 2.0 Assertion
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 7.0
|
||||
* @see Saml2ResponseAssertionAccessor
|
||||
* @see Saml2ResponseAssertion
|
||||
*/
|
||||
public class Saml2AssertionAuthentication extends Saml2Authentication {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -4194323643788693205L;
|
||||
|
||||
private final Saml2ResponseAssertionAccessor assertion;
|
||||
|
||||
private final String relyingPartyRegistrationId;
|
||||
|
||||
public Saml2AssertionAuthentication(Saml2ResponseAssertionAccessor assertion,
|
||||
Collection<? extends GrantedAuthority> authorities, String relyingPartyRegistrationId) {
|
||||
super(assertion, assertion.getResponseValue(), authorities);
|
||||
this.assertion = assertion;
|
||||
this.relyingPartyRegistrationId = relyingPartyRegistrationId;
|
||||
}
|
||||
|
||||
public Saml2AssertionAuthentication(Object principal, Saml2ResponseAssertionAccessor assertion,
|
||||
Collection<? extends GrantedAuthority> authorities, String relyingPartyRegistrationId) {
|
||||
super(principal, assertion.getResponseValue(), authorities);
|
||||
this.assertion = assertion;
|
||||
this.relyingPartyRegistrationId = relyingPartyRegistrationId;
|
||||
setAuthenticated(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Saml2ResponseAssertionAccessor getCredentials() {
|
||||
return this.assertion;
|
||||
}
|
||||
|
||||
public String getRelyingPartyRegistrationId() {
|
||||
return this.relyingPartyRegistrationId;
|
||||
}
|
||||
|
||||
}
|
|
@ -30,8 +30,12 @@ import org.springframework.util.CollectionUtils;
|
|||
*
|
||||
* @author Clement Stoquart
|
||||
* @since 5.2.2
|
||||
* @deprecated Please use
|
||||
* {@link Saml2AssertionAuthentication#getRelyingPartyRegistrationId()} and
|
||||
* {@link Saml2ResponseAssertionAccessor} instead
|
||||
*/
|
||||
public interface Saml2AuthenticatedPrincipal extends AuthenticatedPrincipal, Saml2AuthenticationInfo {
|
||||
@Deprecated
|
||||
public interface Saml2AuthenticatedPrincipal extends AuthenticatedPrincipal {
|
||||
|
||||
/**
|
||||
* Get the first value of Saml2 token attribute by name
|
||||
|
@ -72,17 +76,10 @@ public interface Saml2AuthenticatedPrincipal extends AuthenticatedPrincipal, Sam
|
|||
* @return the {@link RelyingPartyRegistration} identifier
|
||||
* @since 5.6
|
||||
*/
|
||||
@Override
|
||||
default String getRelyingPartyRegistrationId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
default String getNameId() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
default List<String> getSessionIndexes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ public class Saml2Authentication extends AbstractAuthenticationToken {
|
|||
@Serial
|
||||
private static final long serialVersionUID = 405897702378720477L;
|
||||
|
||||
private final AuthenticatedPrincipal principal;
|
||||
private final Object principal;
|
||||
|
||||
private final String saml2Response;
|
||||
|
||||
|
@ -61,6 +61,14 @@ public class Saml2Authentication extends AbstractAuthenticationToken {
|
|||
setAuthenticated(true);
|
||||
}
|
||||
|
||||
public Saml2Authentication(Object principal, String saml2Response,
|
||||
Collection<? extends GrantedAuthority> authorities) {
|
||||
super(authorities);
|
||||
this.principal = principal;
|
||||
this.saml2Response = saml2Response;
|
||||
setAuthenticated(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.saml2.provider.service.authentication;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.opensaml.saml.saml2.core.NameID;
|
||||
import org.opensaml.saml.saml2.core.SessionIndex;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||
|
||||
/**
|
||||
* Additional SAML 2.0 authentication information
|
||||
*
|
||||
* <p>
|
||||
* SAML 2.0 Single Logout requires that the {@link Authentication#getPrincipal()
|
||||
* authenticated principal} or the {@link Authentication} itself implements this
|
||||
* interface.
|
||||
*
|
||||
* @author Christian Schuster
|
||||
*/
|
||||
public interface Saml2AuthenticationInfo {
|
||||
|
||||
/**
|
||||
* Get the {@link RelyingPartyRegistration} identifier
|
||||
* @return the {@link RelyingPartyRegistration} identifier
|
||||
*/
|
||||
String getRelyingPartyRegistrationId();
|
||||
|
||||
/**
|
||||
* Get the {@link NameID} value of the authenticated principal
|
||||
* @return the {@link NameID} value of the authenticated principal
|
||||
*/
|
||||
String getNameId();
|
||||
|
||||
/**
|
||||
* Get the {@link SessionIndex} values of the authenticated principal
|
||||
* @return the {@link SessionIndex} values of the authenticated principal
|
||||
*/
|
||||
List<String> getSessionIndexes();
|
||||
|
||||
/**
|
||||
* Try to obtain a {@link Saml2AuthenticationInfo} instance from an
|
||||
* {@link Authentication}
|
||||
*
|
||||
* <p>
|
||||
* The result is either the {@link Authentication#getPrincipal() authenticated
|
||||
* principal}, the {@link Authentication} itself, or {@code null}.
|
||||
*
|
||||
* <p>
|
||||
* Returning {@code null} indicates that the given {@link Authentication} does not
|
||||
* represent a SAML 2.0 authentication.
|
||||
* @param authentication the {@link Authentication}
|
||||
* @return the {@link Saml2AuthenticationInfo} or {@code null} if unavailable
|
||||
*/
|
||||
static Saml2AuthenticationInfo fromAuthentication(Authentication authentication) {
|
||||
if (authentication == null) {
|
||||
return null;
|
||||
}
|
||||
Object principal = authentication.getPrincipal();
|
||||
if (principal instanceof Saml2AuthenticationInfo) {
|
||||
return (Saml2AuthenticationInfo) principal;
|
||||
}
|
||||
if (authentication instanceof Saml2AuthenticationInfo) {
|
||||
return (Saml2AuthenticationInfo) authentication;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.saml2.provider.service.authentication;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An OpenSAML-based implementation of {@link Saml2ResponseAssertionAccessor}
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 7.0
|
||||
*/
|
||||
public class Saml2ResponseAssertion implements Saml2ResponseAssertionAccessor {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -7505233045395024212L;
|
||||
|
||||
private final String responseValue;
|
||||
|
||||
private final String nameId;
|
||||
|
||||
private final List<String> sessionIndexes;
|
||||
|
||||
private final Map<String, List<Object>> attributes;
|
||||
|
||||
Saml2ResponseAssertion(String responseValue, String nameId, List<String> sessionIndexes,
|
||||
Map<String, List<Object>> attributes) {
|
||||
Assert.notNull(responseValue, "response value cannot be null");
|
||||
Assert.notNull(nameId, "nameId cannot be null");
|
||||
Assert.notNull(sessionIndexes, "sessionIndexes cannot be null");
|
||||
Assert.notNull(attributes, "attributes cannot be null");
|
||||
this.responseValue = responseValue;
|
||||
this.nameId = nameId;
|
||||
this.sessionIndexes = sessionIndexes;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public static Builder withResponseValue(String responseValue) {
|
||||
return new Builder(responseValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameId() {
|
||||
return this.nameId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSessionIndexes() {
|
||||
return this.sessionIndexes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<Object>> getAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResponseValue() {
|
||||
return this.responseValue;
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
|
||||
private final String responseValue;
|
||||
|
||||
private String nameId;
|
||||
|
||||
private List<String> sessionIndexes = List.of();
|
||||
|
||||
private Map<String, List<Object>> attributes = Map.of();
|
||||
|
||||
Builder(String responseValue) {
|
||||
this.responseValue = responseValue;
|
||||
}
|
||||
|
||||
public Builder nameId(String nameId) {
|
||||
this.nameId = nameId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder sessionIndexes(List<String> sessionIndexes) {
|
||||
this.sessionIndexes = sessionIndexes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder attributes(Map<String, List<Object>> attributes) {
|
||||
this.attributes = attributes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Saml2ResponseAssertion build() {
|
||||
return new Saml2ResponseAssertion(this.responseValue, this.nameId, this.sessionIndexes, this.attributes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.saml2.provider.service.authentication;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* An interface that represents key details from a SAML 2.0 Assertion
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 7.0
|
||||
* @see Saml2ResponseAssertion
|
||||
*/
|
||||
public interface Saml2ResponseAssertionAccessor extends Serializable {
|
||||
|
||||
String getNameId();
|
||||
|
||||
List<String> getSessionIndexes();
|
||||
|
||||
/**
|
||||
* Get the first value of Saml2 token attribute by name
|
||||
* @param name the name of the attribute
|
||||
* @param <A> the type of the attribute
|
||||
* @return the first attribute value or {@code null} otherwise
|
||||
*/
|
||||
@Nullable default <A> A getFirstAttribute(String name) {
|
||||
List<A> values = getAttribute(name);
|
||||
return CollectionUtils.firstElement(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Saml2 token attribute by name
|
||||
* @param name the name of the attribute
|
||||
* @param <A> the type of the attribute
|
||||
* @return the attribute or {@code null} otherwise
|
||||
*/
|
||||
@Nullable default <A> List<A> getAttribute(String name) {
|
||||
return (List<A>) getAttributes().get(name);
|
||||
}
|
||||
|
||||
Map<String, List<Object>> getAttributes();
|
||||
|
||||
String getResponseValue();
|
||||
|
||||
}
|
|
@ -27,6 +27,7 @@ import org.springframework.security.saml2.core.OpenSamlInitializationService;
|
|||
import org.springframework.security.saml2.core.Saml2Error;
|
||||
import org.springframework.security.saml2.core.Saml2ErrorCodes;
|
||||
import org.springframework.security.saml2.core.Saml2X509Credential;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlOperations.VerificationConfigurer;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlOperations.VerificationConfigurer.RedirectParameters;
|
||||
import org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata;
|
||||
|
@ -142,8 +143,9 @@ class BaseOpenSamlLogoutRequestValidator implements Saml2LogoutRequestValidator
|
|||
}
|
||||
|
||||
private void validateNameId(NameID nameId, Authentication authentication, Collection<Saml2Error> errors) {
|
||||
String name = nameId.getValue();
|
||||
if (!name.equals(authentication.getName())) {
|
||||
String name = (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor assertion)
|
||||
? assertion.getNameId() : authentication.getName();
|
||||
if (!nameId.getValue().equals(name)) {
|
||||
errors.add(new Saml2Error(Saml2ErrorCodes.INVALID_REQUEST,
|
||||
"Failed to match subject in LogoutRequest with currently logged in user"));
|
||||
}
|
||||
|
|
|
@ -42,7 +42,9 @@ import org.springframework.core.convert.converter.Converter;
|
|||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.saml2.core.OpenSamlInitializationService;
|
||||
import org.springframework.security.saml2.core.Saml2ParameterNames;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||
import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
|
||||
|
@ -148,17 +150,25 @@ final class BaseOpenSamlLogoutRequestResolver implements Saml2LogoutRequestResol
|
|||
logoutRequest.setIssuer(issuer);
|
||||
NameID nameId = this.nameIdBuilder.buildObject();
|
||||
logoutRequest.setNameID(nameId);
|
||||
Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
|
||||
if (info != null) {
|
||||
if (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor info) {
|
||||
nameId.setValue(info.getNameId());
|
||||
}
|
||||
else {
|
||||
nameId.setValue(authentication.getName());
|
||||
}
|
||||
if (authentication.getCredentials() instanceof Saml2ResponseAssertionAccessor info) {
|
||||
for (String index : info.getSessionIndexes()) {
|
||||
SessionIndex sessionIndex = this.sessionIndexBuilder.buildObject();
|
||||
sessionIndex.setValue(index);
|
||||
logoutRequest.getSessionIndexes().add(sessionIndex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
nameId.setValue(authentication.getName());
|
||||
else if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal info) {
|
||||
for (String index : info.getSessionIndexes()) {
|
||||
SessionIndex sessionIndex = this.sessionIndexBuilder.buildObject();
|
||||
sessionIndex.setValue(index);
|
||||
logoutRequest.getSessionIndexes().add(sessionIndex);
|
||||
}
|
||||
}
|
||||
logoutRequest.setIssueInstant(Instant.now(this.clock));
|
||||
this.parametersConsumer
|
||||
|
@ -194,9 +204,14 @@ final class BaseOpenSamlLogoutRequestResolver implements Saml2LogoutRequestResol
|
|||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Attempting to resolve registrationId from " + authentication);
|
||||
}
|
||||
Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
|
||||
if (info != null) {
|
||||
return info.getRelyingPartyRegistrationId();
|
||||
if (authentication == null) {
|
||||
return null;
|
||||
}
|
||||
if (authentication instanceof Saml2AssertionAuthentication response) {
|
||||
return response.getRelyingPartyRegistrationId();
|
||||
}
|
||||
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal principal) {
|
||||
return principal.getRelyingPartyRegistrationId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -24,8 +24,9 @@ import org.springframework.security.core.Authentication;
|
|||
import org.springframework.security.saml2.core.OpenSamlInitializationService;
|
||||
import org.springframework.security.saml2.core.Saml2Error;
|
||||
import org.springframework.security.saml2.core.Saml2ParameterNames;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||
|
@ -130,9 +131,14 @@ final class BaseOpenSamlLogoutRequestValidatorParametersResolver
|
|||
if (registrationId != null) {
|
||||
return registrationId;
|
||||
}
|
||||
Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
|
||||
if (info != null) {
|
||||
return info.getRelyingPartyRegistrationId();
|
||||
if (authentication == null) {
|
||||
return null;
|
||||
}
|
||||
if (authentication instanceof Saml2AssertionAuthentication saml2) {
|
||||
return saml2.getRelyingPartyRegistrationId();
|
||||
}
|
||||
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal saml2) {
|
||||
return saml2.getRelyingPartyRegistrationId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -46,8 +46,9 @@ import org.springframework.security.saml2.core.OpenSamlInitializationService;
|
|||
import org.springframework.security.saml2.core.Saml2Error;
|
||||
import org.springframework.security.saml2.core.Saml2ErrorCodes;
|
||||
import org.springframework.security.saml2.core.Saml2ParameterNames;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
|
||||
|
@ -217,9 +218,14 @@ final class BaseOpenSamlLogoutResponseResolver implements Saml2LogoutResponseRes
|
|||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Attempting to resolve registrationId from " + authentication);
|
||||
}
|
||||
Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
|
||||
if (info != null) {
|
||||
return info.getRelyingPartyRegistrationId();
|
||||
if (authentication == null) {
|
||||
return null;
|
||||
}
|
||||
if (authentication instanceof Saml2AssertionAuthentication saml2) {
|
||||
return saml2.getRelyingPartyRegistrationId();
|
||||
}
|
||||
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal saml2) {
|
||||
return saml2.getRelyingPartyRegistrationId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -33,8 +33,9 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
|||
import org.springframework.security.saml2.core.Saml2Error;
|
||||
import org.springframework.security.saml2.core.Saml2ErrorCodes;
|
||||
import org.springframework.security.saml2.core.Saml2ParameterNames;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AssertionAuthentication;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;
|
||||
|
@ -329,9 +330,14 @@ public final class Saml2LogoutRequestFilter extends OncePerRequestFilter {
|
|||
if (registrationId != null) {
|
||||
return registrationId;
|
||||
}
|
||||
Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
|
||||
if (info != null) {
|
||||
return info.getRelyingPartyRegistrationId();
|
||||
if (authentication == null) {
|
||||
return null;
|
||||
}
|
||||
if (authentication instanceof Saml2AssertionAuthentication saml2) {
|
||||
return saml2.getRelyingPartyRegistrationId();
|
||||
}
|
||||
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal saml2) {
|
||||
return saml2.getRelyingPartyRegistrationId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ import org.springframework.security.core.Authentication;
|
|||
import org.springframework.security.saml2.core.OpenSamlInitializationService;
|
||||
import org.springframework.security.saml2.core.Saml2Error;
|
||||
import org.springframework.security.saml2.core.Saml2ParameterNames;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationInfo;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidatorParameters;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||
|
@ -144,9 +144,11 @@ public final class OpenSamlLogoutRequestValidatorParametersResolver
|
|||
if (registrationId != null) {
|
||||
return registrationId;
|
||||
}
|
||||
Saml2AuthenticationInfo info = Saml2AuthenticationInfo.fromAuthentication(authentication);
|
||||
if (info != null) {
|
||||
return info.getRelyingPartyRegistrationId();
|
||||
if (authentication == null) {
|
||||
return null;
|
||||
}
|
||||
if (authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal principal) {
|
||||
return principal.getRelyingPartyRegistrationId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -893,14 +893,15 @@ public final class OpenSaml5AuthenticationProvider implements AuthenticationProv
|
|||
Saml2AuthenticationToken token = responseToken.token;
|
||||
Assertion assertion = CollectionUtils.firstElement(response.getAssertions());
|
||||
String username = this.principalNameConverter.convert(assertion);
|
||||
Map<String, List<Object>> attributes = BaseOpenSamlAuthenticationProvider.getAssertionAttributes(assertion);
|
||||
List<String> sessionIndexes = BaseOpenSamlAuthenticationProvider.getSessionIndexes(assertion);
|
||||
DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, attributes,
|
||||
sessionIndexes);
|
||||
String registrationId = responseToken.token.getRelyingPartyRegistration().getRegistrationId();
|
||||
principal.setRelyingPartyRegistrationId(registrationId);
|
||||
return new Saml2Authentication(principal, token.getSaml2Response(),
|
||||
this.grantedAuthoritiesConverter.convert(assertion));
|
||||
Saml2ResponseAssertionAccessor accessor = Saml2ResponseAssertion.withResponseValue(token.getSaml2Response())
|
||||
.nameId(authenticatedPrincipal(assertion))
|
||||
.sessionIndexes(BaseOpenSamlAuthenticationProvider.getSessionIndexes(assertion))
|
||||
.attributes(BaseOpenSamlAuthenticationProvider.getAssertionAttributes(assertion))
|
||||
.build();
|
||||
Saml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, accessor);
|
||||
Collection<GrantedAuthority> authorities = this.grantedAuthoritiesConverter.convert(assertion);
|
||||
return new Saml2AssertionAuthentication(principal, accessor, authorities, registrationId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -48,7 +48,8 @@ public class DefaultSaml2AuthenticatedPrincipalTests {
|
|||
|
||||
@Test
|
||||
public void createDefaultSaml2AuthenticatedPrincipalWhenAttributesNullThenException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new DefaultSaml2AuthenticatedPrincipal("user", null))
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new DefaultSaml2AuthenticatedPrincipal("user", (Map<String, List<Object>>) null))
|
||||
.withMessageContaining("attributes cannot be null");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue