anchor.diff:  
       @Override
       protected void configure(HttpSecurity http) throws Exception {
  -        ObjectPostProcessor<OpenSamlAuthenticationProvider> processor = new ObjectPostProcessor<>() {
  -            @Override
  -            public <O extends OpenSamlAuthenticationProvider> O postProcess(O provider) {
  -                provider.setResponseTimeValidationSkew(RESPONSE_TIME_VALIDATION_SKEW);
  -                provider.setAuthoritiesMapper(AUTHORITIES_MAPPER);
  -                provider.setAuthoritiesExtractor(AUTHORITIES_EXTRACTOR);
  -                return provider;
  -            }
  -        };
  +        OpenSamlAuthenticationProvider authenticationProvider = new OpenSamlAuthenticationProvider();
  +        authenticationProvider.setAssertionValidator(OpenSamlAuthenticationProvider
  +                .createDefaultAssertionValidator(assertionToken -> {
  +        			Map<String, Object> params = new HashMap<>();
  +        			params.put(CLOCK_SKEW, Duration.ofMinutes(10).toMillis());
  +        			// ... other validation parameters
  +        			return new ValidationContext(params);
  +        		})
  +        );
   
           http
  -            .authorizeRequests(authorize -> authorize
  +            .authorizeRequests(authz -> authz
                   .anyRequest().authenticated()
               )
               .saml2Login(saml2 -> saml2
  -               .addObjectPostProcessor(processor)
  -            )
  -        ;
  -    }
  -}
  -----
  -
  -[[servlet-saml2-opensamlauthenticationprovider-authenticationmanager]]
  -==== Configure OpenSamlAuthenticationProvider as an Authentication Manager
  -We can leverage the same method, `authenticationManager`, to override and customize the default
  -`OpenSamlAuthenticationProvider`.
  +                .authenticationManager(new ProviderManager(authenticationProvider))
  +            );
  +    }
  +}
  +----
  +
  +==== Coordinating with a `UserDetailsService`
  +
  +Or, perhaps you would like to include user details from a legacy `UserDetailsService`.
  +In that case, the response authentication converter can come in handy, as can be seen below:
   
   [source,java]
   ----
   @EnableWebSecurity
   public class SecurityConfig extends WebSecurityConfigurerAdapter {
  +    @Autowired
  +    UserDetailsService userDetailsService;
   
       @Override
       protected void configure(HttpSecurity http) throws Exception {
  -        OpenSamlAuthenticationProvider authProvider = new OpenSamlAuthenticationProvider();
  -        authProvider.setResponseTimeValidationSkew(RESPONSE_TIME_VALIDATION_SKEW);
  -        authProvider.setAuthoritiesMapper(AUTHORITIES_MAPPER);
  -        authProvider.setAuthoritiesExtractor(AUTHORITIES_EXTRACTOR);
  +        OpenSamlAuthenticationProvider authenticationProvider = new OpenSamlAuthenticationProvider();
  +        authenticationProvider.setResponseAuthenticationConverter(responseToken -> {
  +        	Saml2Authentication authentication = OpenSamlAuthenticationProvider
  +                    .createDefaultResponseAuthenticationConverter() <1>
  +                    .convert(responseToken);
  +        	Assertion assertion = responseToken.getResponse().getAssertions().get(0);
  +            String username = assertion.getSubject().getNameID().getValue();
  +            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); <2>
  +            return MySaml2Authentication(userDetails, authentication); <3>
  +        });
  +
           http
  -            .authorizeRequests(authorize -> authorize
  +            .authorizeRequests(authz -> authz
                   .anyRequest().authenticated()
               )
               .saml2Login(saml2 -> saml2
  -                .authenticationManager(new ProviderManager(asList(authProvider)))
  -            )
  -        ;
  -    }
  -}
  -----
  +                .authenticationManager(new ProviderManager(authenticationProvider))
  +            );
  +    }
  +}
  +----
  +<1> First, call the default converter, which extracts attributes and authorities from the response
  +<2> Second, call the `UserDetailsService` using the relevant information
  +<3> Third, return a custom authentication that includes the user details
  +
  +[NOTE]
  +It's not required to call `OpenSamlAuthenticationProvider` 's default authentication converter.
  +It returns a `Saml2AuthenticatedPrincipal` containing the attributes it extracted from `AttributeStatement` s as well as the single `ROLE_USER` authority.
  +
  +==== Performing additional validation
  +
  +`OpenSamlAuthenticationProvider` performs minimal validation on SAML 2.0 Assertions.
  +After verifying the signature, it will:
  +
  +1. Validate `<AudienceRestriction>` and `<DelegationRestriction>` conditions
  +2. Validate `<SubjectConfirmation>` s, expect for any IP address information
  +
  +To perform additional validation, you can configure your own assertion validator that delegates to `OpenSamlAuthenticationProvider` 's default and then performs its own.
  +
  +For example, you can use OpenSAML's `OneTimeUseConditionValidator` to also validate a `<OneTimeUse>` condition, like so:
  +
  +[source,java]
  +----
  +OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
  +OneTimeUseConditionValidator validator = ...;
  +provider.setAssertionValidator(assertionToken -> {
  +    Saml2ResponseValidatorResult result = OpenSamlAuthenticationProvider
  +            .createDefaultAssertionValidator()
  +            .convert(assertionToken);
  +    Assertion assertion = assertionToken.getAssertion();
  +    OneTimeUse oneTimeUse = assertion.getConditions().getOneTimeUse();
  +    ValidationContext context = new ValidationContext();
  +    try {
  +    	if (validator.validate(oneTimeUse, assertion, context) == ValidationResult.VALID) {
  +    		return result;
  +    	}
  +    } catch (Exception e) {
  +    	return result.concat(new Saml2Error(INVALID_ASSERTION, e.getMessage()));
  +    }
  +    return result.contact(new Saml2Error(INVALID_ASSERTION, context.getValidationFailureMessage()));
  +});
  +----
  +
  +[NOTE]
  +While recommended, it's not necessary to call `OpenSamlAuthenticationProvider` 's default assertion validator.
  +A circumstance where you would skip it would be if you don't need it to check the `<AudienceRestriction>` or the `<SubjectConfirmation>` since you are doing those yourself.
   
   [[servlet-saml2-custom-authenticationmanager]]
  -==== Custom Authentication Manager
  -The authentication manager for the security filter can also be overwritten, using your own
  -custom `AuthenticationManager` implementation.
  -This authentication manager should expect a `Saml2AuthenticationToken` object
  -containing the SAML 2 Response XML data.
  +==== Using a Custom Authentication Manager
  +
  +Of course, the `authenticationManager` DSL method can be used to perform a completely custom SAML 2.0 authentication.
  +This authentication manager should expect a `Saml2AuthenticationToken` object containing the SAML 2 Response XML data.
   
   [source,java]
   ----
  
