SAML authentication providers (Legacy)

ℹ️
This topic refers to legacy configuration syntax. SAML Authentication Providers are now defined as SAML apps, with a corresponding SAML provider.

The Maverics Orchestrator can be configured to act as a SAML Identity Provider. Using the Orchestrator as a SAML-based IDP can be useful when existing applications authenticate via SAML or when identity needs to be asserted into another system such as a legacy IDP.

Name

The name value must be a unique name for each SAMLProvider instance.

Type

The type of identity protocol, saml, that will be used.

Attribute Providers

An optional configuration for an identity system or data store from which the SAMLProvider may retrieve additional attributes used in claimsMapping.

Connector

Connector is a reference to the name of the defined connector which will be used as an attribute provider.

usernameMapping

usernameMapping defines the attribute that will be used as a search key to query for the user’s attributes.

MetadataEndpoint

The metadataEndpoint is the URL which this SAML server serves its metadata file from. This is an optional configuration, if left unset connecting Service Providers will need to be manually configured.

Single Sign On Service Endpoint

The singleSignOnServiceEndpoint is the location of where service providers will send SAML authentication requests.

Single Logout Service Endpoint

The singleLogoutServiceEndpoint is an optional field that defines
the location of where service providers will send SAML logout requests.

Issuer

issuer is the IDP who issues SAML assertions. This value is usually a URL.

Signature

signature is the configuration to sign SAML responses.

Certificate

certificate the x509 certificate used by clients to validate the signature of SAML assertions.

Private Key

privateKey is the RSA256 private key used to sign SAML assertions.

Disable Signing Response

disableSignedResponse optional field that allows you to override signing the response by default. Default is false. Note that either the Response or Assertion must be signed.

Disable Signing Assertion

disableSignedAssertion optional field that allows you to override signing the assertion by default. Default is false. Note that either the Response or Assertion must be signed.

Clients

clients represent the registered SAML clients (AKA Service Providers and Relying Parties).

Name

A unique name identifier for the client.

Audience

audience denotes who the client is. It should match the Issuer field provided by the service provider. This value must be unique.

Consumer Service URL

consumerServiceURL is the URL where SAML responses will be sent.

Duration

The duration in seconds which this server’s responses are valid for.

NameID Format

nameIDFormat is an optional field used to define the NameID Format that will be used in the SAML assertion. When not defined, a value of 'urn:oasis:names:tc:SAML:1.0:nameid-format:unspecified' will be used. If a NameIDPolicy is defined on the SAML Authentication request, it must match the NameID Format defined on the client. For more details on NameID Format, please see section 2.2, 3.4.1, and 8.3 of the SAML spec.

Request Verification

requestVerification is a mandatory configuration, and it will either hold the public key used to validate the signature of incoming requests, or it will disable request signing verification.

Certificate

certificate is the RSA x509 certificate, and will be used to verify the signatures of incoming requests. It may be defined inline or with a secret provider. It must be RSA compatible. Currently this auth provider only supports SHA-256 for request signing and digest algorithm.

requestVerification:
  certificate: |+
    -----BEGIN CERTIFICATE-----
    ...
    -----END CERTIFICATE-----    
requestVerification:
  certificate: <clientSigningCert>

Skip Verification

skipVerification is a boolean value and is used when the client does not want to validate the signatures of incoming requests.

requestVerification:
  skipVerification: true

Authentication with IDPs

The authentication field provides a way to list the idps that will be used to authenticate a user.

In the example below, the user will be prompted to authenticate with Azure first and then Okta.

authproviders:
  - name: saml
    type: samlAuthProvider
    # ...
    clients:
      - name: ExampleClient
        # ...
        authentication:
          idps:
            - azure
            - okta

AttrProviders

An optional configuration for an identity system or data store from which the SAMLProvider may retrieve additional attributes used in claimsMapping. This client level configuration will override any provider level configuration.

Connector

Connector is a reference to the name of the defined connector which will be used as an attribute provider.

usernameMapping

The usernameMapping configuration makes sure the Attribute Provider (i.e. LDAP Connector) has the correct attribute it needs to successfully query for the user’s attributes. It specifies the attribute used to look up user attributes from the defined attribute provider.

