| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 | 
							- = SAML Migrations
 
- The following steps relate to changes around how to configure SAML 2.0.
 
- == Use OpenSAML 4
 
- OpenSAML 3 has reached its end-of-life.
 
- As such, Spring Security 6 drops support for it, bumping up its OpenSAML baseline to 4.
 
- To prepare for the upgrade, update your pom to depend on OpenSAML 4 instead of 3:
 
- [tabs]
 
- ======
 
- Maven::
 
- +
 
- [source,maven,role="primary"]
 
- ----
 
- <dependencyManagement>
 
-     <dependency>
 
-         <groupId>org.opensaml</groupId>
 
-         <artifactId>opensaml-core</artifactId>
 
-         <version>4.2.1</version>
 
-     </dependency>
 
-     <dependency>
 
-         <groupId>org.opensaml</groupId>
 
-         <artifactId>opensaml-saml-api</artifactId>
 
-         <version>4.2.1</version>
 
-     </dependency>
 
-     <dependency>
 
-         <groupId>org.opensaml</groupId>
 
-         <artifactId>opensaml-saml-impl</artifactId>
 
-         <version>4.2.1</version>
 
-     </dependency>
 
- </dependencyManagement>
 
- ----
 
- Gradle::
 
- +
 
- [source,gradle,role="secondary"]
 
- ----
 
- dependencies {
 
-     constraints {
 
-         api "org.opensaml:opensaml-core:4.2.1"
 
-         api "org.opensaml:opensaml-saml-api:4.2.1"
 
-         api "org.opensaml:opensaml-saml-impl:4.2.1"
 
-     }
 
- }
 
- ----
 
- ======
 
- You must use at least OpenSAML 4.1.1 to update to Spring Security 6's SAML support.
 
- == Use `OpenSaml4AuthenticationProvider`
 
- In order to support both OpenSAML 3 and 4 at the same time, Spring Security released `OpenSamlAuthenticationProvider` and `OpenSaml4AuthenticationProvider`.
 
- In 6.0, because OpenSAML3 support is removed, `OpenSamlAuthenticationProvider` is removed as well.
 
- Not all methods in `OpenSamlAuthenticationProvider` were ported 1-to-1 to `OpenSaml4AuthenticationProvider`.
 
- As such, some adjustment will be required to make the challenge.
 
- Consider the following representative usage of `OpenSamlAuthenticationProvider`:
 
- [tabs]
 
- ======
 
- Java::
 
- +
 
- [source,java,role="primary"]
 
- ----
 
- OpenSamlAuthenticationProvider versionThree = new OpenSamlAuthenticationProvider();
 
- versionThree.setAuthoritiesExtractor(myAuthoritiesExtractor);
 
- versionThree.setResponseTimeValidationSkew(myDuration);
 
- ----
 
- Kotlin::
 
- +
 
- [source,kotlin,role="secondary"]
 
- ----
 
- val versionThree: OpenSamlAuthenticationProvider = OpenSamlAuthenticationProvider()
 
- versionThree.setAuthoritiesExtractor(myAuthoritiesExtractor)
 
- versionThree.setResponseTimeValidationSkew(myDuration)
 
- ----
 
- ======
 
- This should change to:
 
- [tabs]
 
- ======
 
- Java::
 
- +
 
- [source,java,role="primary"]
 
- ----
 
- Converter<ResponseToken, Saml2Authentication> delegate = OpenSaml4AuthenticationProvider
 
-         .createDefaultResponseAuthenticationConverter();
 
- OpenSaml4AuthenticationProvider versionFour = new OpenSaml4AuthenticationProvider();
 
- versionFour.setResponseAuthenticationConverter((responseToken) -> {
 
- 	Saml2Authentication authentication = delegate.convert(responseToken);
 
- 	Assertion assertion = responseToken.getResponse().getAssertions().get(0);
 
- 	AuthenticatedPrincipal principal = (AuthenticatedPrincipal) authentication.getPrincipal();
 
- 	Collection<GrantedAuthority> authorities = myAuthoritiesExtractor.convert(assertion);
 
- 	return new Saml2Authentication(principal, authentication.getSaml2Response(), authorities);
 
- });
 
