Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ee): saml idp initiated sso #2046

Merged
merged 7 commits into from
Feb 13, 2025
Merged

Conversation

lfleischmann
Copy link
Member

@lfleischmann lfleischmann commented Feb 11, 2025

Description

The current implementation for SAML SSO cannot handle IDP initiated SSO. The SAML callback handler (in SAML speak: ACS - Assertion Consumer Service) assumes a state (in SAML speak: RelayState) that is created by the Hanko backend and reflected in the callback response from the IDP on SP initated SSO only.

Implementation

Changes to the SAML Relay State on SP initiated flows

  • We need a way to distinguish between SP initiated and IDP initated requests to the callback endpoint. These changes add a prefix to the RelayState on SP initiated SSO so that we can assure that any response to the callback endpoint with the prefix is SP initated and any response without the prefix is IDP initiated.

Changes to the SAML callback handler

  • As soon as it is known that a request is IDP initiated the callback handler executes IDP initated flow specific logic. Just as in the SP initiated callback handler logic, the SAML response is parsed and validated by the github.com/russellhaering/gosaml2 library. But the SAML core specification and SAML security considerations indicate that we also should prohibit using SP initated responses as unsolicited IDP responses and replay of (IDP initiated) SAML responses. Both of these measures require access to some nodes/values of the SAML response that the above mentioned library unfortunately does not provide in an easily accessible manner through its API. As a result these changes parse the response in addition to the parsing (and validation) done by the library to extract information that help implement these protections.

Protecting against SP initated response use

  • The changes check if the root SAML Response node has an InResponseTo attribute. If it does, then we assume it originates from a SP initated flow and hence should not be accepted.

Protecting against replay attacks

  • The changes record every IDP iniated request in the database. A new DB table saml_idp_initated_requests is added for this purpose. It stores the ID of a SAML response and the issuer/provider entity ID discernible from the response. For any incoming IDP initated request, the ID attribute of the root SAML Response node is extracted. The saml_idp_initated_requests is queried for this ID and the response issuer/provider entity ID. If an entry already exists the request is considered a replay. If it is not a new record in the table is persisted.

Changes to SAML callback redirect URL

  • In case of an IDP initiated flow, the redirect URL to the client app (using Hanko Elements) now contains a saml_hint to indicate that the token exchange occurs in an IDP initated flow context.
    Hanko elements uses this hint to initiate a token exchange flow (see next section for this new flow).

Changes to built-in flows

  • The changes add a new token exchange flow. It initializes on the thirdparty state offering only an ExchangeToken action. A handler for this flow is only applied if SAML is enabled in the configuration.

Changes to Hanko Elements

  • Elements now uses the aforementioned saml_hint to determine if it must initialize and advance the new token exchange flow.

Changes to ExchangeToken action

  • Skipping email verification can be configured per SAML provider. These changes assume that this configuration takes precedence over the general Email.RequireVerification configuration. But the current implementation of the ExchangeToken action only checks the latter configuration in order to determine if a verification is required after exchaning a token. These changes modify the behaviour so that if the SAML provider for the identity is configured with skip_email_verification set to true no email verification process is triggered.

How to test

You can use https://mocksaml.com for testing locally without having to configure "real" IDP.

  1. Start the backend with the following configuration:

    saml:
      enabled: true
      endpoint_url: http://localhost:8000
      audience_uri: http://localhost:8000
      allowed_redirect_urls:
        - http://localhost:8888**
      default_redirect_url: http://localhost:8888
      identity_providers:
        - name: MockSaml
          domain: example.com
          metadata_url: https://mocksaml.com/api/saml/metadata
          enabled: true
          attribute_map:
            "email": "email"

    or to test email verification changes:

    saml:
      enabled: true
      endpoint_url: http://localhost:8000
      audience_uri: http://localhost:8000
      allowed_redirect_urls:
        - http://localhost:8888**
      default_redirect_url: http://localhost:8888
      identity_providers:
        - name: MockSaml
          domain: example.com
          metadata_url: https://mocksaml.com/api/saml/metadata
          enabled: true
          attribute_map:
            "email": "email"
          skip_email_verification: true
  2. Start an example app from the frontend/examples folder.

  3. Trigger SP initiated login by entering an email address with the configured domain as a login identifier in the frontend example app or go to https://mocksaml.com, click "Test IdP Login" and then provide http://localhost:8000/saml/callback as the ACS URL and http://localhost:8000 as the Audience.

@lfleischmann lfleischmann marked this pull request as ready for review February 11, 2025 19:50
@lfleischmann lfleischmann changed the title feat(ee): saml idp initated sso feat(ee): saml idp initiated sso Feb 11, 2025
backend/ee/saml/handler.go Outdated Show resolved Hide resolved
backend/ee/saml/handler.go Outdated Show resolved Hide resolved
backend/ee/saml/state.go Outdated Show resolved Hide resolved
@lfleischmann lfleischmann merged commit 983000d into main Feb 13, 2025
8 checks passed
@lfleischmann lfleischmann deleted the fix-saml-idp-initiaded-sso branch February 13, 2025 11:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: ✅ Recently closed
Development

Successfully merging this pull request may close these issues.

2 participants