Assertion

assertion is the parent category for storing configurations related to the SAML assertions of the client. If omitted, the assertions for this client will not be
encrypted.

Encryption

encryption is the configuration category for encrypting the SAML assertion.

Key Encrypt Method

keyEncryptMethod configures the encryption method for encrypting the symmetric key which is used to encrypt the assertion. Currently, we support two values here which are RSA_OAEP and RSA-1_5.

RSA-1_5 is not recommended according to the XML encryption spec due to security risks associated with the algorithm.

Data Encrypt Method

dataEncryptMethod configures the encryption method for encrypting the actual data of the assertion. Valid values are AES128CBC, AES192CBC, and AES256CBC.

Digest Method

digestMethod is the message digest algorithm use to compute a message digest as part of the encryption process. Valid values are SHA256, and SHA512.

Certificate

certificate under the assertion section is the PEM encoded string that can be defined inline or via a secret provider. This certificate is typically retrieved from the Service Provider. Below is an example of defining the certificate inline.

    assertion:
      encryption:
        keyEncryptMethod: RSA_OAEP
        dataEncryptMethod: AES256CBC
        digestMethod: SHA256
        certificate: |+ # An example of defining the certificate value inline.
          -----BEGIN CERTIFICATE-----
          ...
          -----END CERTIFICATE-----

ClaimsMapping

The claimsMapping provides a way to provide additional claims to this user. This maps claims to session attributes provided by the IDP(s) and any optionally defined AttributeProvider(s). These mapping fields are used to map claims from the connectors back to the SAML Service Provider in AttributeStatement as AttributeValue.
Note that the key name is used to denote the value which will be used as the Subject of the SAMLResponse.

Example config:

authproviders:
  - name: samlAuthProvider
    type: saml
    # ...
    clients:
      - name: ExampleClient
        # ...
        attrProviders:
          - connector: ldap
            usernameMapping: azure.email
        claimsMapping:
          name: azure.objectidentifier
          email: azure.name
          family_name: azure.surname
          given_name: azure.givenname
          phone: ldap.mobile

IDP Initiated Login

The idpInitiatedLogin key is an optional configuration which when specified will enable the auth provider to perform IDP initiated login to the client.

Login URL

loginURL is the endpoint that the user will visit from their browser to initiate the IDP login flow. This endpoint needs to be unique on the Orchestrator.

Relay State URL

relayStateURL is the endpoint that gets passed to the service provider and is intended to be the landing page for the user after the authentication flow is complete.

authproviders:
  - type: saml
    # ...
    clients:
      - name: sonar
        # ...
        idpInitiatedLogin:
          loginURL: https://maverics.sonarsystems.com/login-sonar
          relayStateURL: https://app.sonarsystems.com/index.html

Auth Provider and Client level Service Extensions

The below Service Extensions may optionally be defined at the Auth Provider or Client level in order to control the behavior for clients.

isAuthenticatedSE

isAuthenticatedSE is an optional Service Extension that can be used to override the default behavior that determines if a user is already authenticated.

authproviders:
  - name: samlAuthProvider
    type: saml
    isAuthenticatedSE:
      funcName: IsAuthenticated
      file: /etc/maverics/extensions/auth.go

/etc/maverics/extensions/auth.go

package main

import (
  "net/http"

  "maverics/auth"
  "maverics/session"
)

func IsAuthenticated(sp *auth.SAMLProvider, rw http.ResponseWriter, req *http.Request) bool {
  if session.GetString(req, "azure.authenticated") == "true" {
    return true
  }

  return false
}

authenticateSE

The authenticateSE is an optional Service Extension used to take control of how authentication will be done.

authproviders:
  - name: saml
    type: samlAuthProvider
    authenticateSE:
      funcName: Authenticate
      file: /etc/maverics/extensions/auth.go
    # ...

/etc/maverics/extensions/auth.go

package main

import (
  "net/http"

  "maverics/auth"
)

func Authenticate(sp *auth.SAMLProvider, rw http.ResponseWriter, req *http.Request) {
  sp.IDPs["azure"].CreateRequest().Login(rw, req)
}

buildClaimsSE

