diff --git a/README.md b/README.md index f9ae4e4..856568a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SilverStripe SAML module +# Silverstripe SAML module [![Build Status](https://github.com/silverstripe/silverstripe-saml/actions/workflows/ci.yml/badge.svg)](https://github.com/silverstripe/silverstripe-saml/actions/workflows/ci.yml) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/silverstripe/silverstripe-saml/badges/quality-score.png)](https://scrutinizer-ci.com/g/silverstripe/silverstripe-saml/) @@ -9,35 +9,35 @@ -- [Introduction](#introduction) -- [Requirements](#requirements) -- [Overview](#overview) -- [Security](#security) -- [In-depth guides](#in-depth-guides) - - [For SilverStripe developers](#for-silverstripe-developers) - - [For identity provider administrators](#for-identity-provider-administrators) -- [Changelog](#changelog) +- [Introduction](#introduction) +- [Requirements](#requirements) +- [Overview](#overview) +- [Security](#security) +- [In-depth guides](#in-depth-guides) + - [For Silverstripe developers](#for-silverstripe-developers) + - [For identity provider administrators](#for-identity-provider-administrators) +- [Changelog](#changelog) ## Introduction -This SilverStripe module provides single sign-on authentication integration with a SAML provider. +This Silverstripe module provides single sign-on authentication integration with a SAML provider. -This component can also be used alongside the default SilverStripe authentication scheme. +This component can also be used alongside the default Silverstripe authentication scheme. ## Requirements -- PHP 8+ with extensions: openssl, dom -- SilverStripe 5+ (see `2` branch for SilverStripe 4) -- Active Directory Federation Services 2.0 or greater (ADFS) -- HTTPS endpoint on SilverStripe site -- HTTPS endpoint on ADFS +- PHP 8+ with extensions: openssl, dom +- Silverstripe CMS 5 (see `2` branch for Silverstripe 4) +- Active Directory Federation Services 2.0 or greater (ADFS) +- HTTPS endpoint on Silverstripe site +- HTTPS endpoint on ADFS -This module has prevoiously been tested on the following configurations, but is now untested: +This module has previously been tested on the following configurations, but is now untested: -- Windows Server 2008 R2 with ADFS 2.0 -- Windows Server 2012 R2 with ADFS 3.0 +- Windows Server 2008 R2 with ADFS 2.0 +- Windows Server 2012 R2 with ADFS 3.0 **Note:** For LDAP only Active Directory integration, please see [silverstripe-ldap](https://github.com/silverstripe/silverstripe-ldap). @@ -48,11 +48,11 @@ _(Image) Typical authentication and authorisation flow for this module_ [Security Assertion Markup Language (SAML)](http://en.wikipedia.org/wiki/Security_Assertion_Markup_Language) is an XML-based, open-standard data format for exchanging authentication and authorization data between parties. The single most important requirement that SAML addresses is web browser single sign-on (SSO). -With this module, SilverStripe site is able to act as a SAML Service Provider (SP) entity, and thus allows users to perform a single sign-on against a centralised user directory (an Identity Provider - IdP). +With this module, Silverstripe site is able to act as a SAML Service Provider (SP) entity, and thus allows users to perform a single sign-on against a centralised user directory (an Identity Provider - IdP). The intended counterparty for this module is the [Active Directory Federation Services (ADFS)](http://en.wikipedia.org/wiki/Active_Directory_Federation_Services). ADFS is a software component developed by Microsoft that can be installed on Windows Server operating systems to provide users with single sign-on access to systems and applications located across organizational boundaries. -ADFS uses a claims-based access control authorization model to maintain application security and implement federated identity. We rely on this mechanism for authentication, and for automated synchronisation of some basic personal details into SilverStripe. +ADFS uses a claims-based access control authorization model to maintain application security and implement federated identity. We rely on this mechanism for authentication, and for automated synchronisation of some basic personal details into Silverstripe. This module doesn't allow you to store additional user attributes. If this is desired, you can optionally install the [silverstripe-ldap](https://github.com/silverstripe/silverstripe-ldap) module and run alongside to synchronise custom user attributes from an Active Directory server. @@ -60,23 +60,23 @@ This module doesn't allow you to store additional user attributes. If this is de With appropriate configuration, this module provides a secure means of authentication and authorisation. -For secure communication over the internet during the SAML authentication process, users must communicate with SilverStripe and ADFS using HTTPS. Similarly, for AD authentication to be secure users must access the SilverStripe site using HTTPS. +For secure communication over the internet during the SAML authentication process, users must communicate with Silverstripe and ADFS using HTTPS. Similarly, for AD authentication to be secure users must access the Silverstripe site using HTTPS. -SilverStripe trusts ADFS responses based on pre-shared X509 certificates. These certificates are exchanged between the Identity Provider (ADFS) and the Service Provider (SilverStripe site) during the initial configuration phase. +Silverstripe trusts ADFS responses based on pre-shared X509 certificates. These certificates are exchanged between the Identity Provider (ADFS) and the Service Provider (Silverstripe site) during the initial configuration phase. ## In-depth guides -### For SilverStripe developers +### For Silverstripe developers -- [Developer guide](docs/en/developer.md) - configure your SilverStripe site -- [Troubleshooting](docs/en/troubleshooting.md) - common problems +- [Developer guide](docs/en/developer.md) - configure your Silverstripe site +- [Troubleshooting](docs/en/troubleshooting.md) - common problems ### For identity provider administrators These guides will help you prepare your identity provider and configure it to work with the module correctly. -- [ADFS administrator guide](docs/en/adfs.md) -- [Azure AD administrator guide](docs/en/azure-ad.md) +- [ADFS administrator guide](docs/en/adfs.md) +- [Azure AD administrator guide](docs/en/azure-ad.md) ## Changelog diff --git a/docs/en/adfs.md b/docs/en/adfs.md index 643a034..62230e0 100644 --- a/docs/en/adfs.md +++ b/docs/en/adfs.md @@ -1,15 +1,14 @@ # ADFS administrator guide -This guide will step you through the configuration steps needed to integrate with a SilverStripe site by getting ADFS to act as a SAML Identity Provider (IdP). +This guide will step you through the configuration steps needed to integrate with a Silverstripe site by getting ADFS to act as a SAML Identity Provider (IdP). -As an ADFS administrator, after reading this guide, you should be able to provide federated authentication service to a SilverStripe site using the *silverstripe-saml* module. +As an ADFS administrator, after reading this guide, you should be able to provide federated authentication service to a Silverstripe site using the *silverstripe-saml* module. ## Table of contents - - [Overview](#overview) - [Make IdP certificate available](#make-idp-certificate-available) - [Create a new relying party trust](#create-a-new-relying-party-trust) @@ -36,9 +35,9 @@ If you are using a different version of AD or ADFS, this guide will hopefully gi ## Make IdP certificate available -SilverStripe site needs the IdP certificate to be able to establish the trust relationship. +Silverstripe site needs the IdP certificate to be able to establish the trust relationship. -If you have access to the web server, install the certificate at a known location and pass the path to the SilverStripe developer for configuration. +If you have access to the web server, install the certificate at a known location and pass the path to the Silverstripe developer for configuration. You can get the certificate by either parsing it out from the endpoint `https:///FederationMetadata/2007-06/FederationMetadata.xml` or by exporting the certificate manually using ADFS console on Windows. @@ -55,7 +54,7 @@ A wizard opens, click "Next" and then choose "Base-64 encoded X.509 (.CER)". Cli ## Create a new relying party trust -We'll now set up the trust relationship between the SilverStripe site and the IdP. +We'll now set up the trust relationship between the Silverstripe site and the IdP. ![](img/create_relying_party.png) @@ -63,7 +62,7 @@ Right click "Relying Party Trusts" and click "Add Relying Party Trust...". Click ### Select Data Source -Enter the SilverStripe site SAML metadata endpoint: `https:///saml/metadata` and press "Next". +Enter the Silverstripe site SAML metadata endpoint: `https:///saml/metadata` and press "Next". ![](img/add_metadata_from_endpoint.png) @@ -87,9 +86,9 @@ Right click the relying party and choose "Edit Claim Rules". ### Rule 1: Send LDAP Attributes -This rule makes arbitrary AD attributes available for SAML authentication. We surface such parameters as "mail" for use as SilverStripe's email, "givenName" and "sn" for identifying the person, and "objectGuid" as a unique identifier. +This rule makes arbitrary AD attributes available for SAML authentication. We surface such parameters as "mail" for use as Silverstripe's email, "givenName" and "sn" for identifying the person, and "objectGuid" as a unique identifier. -You could expand the list of fields provided, and the *silverstripe-saml* module would be able to utilise these, but it's best to keep SAML payloads small. A better way to accomplish this is via [LDAP integration](https://github.com/silverstripe/silverstripe-ldap) - ask your SilverStripe developer to do that instead. +You could expand the list of fields provided, and the *silverstripe-saml* module would be able to utilise these, but it's best to keep SAML payloads small. A better way to accomplish this is via [LDAP integration](https://github.com/silverstripe/silverstripe-ldap) - ask your Silverstripe developer to do that instead. Note that the "privatepersonalidentifier" must be a unique identifier (we will rely on it in "Rule 2"). Here we use "objectGuid". diff --git a/docs/en/azure-ad.md b/docs/en/azure-ad.md index 1ef6b73..82b1e08 100644 --- a/docs/en/azure-ad.md +++ b/docs/en/azure-ad.md @@ -1,7 +1,6 @@ - - [Azure AD administrator guide](#azure-ad-administrator-guide) - [Table of contents](#table-of-contents) - [Overview](#overview) @@ -13,7 +12,7 @@ # Azure AD administrator guide -This guide will step you through configuring a new SAML integration in Azure AD, such that Azure AD can act as an Identity Provider (IdP) for a SilverStripe site. +This guide will step you through configuring a new SAML integration in Azure AD, such that Azure AD can act as an Identity Provider (IdP) for a Silverstripe site. ## Table of contents diff --git a/docs/en/contributing.md b/docs/en/contributing.md index 2f19c4a..2fca24b 100644 --- a/docs/en/contributing.md +++ b/docs/en/contributing.md @@ -6,7 +6,6 @@ This document describes additional contribution guidelines that apply to this mo - - [Documentation](#documentation) - [Adding new functionality](#adding-new-functionality) - [Adding support for new identity providers (IdPs)](#adding-support-for-new-identity-providers-idps) diff --git a/docs/en/developer.md b/docs/en/developer.md index 0db56fd..3f88543 100644 --- a/docs/en/developer.md +++ b/docs/en/developer.md @@ -1,8 +1,8 @@ # Developer guide -This guide will step you through configuring your SilverStripe project to function as a SAML 2.0 Service Provider (SP). It will also show you a typical way to synchronise user details and group memberships from LDAP, using the [LDAP module](https://github.com/silverstripe/silverstripe-ldap). +This guide will step you through configuring your Silverstripe project to function as a SAML 2.0 Service Provider (SP). It will also show you a typical way to synchronise user details and group memberships from LDAP, using the [LDAP module](https://github.com/silverstripe/silverstripe-ldap). -As a SilverStripe developer after reading this guide, you should be able to correctly configure your site to integrate with the Identity Provider (IdP). You will also be able to authorise users based on their AD group memberships, and synchronise their personal details. +As a Silverstripe developer after reading this guide, you should be able to correctly configure your site to integrate with the Identity Provider (IdP). You will also be able to authorise users based on their AD group memberships, and synchronise their personal details. We assume ADFS 2.0 or greater is used as an IdP. @@ -11,7 +11,6 @@ We assume ADFS 2.0 or greater is used as an IdP. - - [Install the module](#install-the-module) - [Make x509 certificates available](#make-x509-certificates-available) - [SP certificate and key](#sp-certificate-and-key) @@ -23,7 +22,7 @@ We assume ADFS 2.0 or greater is used as an IdP. - [Additional configuration for Azure AD](#additional-configuration-for-azure-ad) - [GUID Transformation](#guid-transformation) - [Establish trust](#establish-trust) -- [Configure SilverStripe Authenticators](#configure-silverstripe-authenticators) +- [Configure Silverstripe Authenticators](#configure-silverstripe-authenticators) - [Show the SAML Login button on login form](#show-the-saml-login-button-on-login-form) - [Automatically require SAML login for every request](#automatically-require-saml-login-for-every-request) - [Test the connection](#test-the-connection) @@ -35,6 +34,7 @@ We assume ADFS 2.0 or greater is used as an IdP. - [Advanced SAML configuration](#advanced-saml-configuration) - [Allow insecure linking-by-email](#allow-insecure-linking-by-email) - [Adjust the requested AuthN contexts](#adjust-the-requested-authn-contexts) + - [Allow authentication with alternative domains (e.g. subdomains)](#allow-authentication-with-alternative-domains-eg-subdomains) - [Create your own SAML configuration for completely custom settings](#create-your-own-saml-configuration-for-completely-custom-settings) - [Additional GET Query Params for SAML](#additional-get-query-params-for-saml) - [Resources](#resources) @@ -43,7 +43,7 @@ We assume ADFS 2.0 or greater is used as an IdP. ## Install the module -First step is to add this module into your SilverStripe project. You can use Composer for this: +First step is to add this module into your Silverstripe project. You can use Composer for this: ``` composer require silverstripe/saml @@ -53,11 +53,11 @@ Commit the changes. ## Make x509 certificates available -SAML uses pre-shared certificates for establishing trust between the Service Provider (SP - here, SilverStripe) and the Identity Provider (IdP - here, ADFS). +SAML uses pre-shared certificates for establishing trust between the Service Provider (SP - here, Silverstripe) and the Identity Provider (IdP - here, ADFS). ### SP certificate and key -You need to make the SP x509 certificate and private key available to the SilverStripe site to be able to sign SAML requests. The certificate's "Common Name" needs to match the site endpoint that the ADFS will be using. +You need to make the SP x509 certificate and private key available to the Silverstripe site to be able to sign SAML requests. The certificate's "Common Name" needs to match the site endpoint that the ADFS will be using. For testing purposes, you can generate this yourself by using the `openssl` command: @@ -69,7 +69,7 @@ Contact your system administrator if you are not sure how to install these. ### IdP certificate -You also need to make the certificate for your ADFS endpoint available to the SilverStripe site. Talk with your ADFS administrator to find out how to obtain this. +You also need to make the certificate for your ADFS endpoint available to the Silverstripe site. Talk with your ADFS administrator to find out how to obtain this. * In you are integrating with ADFS, direct the ADFS administrator to the [ADFS administrator guide](adfs.md). * If you are integrating with Azure AD, direct the Azure AD administrator to the [Azure AD administrator guide](azure-ad.md). @@ -158,9 +158,9 @@ If you prefer to receive the GUID in lower-case or upper-case format you can use ## Establish trust -At this stage the SilverStripe site trusts the IdP, but the IdP does not have any way to establish the identity of the SilverStripe site. +At this stage the Silverstripe site trusts the IdP, but the IdP does not have any way to establish the identity of the Silverstripe site. -The IdP should now be configured to extract the SP certificate from SilverStripe's SP endpoint. Once this is completed, bi-directional trust has been established and the authentication should be possible. +The IdP should now be configured to extract the SP certificate from Silverstripe's SP endpoint. Once this is completed, bi-directional trust has been established and the authentication should be possible. *silverstripe-saml* has some specific requirements on how ADFS, Azure AD and other IdPs are configured. Consult one of the following guides depending on the IdP you are integrating with. @@ -172,7 +172,7 @@ In particular, most IdPs will require that you provide them with the entity ID a * The Entity ID is the URL exactly as you have entered it in the YML above, which should be the URL to the root of your website (e.g. https://example.com) * The Reply URL is the Entity ID, with the suffix '/saml/acs' added to the end (e.g. https://example.com/saml/acs) -## Configure SilverStripe Authenticators +## Configure Silverstripe Authenticators To be able to use the SAML or the LDAP authenticator you will need to set them up in the `mysite/_config/saml.yml`. @@ -188,7 +188,7 @@ SilverStripe\Core\Injector\Injector: default: '%$SilverStripe\SAML\Authenticators\SAMLAuthenticator' ``` -**Note:** to prevent locking yourself out if using the LDAP module as well, before you remove the "MemberAuthenticator" make sure you map at least one LDAP group to the SilverStripe `Administrator` Security Group. Consult [CMS usage docs](usage.md) for how to do it. +**Note:** to prevent locking yourself out if using the LDAP module as well, before you remove the "MemberAuthenticator" make sure you map at least one LDAP group to the Silverstripe `Administrator` Security Group. Consult [CMS usage docs](usage.md) for how to do it. ### Automatically require SAML login for every request @@ -340,6 +340,17 @@ SilverStripe\SAML\Services\SAMLConfiguration: You can also set `disable_authn_contexts: true` which will disable the sending of AuthN contexts at all, allowing the remote IdP to make its best decision over what to use. This will also not require an exact match (and is therefore not recommended). +### Allow authentication with alternative domains (e.g. subdomains) + +SAML Authentication responses are typically sent to the ACS (reply) url specified to the IdP - e.g. https://example.com/saml/acs - which does not take subdomains or alternative valid domains into account - effectively redirecting someone from sub.example.com to example.com on successful authentication. IdPs often allow for this via configuring other valid reply URLs for the SP. To allow for this within your Silverstripe app, set the `SAMLConfiguration.extra_acs_base` configuration to an array of valid strings. These need to be in the same format as the EntityId - valid URLs WITHOUT a trailing slash (since Silverstripe CMS 5.0). + +```yml +SilverStripe\SAML\Services\SAMLConfiguration: + extra_acs_base: + - https://app.example.com + - https://docs.example.com +``` + ### Create your own SAML configuration for completely custom settings It is possible to customize all the settings provided by the 3rd party SAML code. diff --git a/docs/en/tech_notes.md b/docs/en/tech_notes.md index 4d14a3b..e0a9c65 100644 --- a/docs/en/tech_notes.md +++ b/docs/en/tech_notes.md @@ -1,7 +1,6 @@ - - [Technical notes](#technical-notes) - [Interface between SAML and LDAP](#interface-between-saml-and-ldap) - [SAML+LDAP sequence](#samlldap-sequence) @@ -31,7 +30,7 @@ Normal sequence, involving single sign-on and LDAP synchronisation: 1. User is sent back to `SAMLController::acs`, with an appropriate authentication token 1. If `Member` record is not found, stub is created with some basic fields (i.e. GUID, name, surname, email), but no group mapping. -1. User is logged into SilverStripe as that member, considered authenticated. GUID is used to uniquely identify that +1. User is logged into Silverstripe as that member, considered authenticated. GUID is used to uniquely identify that user. 1. A login hook is triggered at `LDAPMemberExtension::memberLoggedIn` 1. LDAP synchronisation is performed by looking up the GUID. All `Member` fields are overwritten with the data obtained diff --git a/docs/en/troubleshooting.md b/docs/en/troubleshooting.md index 190dc38..7340f18 100644 --- a/docs/en/troubleshooting.md +++ b/docs/en/troubleshooting.md @@ -9,17 +9,16 @@ This guide contains a list of solutions to problems we have encountered in pract - -- ["Invalid issuer" error in SilverStripe](#invalid-issuer-error-in-silverstripe) +- ["Invalid issuer" error in Silverstripe](#invalid-issuer-error-in-silverstripe) - [Updating ADFS from 1.0 to 2.0](#updating-adfs-from-10-to-20) - [ADFS 3.0 and Chrome authentication](#adfs-30-and-chrome-authentication) - [Intranet level security settings](#intranet-level-security-settings) -## "Invalid issuer" error in SilverStripe +## "Invalid issuer" error in Silverstripe -In your SAML configuration file for SilverStripe, `entityId` must match *exactly* to the correct URL (including the protocol). +In your SAML configuration file for Silverstripe, `entityId` must match *exactly* to the correct URL (including the protocol). The correct URL can be extracted from ADFS by checking the "Federation Service Properties". diff --git a/src/Services/SAMLConfiguration.php b/src/Services/SAMLConfiguration.php index 5e64b6a..f7f486c 100644 --- a/src/Services/SAMLConfiguration.php +++ b/src/Services/SAMLConfiguration.php @@ -151,7 +151,6 @@ public function asArray() $spEntityId = Injector::inst()->convertServiceProperty($sp['entityId']); $extraAcsBaseUrl = (array)$config->get('extra_acs_base'); $currentBaseUrl = Director::absoluteBaseURL(); - $count = count($extraAcsBaseUrl); $acsBaseUrl = in_array($currentBaseUrl, $extraAcsBaseUrl) ? $currentBaseUrl : $spEntityId; $spX509Cert = Injector::inst()->convertServiceProperty($sp['x509cert']); diff --git a/tests/php/Services/SAMLConfigurationTest.php b/tests/php/Services/SAMLConfigurationTest.php index c5b6481..91f0b98 100644 --- a/tests/php/Services/SAMLConfigurationTest.php +++ b/tests/php/Services/SAMLConfigurationTest.php @@ -16,7 +16,7 @@ protected function setUp(): void $config->set(Director::class, 'alternate_base_url', 'https://running.test'); $config->set(SAMLConfiguration::class, 'extra_acs_base', [ - 'https://example.running.test/' + 'https://example.running.test' ]); $config->set(SAMLConfiguration::class, 'SP', [ @@ -46,12 +46,12 @@ public function provideBaseUrls(): array 'SP.EntityId should be used by default' ], [ - 'https://example.running.test/', + 'https://example.running.test', 'https://example.running.test/saml/acs', 'Extra ACS should work when the loaded (or specified) domain matches' ], [ - 'https://not-legit.running.test/', + 'https://not-legit.running.test', 'https://running.test/saml/acs', 'Unlisted ACS base should result in the SP.EntityId being used instead', ],