- Converter<AssertionToken, Saml2ResponseValidationResult> validator = OpenSaml4AuthenticationProvider
 
-         .createDefaultAssertionValidatorWithParameters((p) -> p.put(CLOCK_SKEW, myDuration));
 
- versionFour.setAssertionValidator(validator);
 
- ----
 
- Kotlin::
 
- +
 
- [source,kotlin,role="secondary"]
 
- ----
 
- val delegate = OpenSaml4AuthenticationProvider.createDefaultResponseAuthenticationConverter()
 
- val versionFour = OpenSaml4AuthenticationProvider()
 
- versionFour.setResponseAuthenticationConverter({
 
-     responseToken -> {
 
-         val authentication = delegate.convert(responseToken)
 
-         val assertion = responseToken.getResponse().getAssertions().get(0)
 
-         val principal = (AuthenticatedPrincipal) authentication.getPrincipal()
 
-         val authorities = myAuthoritiesExtractor.convert(assertion)
 
-         return Saml2Authentication(principal, authentication.getSaml2Response(), authorities)
 
-     }
 
- })
 
- val validator = OpenSaml4AuthenticationProvider
 
-         .createDefaultAssertionValidatorWithParameters({ p -> p.put(CLOCK_SKEW, myDuration) })
 
- versionFour.setAssertionValidator(validator)
 
- ----
 
- ======
 
- == Stop Using SAML 2.0 `Converter` constructors
 
- In an early release of Spring Security's SAML 2.0 support, `Saml2MetadataFilter` and `Saml2AuthenticationTokenConverter` shipped with constructors of type `Converter`.
 
- This level of abstraction made it tricky to evolve the class and so a dedicated interface `RelyingPartyRegistrationResolver` was introduced in a later release.
 
- In 6.0, the `Converter` constructors are removed.
 
- To prepare for this in 5.8, change classes that implement `Converter<HttpServletRequest, RelyingPartyRegistration>` to instead implement `RelyingPartyRegistrationResolver`.
 
- == Change to Using `Saml2AuthenticationRequestResolver`
 
- `Saml2AuthenticationContextResolver` and `Saml2AuthenticationRequestFactory` are removed in 6.0 as is the `Saml2WebSsoAuthenticationRequestFilter` that requires them.
 
- They are replaced by `Saml2AuthenticationRequestResolver` and a new constructor in `Saml2WebSsoAuthenticationRequestFilter`.
 
- The new interface removes an unnecessary transport object between the two classes.
 
- Most applications need do nothing; however, if you use or configure `Saml2AuthenticationRequestContextResolver` or `Saml2AuthenticationRequestFactory`, try the following steps to convert instead use `Saml2AuthenticationRequestResolver`.
 
- === Use `setAuthnRequestCustomizer` instead of `setAuthenticationRequestContextConverter`
 
- If you are calling `OpenSaml4AuthenticationReqeustFactory#setAuthenticationRequestContextConverter`, for example, like so:
 
- [tabs]
 
- ======
 
- Java::
 
- +
 
- [source,java,role="primary"]
 
- ----
 
- @Bean
 
