# How to Configure SAML Single Sign-On (SSO) in Stirling-PDF

> Easily configure SAML single sign-on SSO in Stirling-PDF. Secure your application by integrating with IdPs like Keycloak, Okta, or Azure AD for seamless user access. Follow our simple guide for quick setup.

- Repository: [Stirling Tools/Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF)
- Tags: how-to-guide
- Published: 2026-03-01

---

**To configure SAML single sign-on (SSO) in Stirling-PDF, enable the feature in [`settings.yml`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/settings.yml) by setting `security.saml2.enabled: true`, provide your Identity Provider certificate and Service Provider key pair, and set `loginMethod: saml2` to redirect users to your IdP (e.g., Keycloak, Okta, or Azure AD).**

Stirling-PDF supports enterprise-grade **SAML 2.0** authentication through its Spring Boot 3 security layer, allowing organizations to integrate with corporate identity providers. This guide demonstrates how to wire the `ApplicationProperties` configuration class to activate SSO, using actual implementation details from the Stirling-Tools/Stirling-PDF repository.

## Prerequisites for SAML 2.0 Integration

Before modifying configuration files, ensure your environment meets these requirements:

- **Enterprise License**: The SAML provider is gated behind the Enterprise tier (`AuthenticationType.SAML2`).
- **Identity Provider**: An IdP supporting SAML 2.0 (Keycloak, Okta, Azure AD, etc.).
- **Cryptographic Material**: 
  - IdP signing certificate (`idpCert`)
  - Service Provider private key (`privateKey`)
  - Service Provider public certificate (`spCert`)
- **Backend URL**: The public URL where Stirling-PDF is reachable (`system.backendUrl`), required for generating metadata and Assertion Consumer Service (ACS) endpoints.

## Enabling SAML SSO in settings.yml

Add the following configuration to **[`settings.yml`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/settings.yml)** (or inject via environment variables/Docker `-e` flags):

```yaml
security:
  saml2:
    enabled: true              # Activates the SAML2AuthenticationProvider

    provider: keycloak         # Display name for the UI

    registrationId: stirling   # Must match the RelyingPartyRegistration ID

    autoCreateUser: true       # Provisions local accounts on first login

    blockRegistration: false   # Disables self-service registration via SAML

    idpMetadataUri: "https://idp.example.com/realms/stirling/protocol/saml/descriptor"
    idpCert: "/opt/stirling/config/idp-signing-cert.pem"
    privateKey: "/opt/stirling/config/sp-private-key.key"
    spCert: "/opt/stirling/config/sp-public-cert.crt"
    idpIssuer: "https://idp.example.com/realms/stirling"
    idpSingleLoginUrl: "https://idp.example.com/realms/stirling/protocol/saml"
    idpSingleLogoutUrl: "https://idp.example.com/realms/stirling/protocol/saml"

system:
  backendUrl: "https://pdf.mycompany.com"

loginMethod: saml2           # Optional: forces SAML-only authentication

```

*The fields above map directly to the `ApplicationProperties.Security.SAML2` inner class defined in [`app/common/src/main/java/stirling/software/common/model/ApplicationProperties.java`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/app/common/src/main/java/stirling/software/common/model/ApplicationProperties.java) (lines 266‑338).*

To restrict authentication to SAML only, set the global login method as shown above. The `isSaml2Active()` method in `ApplicationProperties` evaluates this flag to determine whether to display the SAML login button.

## How the Configuration Is Consumed

The SAML integration relies on two primary components in the proprietary module:

- **`ApplicationProperties`**: Holds the POJO representation of your YAML settings. The `SAML2` inner class defines getters for `enabled`, `idpCert`, `privateKey`, and other binding properties (lines 260‑340).

- **`Saml2Configuration`**: Located at [`app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/Saml2Configuration.java`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/app/proprietary/src/main/java/stirling/software/proprietary/security/saml2/Saml2Configuration.java), this Spring `@Configuration` class:
  - Conditionally loads only when `security.saml2.enabled=true` (lines 28‑31).
  - Loads certificates via `CertificateUtils` and constructs a `RelyingPartyRegistration` using URLs derived from `system.backendUrl` (lines 102‑118).
  - Registers an `OpenSaml5AuthenticationRequestResolver` that injects unique `AuthnRequest` IDs and relay state (lines 69‑80).

Once the YAML is present and the application restarts, the SAML service provider is automatically wired into the Spring Security filter chain.

## Testing SAML Locally with Keycloak

Stirling-PDF provides a complete local testing stack under `testing/compose/`:

| File | Purpose |
|------|---------|
| [`testing/compose/docker-compose-keycloak-saml.yml`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/testing/compose/docker-compose-keycloak-saml.yml) | Spins up a pre-configured Keycloak container with the Stirling-PDF SAML client. |
| [`testing/compose/keycloak-realm-saml.json`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/testing/compose/keycloak-realm-saml.json) | Realm definition containing clients, users, and certificates. |
| [`testing/compose/start-saml-test.sh`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/testing/compose/start-saml-test.sh) | Generates self-signed certificates and orchestrates container startup. |