positive.code: }
  ----
  
  [[servlet-saml2-sample-boot]]
  === Spring Boot 2.x Sample
  
  We are currently working with the Spring Boot team on the
  https://github.com/spring-projects/spring-boot/issues/18260[Auto Configuration for Spring Security SAML Login].
  In the meantime, we have provided a Spring Boot sample that supports a Yaml configuration.
  
  To run the sample, follow these three steps
  
  1. Launch the Spring Boot application
  ** `./gradlew :spring-security-samples-boot-saml2login:bootRun`
  2. Open a browser
  ** http://localhost:8080/[http://localhost:8080/]
  3. This will take you to an identity provider, log in using:
  ** User: `user`
  ** Password: `password`
  
  [[servlet-saml2-sample-idps]]
  ==== Multiple Identity Provider Sample
  
  It's very simple to use multiple providers, but there are some defaults that
  may trip you up if you don't pay attention. In our SAML configuration of
  `RelyingPartyRegistration` objects, we default an SP entity ID to
  `+{baseUrl}/saml2/service-provider-metadata/{registrationId}+`
  
  That means in our two provider configuration, our system would look like
  
  ```
  registration-1 (Identity Provider 1) - Our local SP Entity ID is:
  http://localhost:8080/saml2/service-provider-metadata/registration-1
  
  registration-2 (Identity Provider 2) - Our local SP Entity ID is:
  http://localhost:8080/saml2/service-provider-metadata/registration-2
  ```
  
  In this configuration, illustrated in the sample below, to the outside world,
  we have actually created two virtual Service Provider identities
  hosted within the same application.
  
  [source,yaml]
  ----
  spring:
    security:
      saml2:
        login:
          relying-parties:
            - entity-id: &idp-entity-id https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php
              registration-id: simplesamlphp
              web-sso-url: &idp-sso-url https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php
              signing-credentials: &service-provider-credentials
                - private-key: |
                    -----BEGIN PRIVATE KEY-----
                    MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANG7v8QjQGU3MwQE
                    ...................SHORTENED FOR READ ABILITY...................
                    INrtuLp4YHbgk1mi
                    -----END PRIVATE KEY-----
                  certificate: |
                    -----BEGIN CERTIFICATE-----
                    MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMC
                    ...................SHORTENED FOR READ ABILITY...................
                    RZ/nbTJ7VTeZOSyRoVn5XHhpuJ0B
                    -----END CERTIFICATE-----
              verification-credentials: &idp-certificates
                - |
                  -----BEGIN CERTIFICATE-----
                  MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYD
                  ...................SHORTENED FOR READ ABILITY...................
                  lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk
                  -----END CERTIFICATE-----
            - entity-id: *idp-entity-id
              registration-id: simplesamlphp2
              web-sso-url: *idp-sso-url
              signing-credentials: *service-provider-credentials
              verification-credentials: *idp-certificates
  ----
  
  If this is not desirable, you can manually override the local SP entity ID by using the
  
  [source,attrs="-attributes"]
  ----
  localEntityIdTemplate = {baseUrl}/saml2/service-provider-metadata
  ----
  
  If we change our local SP entity ID to this value, it is still important that we give
  out the correct single sign on URL (the assertion consumer service URL)
  for each registered identity provider based on the registration Id.
  `+{baseUrl}/login/saml2/sso/{registrationId}+`
  