- Saml2AuthenticationRequestFactory authenticationRequestFactory() {
 
-     OpenSaml4AuthenticationRequestFactory factory = new OpenSaml4AuthenticationRequestFactory();
 
- 	factory.setAuthenticationRequestContextConverter((context) -> {
 
-         AuthnRequestBuilder authnRequestBuilder =  ConfigurationService.get(XMLObjectProviderRegistry.class)
 
-             .getBuilderFactory().getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME);
 
- 		IssuerBuilder issuerBuilder =  ConfigurationService.get(XMLObjectProviderRegistry.class)
 
-             .getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
 
-         tring issuer = context.getIssuer();
 
- 		String destination = context.getDestination();
 
- 		String assertionConsumerServiceUrl = context.getAssertionConsumerServiceUrl();
 
- 		String protocolBinding = context.getRelyingPartyRegistration().getAssertionConsumerServiceBinding().getUrn();
 
- 		AuthnRequest auth = authnRequestBuilder.buildObject();
 
- 		auth.setID("ARQ" + UUID.randomUUID().toString().substring(1));
 
- 		auth.setIssueInstant(Instant.now());
 
- 		auth.setForceAuthn(Boolean.TRUE);
 
- 		auth.setIsPassive(Boolean.FALSE);
 
- 		auth.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
 
- 		Issuer iss = issuerBuilder.buildObject();
 
- 		iss.setValue(issuer);
 
- 		auth.setIssuer(iss);
 
- 		auth.setDestination(destination);
 
- 		auth.setAssertionConsumerServiceURL(assertionConsumerServiceUrl);
 
- 	});
 
- 	return factory;
 
- }
 
- ----
 
- ======
 
- to ensure that ForceAuthn is set to `true`, you can instead do:
 
- [tabs]
 
- ======
 
- Java::
 
- +
 
- [source,java,role="primary"]
 
- ----
 
- @Bean
 
- Saml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationResolver registrations) {
 
-     OpenSaml4AuthenticationRequestResolver reaolver = new OpenSaml4AuthenticationRequestResolver(registrations);
 
- 	resolver.setAuthnRequestCustomizer((context) -> context.getAuthnRequest().setForceAuthn(Boolean.TRUE));
 
- 	return resolver;
 
- }
 
- ----
 
- ======
 
- Also, since `setAuthnRequestCustomizer` has direct access to the `HttpServletRequest`, there is no need for a `Saml2AuthenticationRequestContextResolver`.
 
- Simply use `setAuthnRequestCustomizer` to read directly from `HttpServletRequest` this information you need.
 
- === Use `setAuthnRequestCustomizer` instead of `setProtocolBinding`
 
- Instead of doing:
 
- [tabs]
 
- ======
 
- Java::
 
- +
 
- [source,java,role="primary"]
 
- ----
 
- @Bean
 
- Saml2AuthenticationRequestFactory authenticationRequestFactory() {
 
-     OpenSaml4AuthenticationRequestFactory factory = new OpenSaml4AuthenticationRequestFactory();
 
- 	factory.setProtocolBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST")
 
- 	return factory;
 
- }
 
- ----
 
- ======
 
- you can do:
 
- [tabs]
 
- ======
 
- Java::
 
- +
 
- [source,java,role="primary"]
 
- ----
 
- @Bean
 
