Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
261:integration:saml [2026/04/28 07:30] – [Changing the license pool and license type for existing users] Weinlein, Thomas261:integration:saml [2026/04/28 09:04] (current) – [Configure signing] Weinlein, Thomas
Line 1: Line 1:
-====== Configure SAML Authentication (work in progress) ======+====== Configure SAML Authentication ======
  
 SAML stands for Security Assertion Markup Language. It is a current standard for authenticating users in a distributed system. SAML stands for Security Assertion Markup Language. It is a current standard for authenticating users in a distributed system.
Line 58: Line 58:
 sp.saml.keystore.password = SECRET sp.saml.keystore.password = SECRET
 </code> </code>
 +
 +//Please make sure to call **''update.sh|bat''** after enabling or disabling SAML and restart Stages.//
  
  
Line 98: Line 100:
 The most reliable way to configure the SAML Identity Provider (IdP) is to ask the access management team for the IdP metadata. The most reliable way to configure the SAML Identity Provider (IdP) is to ask the access management team for the IdP metadata.
  
-From this metadata, you will be able to derive the parameters+Store this metadata in ''conf/secret/idp-metadata.xml'' and add the following configuration to  
 +''conf/config.xml'' 
 +<code xml> 
 +<method type="SAML2" name="saml-idp" enabled="${idp.saml.enabled}"> 
 +    <properties> 
 +        [...] 
 +        <property name="identityProviderMetadataPath" value="${idp.saml.metadata.path}"/> 
 +        [...] 
 +    </properties> 
 +    [...] 
 +</method> 
 +</code>
  
-  * EntityIdfromMetadata +''conf/config.properties'' 
- +<code properties
-  * SingleSignOnServiceLocationFromMetadata (should be HTTPS, please see [[:general:saml-note-samesite]]) +idp.saml.metadata.path file:conf/secret/idp-metadata.xml 
- +</code>
-  * DisplayName (alternative: FirstName, LastName) +
- +
-  * EMailAddress +
- +
-for the following configuration: +
- +
-<code -> +
-<authentication +
-     enabled="true" +
-     keystoreFile="/opt/stages/conf/saml-keystore.jks" +
-     keystorePass="changeit"> +
- +
-    <service-provider +
-         providerId="yourStagesURL" +
-         keyAlias="samlkeyalias"+
- +
-    </service-provider> +
- +
-    <identity-provider +
-        providerId="EntityIDfromMetadata" +
-        providerUrl="SingleSignOnServiceLocationFromMetadata" +
-        nameIdPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" +
-        sendBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" +
-        userFullnameTemplate="%firstname% %lastname%"> +
- +
-        <!-- userFullnameTemplate is used to build the user's full name from multiple IDP attributes +
-             as defined below as <identity-provider-attribute name=.../>. +
-             In the example above the firstname and lastname attributes are concatenated speparated by a space. --> +
- +
-        <!-- hardcoded magic value that specifies the NameID from the SAML reply --> +
-        <identity-provider-attribute name="username" id="http://schemas.stages.methodpark.com/saml/v2/identity/claims/subject" /> +
- +
-        <!-- either "fullname" or "firstname" and "lastname" need to be defined --> +
-        <!--<identity-provider-attribute name="fullname" id="DisplayName" />--> +
-        <identity-provider-attribute name="firstname" id="FirstName" /> +
-        <identity-provider-attribute name="lastname" id="LastName" /> +
- +
-        <identity-provider-attribute name="email" id="EMailAddress" /> +
- +
-        <!-- This matches if the SAML assertion contains a SAML attribute "Organization" with value "External" --> +
-        <!-- +
-        <identity-provider-attribute-match +
-            id="Organization" +
-            pattern="External" +
-            defaultRolesUsername="default_supplier" +
-            defaultLicenseType="DEV" +
-            licensePoolIdent="" +
-            autocreateUser="true" +
-                    /> +
-        -->+
  
 +A complete SAML configuration should then look like that:
 +<code xml>
 +<method type="SAML2" name="saml-idp" enabled="${idp.saml.enabled}">
 +    <properties>
 +        <property name="serviceProviderEntityId" value="${sp.id}"/>
 +        <property name="keystorePath" value="${sp.saml.keystore.path}"/>
 +        <property name="keystorePassword" value="${sp.saml.keystore.password}"/>
 +        <property name="privateKeyPassword" value="${sp.saml.keystore.password}"/>
 +        <property name="keyStoreAlias" value="${sp.saml.keystore.keyAlias}"/>
 +        <property name="serviceProviderMetadataPath" value="${sp.saml.metadata.path}"/>
 +        <property name="identityProviderMetadataPath" value="${idp.saml.metadata.path}"/>
 +        <property name="nameIdPolicyFormat" value="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" />
 +        <property name="mappedAttributes(_subject_nameid_)" value="username"/>
 +        <property name="mappedAttributes(${idp.saml.attribute.firstname})" value="firstname"/>
 +        <property name="mappedAttributes(${idp.saml.attribute.lastname})" value="lastname"/>
 +        <property name="mappedAttributes(${idp.saml.attribute.email})" value="email"/>
 +        <property name="internal.userFullnameTemplate" value="${idp.saml.fullname.template}"/>
 +    </properties>
 +    <filters>
         <!-- This is the default matcher, identified by id and pattern attributes set to "*" -->         <!-- This is the default matcher, identified by id and pattern attributes set to "*" -->