negative.code: [[servlet-saml2login]]
  == SAML 2.0 Login
  :figures: images/servlet/saml2
  :icondir: images/icons
  
  The SAML 2.0 Login feature provides an application with the capability to act as a SAML 2.0 Service Provider, having users log in to the application by using their existing account at a SAML 2.0 Identity Provider (Okta, ADFS, etc).
  
  NOTE: SAML 2.0 Login is implemented by using the *Web Browser SSO Profile*, as specified in
  https://www.oasis-open.org/committees/download.php/35389/sstc-saml-profiles-errata-2.0-wd-06-diff.pdf#page=15[SAML 2 Profiles].
  
  [[servlet-saml2-spring-security-history]]
  Since 2009, support for service providers has existed as an https://github.com/spring-projects/spring-security-saml/tree/1e013b07a7772defd6a26fcfae187c9bf661ee8f#spring-saml[extension project].
  In 2019, the process began to port that into https://github.com/spring-projects/spring-security[Spring Security] proper.
  This process is similar to the once started in 2017 for <<oauth2,Spring Security's OAuth 2.0 support>>.
  
  [NOTE]
  ====
  A working sample for {gh-samples-url}/boot/saml2login[SAML 2.0 Login] is available in the {gh-samples-url}[Spring Security repository].
  ====
  
  [[servlet-saml2login-minimaldependencies]]
  === Minimal Dependencies
  
  SAML 2.0 service provider support is found in `spring-security-saml2-service-provider`.
  It builds off of the OpenSAML library.
  
  [[servlet-saml2login-minimalconfiguration]]
  === Minimal Configuration
  
  When using https://spring.io/projects/spring-boot[Spring Boot], configuring an application as a service provider consists of two basic steps.
  First, include the needed dependencies and second, indicate the necessary identity provider metadata.
  
  ==== Specifying Identity Provider Metadata
  
  In a Spring Boot application, to specify an identity provider's metadata, simply do:
  
  [source,yml]
  ----
  spring:
    security:
      saml2:
        relyingparty:
          registration:
            example:
              identityprovider:
                entity-id: https://idp.example.com/issuer
                verification.credentials:
                  - certificate-location: "classpath:idp.crt"
                singlesignon.url: https://idp.example.com/issuer/SSO.saml2
                singlesignon.sign-request: false
  ----
  
  where
  
  * `https://idp.example.com/issuer` is the value contained in the `Issuer` attribute of the SAML responses that the identity provider will issue
  * `classpath:idp.crt` is the location on the classpath for the identity provider's certificate for verifying SAML responses, and
  * `https://idp.example.com/issuer/SSO.saml2` is the endpoint where the identity provider is expecting `AuthnRequest` s.
  
  And that's it!
  
  From here, consider jumping to:
  
  * <<servlet-saml2login-architecture,How SAML 2.0 Login Works>>
  * <<servlet-saml2login-authenticatedprincipal,How to Use the `Saml2AuthenticatedPrincipal`>>
  * <<servlet-saml2login-sansboot,How to Configure without Spring Boot>>
  
  [[servlet-saml2login-architecture]]
  === How SAML 2.0 Login Works
  
  When the above configuration is used, the application will automatically configure itself as a SAML 2.0 Service Provider - also called a relying party - that points to one or many identity providers - also called asserting parties.
  
  [NOTE]
  Identity Provider and Asserting Party are synonymous, as are Service Provider and Relying Party.
  
  There are two supported authentication flows:
  
  1. AP-Initiated flow, which is when you login in directly to the asserting party, and then select a web application to be authenticated for.
  2. RP-Initiated flow, which is when you access your relying party, and it sends an authentication request to the asserting party.
  You then authenticate and are redirected back to the relying party.
  
  To see this in action, you can navigate to a protected page in your app, for example `http://localhost:8080` is protected by default, and it will redirect you to the configured asserting party where you can authenticate.
  
  Once authenticated, the asserting party will issue an authentication response.
  Your application will then:
  
  1. Validate the response's signature against the set of public keys obtained from configuration
  2. Decrypt any encrypted assertions
  3. Validate the `ExpiresAt` and `NotBefore` timestamps, the `Issuer` url, the `<Subject>` and any `<AudienceRestriction>` conditions
  4. Map each attribute in the `AttributeStatement` into principal attributes
  5. Grant the authority `ROLE_USER` to the resulting authentication
  
  [TIP]
  Because Spring Security iterates through the set of configured public keys, it's possible to achieve key rotation by adding a new key to the list before removing a key you are retiring.
  
  The resulting `Authentication#getPrincipal`, by default, is a Spring Security `Saml2AuthenticatedPrincipal` object, and `Authentication#getName` maps to the first assertion's `NameID` element, if one is present.
  
  [[servlet-saml2login-sansboot]]
  === Overriding or Replacing Boot Auto Configuration
  
  There are two `@Bean` s that Spring Boot generates for a relying party.
  
  The first is a `WebSecurityConfigurerAdapter` that configures the app as a relying party.
  When including `spring-security-saml2-service-provider`, the `WebSecurityConfigurerAdapter` looks like:
  
  .Default JWT Configuration
  ====
  .Java
  [source,java,role="primary"]
  ----
  protected void configure(HttpSecurity http) {
      http
          .authorizeRequests(authorize -> authorize
              .anyRequest().authenticated()
          )
          .saml2Login(withDefaults());
  }
  ----
  
  .Kotlin
  [source,kotlin,role="secondary"]
  ----
  fun configure(http: HttpSecurity) {
      http {
          authorizeRequests {
              authorize(anyRequest, authenticated)
          }
          saml2Login { }
      }
  }
  ----
  ====
  
  If the application doesn't expose a `WebSecurityConfigurerAdapter` bean, then Spring Boot will expose the above default one.
  
  Replacing this is as simple as exposing the bean within the application:
  
  .Custom SAML 2.0 Login Configuration
  ====
  .Java
  [source,java,role="primary"]
  ----
  @EnableWebSecurity
  public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter {
      protected void configure(HttpSecurity http) {
          http
              .authorizeRequests(authorize -> authorize
                  .mvcMatchers("/messages/**").hasAuthority("ROLE_USER")
                  .anyRequest().authenticated()
              )
              .saml2Login(withDefaults());
      }
  }
  ----
  
  .Kotlin
  [source,kotlin,role="secondary"]
  ----
  @EnableWebSecurity
  class MyCustomSecurityConfiguration : WebSecurityConfigurerAdapter() {
      override fun configure(http: HttpSecurity) {
          http {
              authorizeRequests {
                  authorize("/messages/**", hasAuthority("ROLE_USER"))
                  authorize(anyRequest, authenticated)
              }
              saml2Login {
              }
          }
      }
  }
  ----
  ====
  
  The above requires the role of `USER` for any URL that starts with `/messages/`.
  
  [[servlet-saml2login-relyingpartyregistrationrepository]]
  The second `@Bean` Spring Boot creates is a `RelyingPartyRegistrationRepository`, which represents the AP and RP metadata.
  This includes things like the location of the SSO endpoint the relying party should use when requesting authentication from the asserting party.
  
  You can override the default by publishing your own `RelyingPartyRegistrationRepository` bean.
  For example, you can look up the asserting party's configuration by hitting its metadata endpoint like so:
  
  .Relying Party Registration Repository
  ====
  [source,java]
  ----
  @Value("${metadata.location}")
  String assertingPartyMetadataLocation;
  
  @Bean
  public RelyingPartyRegistrationRepository relyingPartyRegistrations() {
  	RelyingPartyRegistration registration = RelyingPartyRegistrations
              .fromMetadataLocation(assertingPartyMetadataLocation)
              .registrationId("example")
              .build();
      return new InMemoryRelyingPartyRegistrationRepository(registration);
  }
  ----
  ====
  
  Or you can provide each detail manually, as you can see below:
  
  .Relying Party Registration Repository Manual Configuration
  ====
  [source,java]
  ----
  @Value("${verification.key}")
  File verificationKey;
  
  @Bean
  public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exception {
      X509Certificate certificate = X509Support.decodeCertificate(this.verificationKey);
      Saml2X509Credential credential = Saml2X509Credential.verification(certificate);
      RelyingPartyRegistration registration = RelyingPartyRegistration
              .withRegistrationId("example")
              .assertingPartyDetails(party -> party
                  .entityId("https://idp.example.com/issuer")
                  .singleSignOnServiceLocation("https://idp.example.com/SSO.saml2")
                  .wantAuthnRequestsSigned(false)
                  .verificationX509Credentials(c -> c.add(credential))
              )
              .build();
      return new InMemoryRelyingPartyRegistrationRepository(registration);
  }
  ----
  ====
  
  [NOTE]
  Note that `X509Support` is an OpenSAML class, used here in the snippet for brevity
  
  [[servlet-saml2login-relyingpartyregistrationrepository-dsl]]
  
  Alternatively, you can directly wire up the repository using the DSL, which will also override the auto-configuration:
  
  .Custom Relying Party Registration DSL
  ====
  .Java
  [source,java,role="primary"]
  ----
  @EnableWebSecurity
  public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter {
      protected void configure(HttpSecurity http) {
          http
              .authorizeRequests(authorize -> authorize
                  .mvcMatchers("/messages/**").hasAuthority("ROLE_USER")
                  .anyRequest().authenticated()
              )
              .saml2Login(saml2 -> saml2
                  .relyingPartyRegistrationRepository(relyingPartyRegistrations())
              );
      }
  }
  ----
  
  .Kotlin
  [source,kotlin,role="secondary"]
  ----
  @EnableWebSecurity
  class MyCustomSecurityConfiguration : WebSecurityConfigurerAdapter() {
      override fun configure(http: HttpSecurity) {
          http {
              authorizeRequests {
                  authorize("/messages/**", hasAuthority("ROLE_USER"))
                  authorize(anyRequest, authenticated)
              }
              saml2Login {
                  relyingPartyRegistrationRepository = relyingPartyRegistrations()
              }
          }
      }
  }
  ----
  ====
  
  [NOTE]
  A relying party can be multi-tenant by registering more than one relying party in the `RelyingPartyRegistrationRepository`.
  
  [[servlet-saml2-relyingpartyregistration]]
  === RelyingPartyRegistration
  A {security-api-url}org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.html[`RelyingPartyRegistration`]
  instance represents a link between an RP and AP's metadata.
  
  In a `RelyingPartyRegistration`, you can provide RP metadata like its `Issuer` value, where it expects SAML Responses to be sent to, and any credentials that it owns for the purposes of signing or decrypting payloads.
  
  Also, you can provide AP metadata like its `Issuer` value, where it expects AuthnRequests to be sent to, and any credentials that it owns for the purposes of the RP verifying or encrypting paylods.
  
  The following `RelyingPartyRegistration` is the minimum required for most setups:
  
  [source,java]
  ----
  RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations
          .fromMetadataLocation("https://ap.example.org/metadata")
          .registrationId("my-id")
          .build();
  ----
  
  Though a more sophisticated setup is also possible, like so:
  
  [source,java]
  ----
  RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("my-id")
          .entityId("{baseUrl}/{registrationId}")
          .decryptionX509Credentials(c -> c.add(relyingPartyDecryptingCredential()))
          .assertionConsumerServiceLocation("/my-login-endpoint/{registrationId}")
          .assertingParty(party -> party
                  .entityId("https://ap.example.org")
                  .verificationX509Credentials(c -> c.add(assertingPartyVerifyingCredential()))
                  .singleSignOnServiceLocation("https://ap.example.org/SSO.saml2")
          );
  ----
  
  [TIP]
  The top-level metadata methods are details about the RP. The methods inside `assertingPartyDetails` are details about the AP.
  
  [NOTE]
  The location where an RP is expecting SAML Responses is known as the Assertion Consumer Service Location.
  
  The default for the RP's `entityId` is `+{baseUrl}/saml2/service-provider-metadata/{registrationId}+`.
  This is this value needed when configuring the AP to know about your RP.
  
  The default for the `assertionConsumerServiceLocation` is `+/login/saml2/sso/{registrationId}+`.
  It's mapped by default to {security-api-url}org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilter.html[`Saml2WebSsoAuthenticationFilter`] in the filter chain.
  
  [[servlet-saml2-rpr-uripatterns]]
  ==== URI Patterns
  
  You probably noticed in the above examples the `+{baseUrl}+` and `+{registrationId}+` placeholders.
  
  These are useful for generating URIs. As such, the RP's `entityId` and `assertionConsumerServiceLocation` support the following placeholders:
  
  * `baseUrl` - the scheme, host, and port of a deployed application
  * `registrationId` - the registration id for this relying party
  * `baseScheme` - the scheme of a deployed application
  * `baseHost` - the host of a deployed application
  * `basePort` - the port of a deployed application
  
  For example, the `assertionConsumerServiceLocation` defined above was:
  
  `+/my-login-endpoint/{registrationId}+`
  
  which in a deployed application would translate to
  
  `+/my-login-endpoint/my-id+`
  
  The `entityId` above was defined as:
  
  `+{baseUrl}/{registrationId}+`
  
  which in a deployed application would translate to
  
  `+https://rp.example.org/my-id+`
  
  [[servlet-saml2-rpr-credentials]]
  ==== Credentials
  
  You also likely noticed the credential that was used.
  
  Oftentimes, an RP will use the same key to sign payloads as well as decrypt them.
  Or it will use the same key to verify payloads as well as encrypt them.
  
  Because of this, Spring Security ships with `Saml2X509Credential`, a SAML-specific credential that simplifies configuring the same key for different use cases.
  
  At a minimum, it's necessary to have a certificate from the Asserting Party so that the AP's signed responses can be verified.
  
  To construct a `Saml2X509Credential` that you'll use to verify assertions from the Asserting Party, you can load the file and use `CertificateFactory` like so:
  
  [source,java]
  ----
  Resource resource = new ClassPathResource("ap.crt");
  try (InputStream is = resource.getInputStream()) {
  	X509Certificate certificate = (X509Certificate)
              CertificateFactory.getInstance("X.509").generateCertificate(is);
  	return Saml2X509Credential.verification(certificate);
  }
  ----
  
  Let's say that the Asserting Party is going to also encrypt the assertion.
  In that case, the Relying Party will need a private key to be able to decrypt the encrypted value.
  
  In that case, you'll need an `RSAPrivateKey` as well as its corresponding `X509Certificate`.
  You can load the first using Spring Security's `RsaKeyConverters` utility class and the second as you did before:
  
  [source,java]
  ----
  X509Certificate certificate = relyingPartyDecryptionCertificate();
  Resource resource = new ClassPathResource("rp.crt");
  try (InputStream is = resource.getInputStream()) {
  	RSAPrivateKey rsa = RsaKeyConverters.pkcs8().convert(is);
  	return Saml2X509Credential.decryption(rsa, certificate);
  }
  ----
  
  [TIP]
  When you specify the locations of these files as the appropriate Spring Boot properties, then Spring Boot will perform these conversions for you.
  
  [[servlet-saml2-rpr-duplicated]]
  ==== Duplicated Relying Party Configurations
  
  When an application uses multiple asserting parties, some configuration is duplicated between `RelyingPartyRegistration` instances:
  
  * The RP's `entityId`
  * Its `assertionConsumerServiceLocation`, and
  * Its credentials, for example its signing or decryption credentials
  
  What's nice about this setup is credentials may be more easily rotated for some identity providers vs others.
  
  The duplication can be alleviated in a few different ways.
  
  First, in YAML this can be alleviated with references, like so:
  
  [source,yaml]
  ----
  spring:
    security:
      saml2:
        relyingparty:
          okta:
            signing.credentials: &relying-party-credentials
              - private-key-location: classpath:rp.key
              - certificate-location: classpath:rp.crt
            identityprovider:
              entity-id: ...
          azure:
            signing.credentials: *relying-party-credentials
            identityprovider:
              entity-id: ...
  ----
  
  Second, in a database, it's not necessary to replicate `RelyingPartyRegistration`'s model.
  
  Third, in Java, you can create a custom configuration method, like so:
  
  [source,java]
  ----
  private RelyingPartyRegistration.Builder
          addRelyingPartyDetails(RelyingPartyRegistration.Builder builder) {
  
  	Saml2X509Credential signingCredential = ...
  	builder.signingX509Credentials(c -> c.addAll(signingCredential));
  	// ... other relying party configurations
  }
  
  @Bean
  public RelyingPartyRegistrationRepository relyingPartyRegistrations() {
      RelyingPartyRegistration okta = addRelyingPartyDetails(
              RelyingPartyRegistration
                  .fromMetadataLocation(oktaMetadataUrl)
                  .registrationId("okta")).build();
  
      RelyingPartyRegistration azure = addRelyingPartyDetails(
              RelyingPartyRegistration
                  .fromMetadataLocation(oktaMetadataUrl)
                  .registrationId("azure")).build();
  
      return new InMemoryRelyingPartyRegistrationRepository(okta, azure);
  }
  ----
  
  [[servlet-saml2-sp-initiated]]
  === RP-initiated Login
  
  You can initiate login by navigating to the SAML 2.0 login endpoint for a given `RelyingPartyRegistration`, like so:
  
  `+{baseUrl}/saml2/authenticate/{registrationId}+`
  
  Where you replace `+{baseUrl}+` and `+{registrationId}+` with the deployed location of your application and the identifier for that registration.
  
  For example, if you were deployed to `https://rp.example.org` and you gave your registration an ID of `ping`, you could navigate to:
  
  `https://rp.example.org/saml2/authenticate/ping`
  
  You can modify this by post-processing the `Saml2WebSsoAuthenticationRequestFilter`.
  
  By default, this filter will use HTTP-Redirect for the AuthNRequest.
  However, by setting `RelyingPartyRegistration.AssertingPartyDetails#singleSignOnServiceBinding` to `Saml2MessageType.POST`, like so, it will use HTTP-POST instead:
  
  [source,java]
  ----
  RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("my-id")
          // ...
          .assertingPartyDetails(party -> party
              // ...
              .singleSignOnServiceBinding(Saml2MessageType.POST)
          );
  ----
  
  [[servlet-saml2-sp-initiated-factory]]
  ==== Customizing the AuthNRequest
  
  There are a number of reasons that you may want to adjust an AuthNRequest.
  For example, you may want `ForceAuthN` to be set to `true`, which Spring Security sets to `false` by default.
  
  If you don't need information from the `HttpServletRequest` to make your decision, then the easiest way is to register a custom `AuthnRequestMarshaller` with OpenSAML.
  This will give you access to post-process the `AuthnRequest` instance before it's serialized.
  
  But, if you do need something from the request, then you can use create a custom `Saml2AuthenticationRequestContext` implementation and then a `Converter<Saml2AuthenticationRequestContext, AuthnRequest>` to build it yourself, like so:
  
  [source,java]
  ----
  @Component
  public class AuthnRequestConverter implements
          Converter<MySaml2AuthenticationRequestContext, AuthnRequest> {
  
  	private final AuthnRequestBuilder authnRequestBuilder;
  	private final IssuerBuilder issuerBuilder;
  
  	// ... constructor
  
  	public AuthnRequest convert(Saml2AuthenticationRequestContext context) {
  		MySaml2AuthenticationRequestContext custom = (MySaml2AuthenticationRequestContext) context;
  		Issuer issuer = issuerBuilder.buildObject();
  		issuer.setValue(context.getIssuer());
  
  		AuthnRequest authnRequest = authnRequestBuilder.buildObject();
  		authnRequest.setIssuer(iss);
          authnRequest.setDestination(context.getDestination());
  		authnRequest.setAssertionConsumerServiceURL(context.getAssertionConsumerServiceUrl());
  
  		// ... additional settings
  
  		authRequest.setForceAuthn(custom.getForceAuthn());
  		return authnRequest;
  	}
  }
  ----
  
  And then you can construct your own `Saml2AuthenticationRequestContextResolver` and `Saml2AuthenticationRequestFactory`, and publish them as `@Bean` s:
  
  [source,java]
  ----
  @Bean
  Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver() {
  	Saml2AuthenticationRequestContextResolver resolver =
              new DefaultSaml2AuthenticationRequestContextResolver();
  	return request -> {
          Saml2AuthenticationRequestContext context = resolver.resolve(request);
          return new MySaml2AuthenticationRequestContext(context, request.getParameter("force") != null);
  	};
  }
  
  @Bean
  Saml2AuthenticationRequestFactory authenticationRequestFactory(
  		AuthnRequestConverter authnRequestConverter) {
  
  	OpenSamlAuthenticationRequestFactory authenticationRequestFactory =
              new OpenSamlAuthenticationRequestFactory();
  	authenticationRequestFactory.setAuthenticationRequestContextConverter(authnRequestConverter);
  	return authenticationRequestFactory;
  }
  ----
  
  [[servlet-saml2-login-customize]]
  === Customizing Authentication Logic
  
  To verify SAML 2.0 Responses, Spring Security uses `OpenSamlAuthenticationProvider` by default.
  
  You can configure this in a number of ways including:
  
  1. Setting a clock skew to timestamp validation
  2. Mapping the response to a list of `GrantedAuthority` instances
  3. Customizing the strategy for validating assertions
  
  To configure these, you'll use the `saml2Login#authenticationManager` method in the DSL.
  
  [[servlet-saml2-opensamlauthenticationprovider-authenticationmanager]]
  ==== Setting a Clock Skew
  
  It's not uncommon for the asserting and relying parties to have system clocks that aren't perfectly synchronized.
  For that reason, you can configure `OpenSamlAuthenticationProvider` 's default assertion validator with some tolerance:
  
  [source,java]
  ----
  