- Saml2AuthenticationRequestResolver authenticationRequestResolver() {
 
- 	OpenSaml4AuthenticationRequestResolver reaolver = new OpenSaml4AuthenticationRequestResolver(registrations);
 
- 	resolver.setAuthnRequestCustomizer((context) -> context.getAuthnRequest()
 
-             .setProtocolBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"));
 
- 	return resolver;
 
- }
 
- ----
 
- ======
 
- [NOTE]
 
- ====
 
- Since Spring Security only supports the `POST` binding for authentication, there is not very much value in overriding the protocol binding at this point in time.
 
- ====
 
- == Use the latest `Saml2AuthenticationToken` constructor
 
- In an early release, `Saml2AuthenticationToken` took several individual settings as constructor parameters.
 
- This created a challenge each time a new parameter needed to be added.
 
- Since most of these settings were part of `RelyingPartyRegistration`, a new constructor was added where a `RelyingPartyRegistration` could be provided, making the constructor more stable.
 
- It also is valuable in that it more closely aligns with the design of `OAuth2LoginAuthenticationToken`.
 
- Most applications do not construct this class directly since `Saml2WebSsoAuthenticationFilter` does.
 
- However, in the event that your application constructs one, please change from:
 
- [tabs]
 
- ======
 
- Java::
 
- +
 
- [source,java,role="primary"]
 
- ----
 
- new Saml2AuthenticationToken(saml2Response, registration.getSingleSignOnServiceLocation(),
 
-     registration.getAssertingParty().getEntityId(), registration.getEntityId(), registration.getCredentials())
 
- ----
 
- Kotlin::
 
- +
 
- [source,kotlin,role="secondary"]
 
- ----
 
- Saml2AuthenticationToken(saml2Response, registration.getSingleSignOnServiceLocation(),
 
-     registration.getAssertingParty().getEntityId(), registration.getEntityId(), registration.getCredentials())
 
- ----
 
- ======
 
- to:
 
- [tabs]
 
- ======
 
- Java::
 
- +
 
- [source,java,role="primary"]
 
- ----
 
- new Saml2AuthenticationToken(saml2Response, registration)
 
- ----
 
- Kotlin::
 
- +
 
- [source,kotlin,role="secondary"]
 
- ----
 
- Saml2AuthenticationToken(saml2Response, registration)
 
- ----
 
- ======
 
- == Use `RelyingPartyRegistration` updated methods
 
- In an early release of Spring Security's SAML support, there was some ambiguity on the meaning of certain `RelyingPartyRegistration` methods and their function.
 
- As more capabilities were added to `RelyingPartyRegistration`, it became necessary to clarify this ambiguity by changing method names to ones that aligned with spec language.
 
- The deprecated methods in `RelyingPartyRegstration` are removed.
 
- To prepare for that, consider the following representative usage of `RelyingPartyRegistration`:
 
- [tabs]
 
- ======
 
- Java::
 
- +
 
- [source,java,role="primary"]
 
- ----
 
- String idpEntityId = registration.getRemoteIdpEntityId();
 
- String assertionConsumerServiceUrl = registration.getAssertionConsumerServiceUrlTemplate();
 
- String idpWebSsoUrl = registration.getIdpWebSsoUrl();
 
- String localEntityId = registration.getLocalEntityIdTemplate();
 
- List<Saml2X509Credential> verifying = registration.getCredentials().stream()
 
-         .filter(Saml2X509Credential::isSignatureVerficationCredential)
 
-         .collect(Collectors.toList());
 
- ----
 
- Kotlin::
 
- +
 
- [source,kotlin,role="secondary"]
 
- ----
 
- val idpEntityId: String = registration.getRemoteIdpEntityId()
 
- val assertionConsumerServiceUrl: String = registration.getAssertionConsumerServiceUrlTemplate()
 
- val idpWebSsoUrl: String = registration.getIdpWebSsoUrl()
 
- val localEntityId: String = registration.getLocalEntityIdTemplate()
 
- val verifying: List<Saml2X509Credential> = registration.getCredentials()
 
-         .filter(Saml2X509Credential::isSignatureVerficationCredential)
 
- ----
 
- ======
 
- This should change to:
 
- [tabs]
 
- ======
 
- Java::
 
- +
 
- [source,java,role="primary"]
 
- ----
 
- String assertingPartyEntityId = registration.getAssertingPartyDetails().getEntityId();
 
- String assertionConsumerServiceLocation = registration.getAssertionConsumerServiceLocation();
 
- String singleSignOnServiceLocation = registration.getAssertingPartyDetails().getSingleSignOnServiceLocation();
 
- String entityId = registration.getEntityId();
 
- List<Saml2X509Credential> verifying = registration.getAssertingPartyDetails().getVerificationX509Credentials();
 
- ----
 
- Kotlin::
 
- +
 
- [source,kotlin,role="secondary"]
 
- ----
 
- val assertingPartyEntityId: String = registration.getAssertingPartyDetails().getEntityId()
 
- val assertionConsumerServiceLocation: String = registration.getAssertionConsumerServiceLocation()
 
- val singleSignOnServiceLocation: String = registration.getAssertingPartyDetails().getSingleSignOnServiceLocation()
 
- val entityId: String = registration.getEntityId()
 
- val verifying: List<Saml2X509Credential> = registration.getAssertingPartyDetails().getVerificationX509Credentials()
 
- ----
 
- ======
 
- For a complete listing of all changed methods, please see {security-api-url}org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.html[``RelyingPartyRegistration``'s JavaDoc].
 
 
  |