buildClaimsSE is an optional Service Extension that can customize which attributes will be shared to the up-stream SAML Service Provider as a SAML 2.0 AttributeStatement.

authproviders:
  - name: samlAuthProvider
    type: saml
    buildClaimsSE:
      funcName: BuildClaims
      file: /etc/maverics/extensions/buildClaims.go

/etc/maverics/extensions/buildClaims.go

package main

import (
  "fmt"
  "net/http"

  "maverics/auth"
  "maverics/log"
  "maverics/session"
)

func BuildClaims(sp *auth.SAMLProvider, rw http.ResponseWriter, req *http.Request) (map[string]string, error) {
  log.Debug("msg", "building custom claims")

  email := session.GetString(req, "azure.email")
  if email == "" {
    return nil, fmt.Errorf("did not find user email on session")
  }

  returnAttrs := map[string]string{
    "email": email,
  }
  log.Debug(
    "msg", "successfully built custom claims",
    "claims", fmt.Sprintf("%v", returnAttrs),
  )
  return returnAttrs, nil
}

Examples

Basic

This example shows the SAML auth provider being defined.

authproviders:
  - name: samlAuthProvider
    type: saml
    metadataEndpoint: https://maverics.com/idp/saml/metadata.xml # Optional endpoint.
    singleSignOnServiceEndpoint: https://maverics.com/idp/saml/sso
    singleLogoutServiceEndpoint: https://maverics.com/idp/saml/slo # Optional endpoint to enable logout.
    issuer: https://maverics.com/idp/
    signature:
      certificate: |+ # An example of defining the certificate value inline.
        -----BEGIN CERTIFICATE-----
        ...
        -----END CERTIFICATE-----
      privateKey: <samlTestPrivateKey> # An example of using a secret provider to load the private key.
      disableSignedResponse: false
      disableSignedAssertion: false
    clients:
      - name: exampleClient
        audience: https://exampleco.com/
        consumerServiceURL: https://exampleco.com/saml/acs
        duration: 60
        nameIDFormat: urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
        requestVerification:
          skipVerification: true
        authentication:
          idps:
            - azure
        attrProviders: # An optional config to support non-idps to be used in claimsMapping.
          - connector: ldap
            usernameMapping: azure.email
        assertion:
          encryption:
            keyEncryptMethod: RSA_OAEP
            dataEncryptMethod: AES256CBC
            digestMethod: SHA256
            certificate: <encryptionCert>
        claimsMapping:
          name: azure.username
          # Optional claim mappings.
          email: azure.name
          family_name: azure.surname
          given_name: azure.givenname
          phone: ldap.mobile

Service Extensions

The below example shows a complete configuration using default implementation, alongside AuthProvider level and client level Service Extension definitions.

authproviders:
  - name: samlAuthProvider
    type: saml
    metadataEndpoint: https://maverics.com/idp/saml/metadata.xml # optional
    singleSignOnServiceEndpoint: https://maverics.com/idp/saml/sso
    issuer: https://maverics.com/idp/
    signature:
      certificate: |+ # An example of defining the certificate value inline.
        -----BEGIN CERTIFICATE-----
        ...
        -----END CERTIFICATE-----
      privateKey: <samlTestPrivateKey> # An example of using a secret provider to load the private key.
      disableSignedResponse: false
      disableSignedAssertion: false

    # Below are examples of optional Service Extension definitions at the
    # AuthProvider level. These will run for clients which do not use `claimsMapping` 
    # or define their own Service Extensions.
    isAuthenticatedSE:
      funcName: IsAuthenticated
      file: /etc/maverics/extensions/authprovider.go

    authenticateSE:
      funcName: Authenticate
      file: /etc/maverics/extensions/authprovider.go

    buildClaimsSE:
      funcName: BuildClaims
      file: /etc/maverics/extensions/authprovider.go

    clients:
      # An example of a client definition using the default implementations for authentication 
      # and building attributes.
      - name: alphaClient
        audience: https://clientapp.com/audience/
        consumerServiceURL: https://exampleco.com/saml/acs
        duration: 60
        requestVerification:
          skipVerification: true
        authentication:
          idps:
            - azure
        claimsMapping:
          name: azure.objectidentifier
          email: azure.name
          family_name: azure.surname
          given_name: azure.givenname

      # An example of a client definition which falls back to using the AuthProvider
      # level Service Extensions for authentication and building attributes.
      - name: betaClient
        audience: https://clientapp.com/audience/
        consumerServiceURL: https://exampleco.com/saml/acs
        duration: 60
        requestVerification:
          certificate: <fallbackSEClientSigningCert>

      # An example of a client definition which uses its own Service Extensions for 
      # authentication and building attributes.
      - name: gammaClient
        audience: https://clientapp.com/audience/
        consumerServiceURL: https://exampleco.com/saml/acs
        duration: 60
        requestVerification:
          certificate: <seClientSigningCert>
        authentication:
          isAuthenticatedSE:
            funcName: IsAuthenticated
            file: /etc/maverics/extensions/gammaClient.go
              
          authenticateSE:
            funcName: Authenticate
            file: /etc/maverics/extensions/gammaClient.go

        buildClaimsSE:
          funcName: BuildClaims
          file: /etc/maverics/extensions/gammaClient.go

