SAML 2 Login - Documentation

Fixes gh-7472
https://github.com/spring-projects/spring-security/issues/7472
This commit is contained in:
Filip Hanik 2019-09-28 12:19:03 -07:00
parent fc8a0184b0
commit 8bc3ad16ef
3 changed files with 317 additions and 0 deletions

View File

@ -12,6 +12,8 @@ include::authorization/index.adoc[leveloffset=+1]
include::oauth2/index.adoc[leveloffset=+1]
include::saml2/index.adoc[leveloffset=+1]
include::exploits/index.adoc[leveloffset=+1]
include::integrations/index.adoc[leveloffset=+1]

View File

@ -0,0 +1,3 @@
= SAML2
include::saml2-login.adoc[]

View File

@ -0,0 +1,312 @@
[[saml2login]]
== SAML 2.0 Login
The SAML 2.0 Login, `saml2Login()`, feature provides an application with the capability to have users log in to the application by using their existing account at an 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].
Our implementation is currently limited to a simple authentication scheme.
[[saml2login-spring-security-saml2-history]]
=== SAML 2 Support in Spring Security
SAML 2 Service Provider, SP a.k.a. a relying party, support existed as an
https://github.com/spring-projects/spring-security-saml/tree/1e013b07a7772defd6a26fcfae187c9bf661ee8f#spring-saml[independent project]
since 2009. The 1.0.x branch is still in use, including in the
https://github.com/cloudfoundry/uaa[Cloud Foundry User Account and Authentication Server] that
also created a SAML 2.0 Identity Provider implementation based on the SP implementation.
In 2018 we experimented with creating an updated implementation of both a
https://github.com/spring-projects/spring-security-saml#spring-saml[Service Provider and Identity Provider]
as a standalone library. After careful, and lengthy, deliberation we, the Spring Security team, decided
to discontinue that effort. While this effort created a replacement for that standalone 1.0.x library
we didn't feel that we should build a library on top of another library.
Instead we opted to provide framework support for SAML 2 authentication as part of
https://github.com/spring-projects/spring-security[core Spring Security] instead.
[[samllogin-concepts]]
=== Saml 2 Login - High Level Concepts
`saml2Login()` is aimed to support a fraction of the https://saml.xml.org/saml-specifications[SAML 2 feature set]
with a focus on authentication being a Service Provider, SP, a relying party, receiving XML assertions from an
Identity Provider, aka an asserting party.
A SAML 2 login, or authentication, is the concept that the SP receives and validates an XML message called
an assertion from an IDP.
There are currently two supported authentication flows
1. IDP Initiated flow - example: You login in directly to Okta, and then select a web application to be authenticated for.
Okta, the IDP, sends an assertion to the web application, the SP.
2. SP Initiated flow - example: You access a web application, a SP, the application sends an
authentication request to the IDP requesting an assertion. Upon successful authentication on the IDP,
the IDP sends an assertion to the SP.
[[samllogin-feature-set]]
=== Saml 2 Login - Current Feature Set
1. Service Provider (SP/Relying Party) is identified by `entityId = {baseUrl}/saml2/service-provider-metadata/{registrationId}`
2. Receive assertion embedded in a SAML response via Http-POST or Http-Redirect at `{baseUrl}/login/saml2/sso/{registrationId}`
3. Requires the assertion to be signed, unless the response is signed
4. Supports encrypted assertions
5. Supports encrypted NameId elements
6. Allows for extraction of assertion attributes into authorities using a `Converter<Assertion, Collection<? extends GrantedAuthority>>`
7. Allows mapping and white listing of authorities using a `GrantedAuthoritiesMapper`
8. Public keys in `java.security.cert.X509Certificate` format.
9. SP Initiated Authentication via an `AuthNRequest`
==== Saml 2 Login - Not Yet Supported
1. Mappings assertion conditions and attributes to session features (timeout, tracking, etc)
2. Single logout
3. Dynamic metadata generation
4. Receiving and validating standalone assertion (not wrapped in a response object)
[[samllogin-introduction-java-config]]
=== Saml 2 Login - Introduction to Java Configuration
To add `saml2Login()` to a Spring Security filter chain,
the minimal Java configuration requires a configuration repository,
the `RelyingPartyRegistrationRepository`, that contains the SAML configuration and
the invocation of the `HttpSecurity.saml2Login()` method:
[source,java]
----
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
//SAML configuration
//Mapping this application to one or more Identity Providers
return new InMemoryRelyingPartyRegistrationRepository(...);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.saml2Login()
;
}
}
----
The bean declaration is a convenient, but optional, approach.
You can directly wire up the repository using a method call
[source,java]
----
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.saml2Login()
.relyingPartyRegistrationRepository(...)
;
}
}
----
==== RelyingPartyRegistration
The https://github.com/spring-projects/spring-security/blob/5.2.0.RELEASE/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java[`RelyingPartyRegistration`]
object represents the mapping between this application, the SP, and the asserting party, the IDP.
===== URI Patterns
URI patterns are frequenty used to automatically generate URIs based on
an incoming request. The URI patterns in `saml2Login` can contain the following variables
* `baseUrl`
* `registrationId`
* `baseScheme`
* `baseHost`
* `basePort`
For example:
```
{baseUrl}/login/saml2/sso/{registrationId}
```
===== Relying Party
* `registrationId` - (required) a unique identifer for this configuration mapping.
This identifier may be used in URI paths, so care should be taken that no URI encoding is required.
* `localEntityIdTemplate` - (optional) A URI pattern that creates an entity ID for this application based on the incoming request. The default is
`{baseUrl}/saml2/service-provider-metadata/{registrationId}` and for a small sample application
it would look like
```
http://localhost:8080/saml2/service-provider-metadata/my-test-configuration
```
There is no requirement that this configuration option is a pattern, it can be a fixed URI value.
* `remoteIdpEntityId` - (required) the entity ID of the Identity Provider. Always a fixed URI value or string,
no patterns allowed.
* `assertionConsumerServiceUrlTemplate` - (optional) A URI pattern that denotes the assertion
consumer service URI to be sent with any `AuthNRequest` from the SP to the IDP during the SP initiated flow.
While this can be a pattern the actual URI must resolve to the ACS endpoint on the SP.
The default value is `{baseUrl}/login/saml2/sso/{registrationId}` and maps directly to the
https://github.com/spring-projects/spring-security/blob/5.2.0.RELEASE/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilter.java#L42[`Saml2WebSsoAuthenticationFilter`] endpoint
* `idpWebSsoUrl` - (required) a fixed URI value for the IDP Single Sign On endpoint where
the SP sends the `AuthNRequest` messages.
* `credentials` - A list of credentials, private keys and x509 certificates, used for
message signing, verification, encryption and decryption.
This list can contain redundant credentials to allow for easy rotation of credentials.
For example
** [0] - X509Certificate{VERIFICATION,ENCRYPTION} - The IDP's first public key used for
verification and encryption.
** [1] - X509Certificate/{VERIFICATION,ENCRYPTION} - The IDP's second verification key used for verification.
Encryption is always done using the first `ENCRYPTION` key in the list.
** [2] - PrivateKey/X509Certificate{SIGNING,DECRYPTION} - The SP's first signing and decryption credential.
** [3] - PrivateKey/X509Certificate{SIGNING,DECRYPTION} - The SP's second decryption credential.
Signing is always done using the first `SIGNING` key in the list.
When an incoming message is received, signatures are always required, the system will first attempt
to validate the signature using the certificate at index [0] and only move to the second
credential if the first one fails.
In a similar fashion, the SP configured private keys are used for decryption and attempted in the same order.
The first SP credential (`type=SIGNING`) will be used when messages to the IDP are signed.
===== Duplicated Relying Party Configurations
In the use case where an application uses multiple identity providers it becomes
obvious that some configuration is duplicated between two `RelyingPartyRegistration` objects
* localEntityIdTemplate
* credentials (all SP credentials, IDP credentials change)
* assertionConsumerServiceUrlTemplate
While there is some drawback in duplicating configuration values the back end
configuration repository does not need to replicate this data storage model.
There is a benefit that comes with this setup. Credentials may be more easily rotated
for some identity providers vs others. This object model can ensure that there is no
disruption when configuration is changed in a multi IDP use case and you're not able to rotate
credentials on all the identity providers.
==== Service Provider Metadata
The Spring Security SAML 2 implementation does not yet provide an endpoint for downloading
SP metadata in XML format. The minimal pieces that are exchanged
* *entity ID* - defaults to `{baseUrl}/saml2/service-provider-metadata/{registrationId}`
Other known configuration names that also use this same value
** Audience Restriction
* *single signon URL* - defaults to `{baseUrl}/login/saml2/sso/{registrationId}`
Other known configuration names that also use this same value
** Recipient URL
** Destination URL
** Assertion Consumer Service URL
* X509Certificate - the certificate that you configure as part of your {SIGNING,DECRYPTION}
credentials must be shared with the Identity Provider
==== Authentication Requests - SP Initiated Flow
To initiate an authentication from the web application, a simple redirect to
```
{baseUrl}/saml2/authenticate/{registrationId}
```
The endpoint will generate an `AuthNRequest` by invoking the `createAuthenticationRequest` method on a
configurable factory. Just expose the `Saml2AuthenticationRequestFactory` as a bean in your configuration.
[source,java]
----
public interface Saml2AuthenticationRequestFactory {
String createAuthenticationRequest(Saml2AuthenticationRequest request);
}
----
[[samllogin-sample-boot]]
=== Spring Boot 2.x Sample
We are currently working with the 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`
==== 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
```
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}`