Execute the test environment:

```bash
cd testing/compose
./start-saml-test.sh --auto

```

The script performs the following actions:

1. Generates an SP key pair (`saml-private-key.key` / `saml-public-cert.crt`).
2. Launches Keycloak with the `KEYCLOAK_SAML` service profile.
3. Exports the IdP metadata to `http://localhost:9080/realms/stirling-saml/protocol/saml/descriptor`.

The compose file automatically injects the required environment variables:

```yaml
SECURITY_SAML2_ENABLED: "true"
SECURITY_SAML2_PROVIDER: "keycloak"
SECURITY_SAML2_REGISTRATIONID: "keycloak"
SECURITY_SAML2_IDP_ISSUER: "http://localhost:9080/realms/stirling-saml"
SECURITY_SAML2_IDP_CERT: "/app/keycloak-saml-cert.pem"
SECURITY_SAML2_PRIVATEKEY: "/app/saml-private-key.key"
SECURITY_SAML2_SP_CERT: "/app/saml-public-cert.crt"
SECURITY_SAML2_SP_ENTITYID: "http://localhost:8080"
SECURITY_SAML2_SP_ACS: "http://localhost:8080/login/saml2/sso/keycloak"

```

Verify the flow by navigating to `http://localhost:8080` and selecting the **SAML2** login option, which redirects to the Keycloak authentication page.

## Troubleshooting Common SAML Issues

| Symptom | Root Cause | Resolution |
|---------|------------|------------|
| **"SAML2 IdP certificate not found"** | The `idpCert` path in [`settings.yml`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/settings.yml) points to a missing file or uses an incorrect classpath prefix. | Verify the file exists at the specified absolute path and is readable by the container process. |
| **Login button missing** | The `loginMethod` property excludes `saml2` or `security.saml2.enabled` is `false`. | Set `loginMethod: saml2` (or `all`) and ensure the enabled flag is `true`. |
| **Metadata URL returns 404** | The `idpMetadataUri` is unreachable or uses an incorrect scheme. | For local testing, use the HTTP descriptor URL; for production, ensure the HTTPS endpoint is publicly accessible. |
| **User auto-creation fails** | The `autoCreateUser` flag is disabled or the Enterprise license is inactive. | Enable `autoCreateUser: true` and verify your license status via the application health endpoint. |

Enable `DEBUG` logging for the `stirling.software` package to view detailed request/response traces from `Saml2Configuration` and `CustomSaml2AuthenticationSuccessHandler`.

## Summary

- **SAML 2.0 requires an Enterprise license** and is configured exclusively through [`settings.yml`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/settings.yml) properties mapped to `ApplicationProperties.Security.SAML2`.
- **Four cryptographic files are mandatory**: IdP signing certificate, SP private key, SP public certificate, and the IdP metadata URI.
- **The `Saml2Configuration` bean** constructs the Spring Security `RelyingPartyRegistration` only when `security.saml2.enabled=true`.
- **Local testing is supported** via [`testing/compose/docker-compose-keycloak-saml.yml`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/testing/compose/docker-compose-keycloak-saml.yml) and the [`start-saml-test.sh`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/start-saml-test.sh) helper script.
- **The `loginMethod: saml2` setting** forces exclusive SSO authentication and hides native login forms.

## Frequently Asked Questions

### Is SAML 2.0 available in the open-source version of Stirling-PDF?

No. According to the source code in [`ApplicationProperties.java`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/ApplicationProperties.java), the SAML2 authentication type is gated behind the Enterprise tier. The `Saml2Configuration` class resides in the `app/proprietary` module, which requires a valid enterprise license to activate.

### What Identity Providers are compatible with Stirling-PDF SAML configuration?

Any IdP conforming to the SAML 2.0 specification is compatible, including **Keycloak**, **Okta**, **Azure Active Directory**, and **Auth0**. The configuration in [`settings.yml`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/settings.yml) accepts standard metadata descriptors and single sign-on/logout URLs regardless of the vendor.

### How do I generate the required SAML certificates for Stirling-PDF?

For production, obtain certificates from your organization's Certificate Authority or IdP. For local development, use the [`start-saml-test.sh`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/start-saml-test.sh) script in `testing/compose/`, which automatically generates a self-signed SP key pair using OpenSSL. The script places `saml-private-key.key` and `saml-public-cert.crt` in the compose directory for immediate use.

### Why does the SAML login redirect fail after successful authentication?

This typically indicates a mismatch between the `system.backendUrl` value and the actual public URL. The `Saml2Configuration` class uses this property to construct the ACS URL (lines 102‑110). If the backend URL is omitted, the application falls back to request-derived URLs, which often fail behind reverse proxies or load balancers. Explicitly set `system.backendUrl` to the canonical HTTPS address.