/etc/maverics/extensions/authprovider.go

package main

import (
  "errors"
  "net/http"
  
  "maverics/auth"
  "maverics/log"
  "maverics/session"
)

func IsAuthenticated(sp *auth.SAMLProvider, rw http.ResponseWriter, req *http.Request) bool {
  result := session.GetString(req, "azure.authenticated") == "true"
  log.Debug(
    "msg", "called isAuthnAuthProvider",
    "path", req.URL.Path,
    "result", result,
  )
  return result
}

func Authenticate(sp *auth.SAMLProvider, rw http.ResponseWriter, req *http.Request) error {
  log.Debug("msg", "authenticating user", "path", req.URL.Path)

  idp, ok := sp.IDPs["azure"]
  if !ok {
    return errors.New("missing idp definition for 'azure' in 'idps'")
  }

  // The IDP exists. Attempt to login and proceed to the authz flow.
  log.Debug("msg", "azure IDP exsits, start login", "path", req.URL.Path)
  idp.CreateRequest().Login(rw, req)

  http.Redirect(rw, req, req.URL.Path, 302)

  return nil
}

func BuildClaims(sp *auth.SAMLProvider, rw http.ResponseWriter, req http.Request) (map[string]string, error) {
  return map[string]string{
    "role": session.GetString("ldap.role"),
  }, nil
}

/etc/maverics/extensions/gammaClient.go

package main

import (
  "errors"
  "net/http"

  "maverics/auth"
  "maverics/log"
  "maverics/session"
)

func IsAuthenticated(sp *auth.SAMLProvider, rw http.ResponseWriter, req *http.Request) bool {
  result := session.GetString(req, "azure.authenticated") == "true"
  log.Debug("msg", "called isAuthnClient",
    "path", req.URL.Path,
    "result", result)
  return result
}

func Authenticate(sp *auth.SAMLProvider, rw http.ResponseWriter, req *http.Request) error {
  log.Debug("msg", "authenticating user", "path", req.URL.Path)

  idp, ok := sp.IDPs["azure"]
  if !ok {
    return errors.New("missing idp definition for 'azure' in 'idps'")
  }

  // The IDP exists. Attempt to login and proceed to the authz flow.
  log.Debug("msg", "azure IDP exsits, start login", "path", req.URL.Path)
  idp.CreateRequest().Login(rw, req)

  http.Redirect(rw, req, req.URL.Path, 302)

  return nil
}

func BuildClaims(sp *auth.SAMLProvider, rw http.ResponseWriter, req http.Request) (map[string]string, error) {
  return map[string]string{
    "role": session.GetString("ldap.role"),
  }, nil
}

Limitations

Authn Request

  • ProtocolBinding is a URI reference that identifies the SAML protocol binding to be used when returning the Response message. The supported bindings are:
    • urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST
    • urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect
  • SignatureMethod - SAML assertions and protocols must use enveloped signatures when signing assertions and protocol messages. The SAMLProvider only supports rsa-sha256 and intentionally does not support rsa-sha1 since it is cryptographically insecure.