-        <identity-provider-attribute-match +        <attribute-match 
-            id="*" +                id="*" 
-            pattern="*" +                pattern="*" 
-            defaultRolesUsername="default" +                defaultUserGroupsUsername="${user.default.username}
-            defaultLicenseType="QM" +                defaultLicenseType="${user.default.licenseType}
-            licensePoolIdent="+                autocreateUser="${idp.saml.autocreate}"
-            autocreateUser="true"+
         />         />
 +    </filters>
 +</method>
 +</code>
  
-        <!-- +''conf/config.properties'' 
-            Specifying at least one signing certificate automatically enables +<code properties> 
-            signature verification of the authentication response. +sp.id = https://stages.example.com 
-            The key data can also be copied from the IdP metadata+#needs to be generatedPath needs to be relative to ${STAGES_ROOT} 
-            If no signing certificate is specified, no signature +sp.saml.keystore.path = file:conf/secret/saml-keystore.pk12 
-            validation will be performed+#will be generated on first login page access (path needs to be relative to ${STAGES_ROOT}) 
-        --> +sp.saml.metadata.path = file:data-cache/generated/saml-sp-metadata.xml
-        <certificate use="signing">${saml.idp.signatureCertificate}+
  
-       <!-- MIIDCTCC...Qwgf5bXby+ug==   --> +idp.saml.enabled = false 
-        </certificate>+#exported metadata from SAML IDP (path needs to be relative to ${STAGES_ROOT}) 
 +idp.saml.metadata.path = file:conf/secret/saml-idp-metadata.xml 
 +idp.saml.attribute.firstname urn:oid:2.5.4.42 
 +idp.saml.attribute.lastname urn:oid:2.5.4.4 
 +idp.saml.attribute.email = urn:oid:1.2.840.113549.1.9.1 
 +idp.saml.fullname.template = %firstname% %lastname% 
 +idp.saml.autocreate = true
  
-        <!-- +user.default.username default 
-             In case the IDP only provides encrypted assertions specify +user.default.licenseType AuthPsReader
-             encryption certifacteThe key data can also be copied from the +
-             IdP metadataIf no encryption certificate is specified, no encrypted +
-             assertion can be accepted. +
-        --> +
-        <certificate use="encryption">${saml.idp.encryptionCertificate} +
-                <!-- MIIDCTCC...Qwgf5bXby+ug==  --> +
-       </certificate> +
-    </identity-provider> +
- +
-    <!-- +
-        This identity provider statement with the "STAGES" +
-        magic id enables local Stages logins to be available +
-        for selection between multiple identity providers +
-        (NOT IMPLEMENTED YET). +
-        If the authentication with the IdP fails and the "STAGES" +
-        provider is enabled, the Stages login screen is shown, so +
-        the user may log in with a local user idIf it is not +
-        configured, the server answers with a 403 (Forbidden) +
-        status code. +
-    --> +
-    <identity-provider providerId="STAGES"/> +
-</authentication>+
 </code> </code>
  
Line 240: Line 207:
  
 <code xml>  <code xml> 
-<identity-provider-attribute-match      +<attribute-match      
     id="[saml_attribute_id]"     id="[saml_attribute_id]"
     pattern="[matching_value]"           pattern="[matching_value]"      
Line 275: Line 242:
      [...]      [...]
  </method>  </method>
 +</code>
 +
 +===== Configure signature validation =====
 +
 +By default Stages send authentication requests signed and expects assertions in the response as well as the response itself to be signed.
 +In case this is not supported by the IDP it can be disabled by setting the according property to **false**.
 +
 +//Please use with care as it degrades security.//
 +<code xml>
 +<method type="SAML2" [...]>
 +    <properties> <!-- implicit default values -->
 +        <property name="authnRequestSigned" value="true" />
 +        <property name="wantsAssertionsSigned" value="true" />
 +        <property name="wantsResponsesSigned" value="true" />
 +    </properties>
 +</method>
 +</code>
 +===== Configure multiple SAML IDPs =====
 +
 +Stages does now allow to configure multiple IDPs. Just add another authentication method of type SAML2 and ensure it has an **unique name**. E.g.
 +
 +<code xml>
 +<authentication>
 +    [...]
 +    <method type="SAML2" name="saml-idp-1" enabled="${idp1.saml.enabled}">
 +        [...]
 +    </method>
 +    <method type="SAML2" name="saml-idp-2" enabled="${idp2.saml.enabled}">
 +        [...]
 +    </method>
 +</authentication>
 +</code>
 +
 +This will create a SSO button on the login page for each IDP.
 +Please provide a user understandable naming by defining a translation property for each login.sso.[name] propertyin each supported language:
 +''conf/local.properties'', ''conf/local_de.properties'', ...
 +<code properties>
 +login.sso.saml-idp-1 = Single Sign-On for company 1
 +login.sso.saml-idp-2 = Single Sign-On for company 2
 </code> </code>