Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,12 @@ Enable SAML single sign-on by setting the two required variables and mounting yo
|----------|------|---------|-------------|
| `SSO_SAML_IDP_METADATA_FILE` | string | _(empty)_ | Path to the mounted IdP metadata XML (e.g. `/etc/currents/sso/idp-metadata.xml`). **Required to enable SSO.** |
| `SSO_SAML_ISSUER` | string | _(empty)_ | SP entityID / audience — a stable opaque identifier (e.g. `currents-onprem:your-org`). Must match the Audience / SP Entity ID configured in your IdP. **Required to enable SSO.** |
| `SSO_SAML_PROVIDER_ID` | string | `onprem-saml` | Provider id; recommend your IdP name (e.g. `okta`). Becomes the last path segment of the ACS callback URL. |
| `SSO_SAML_PROVIDER_ID` | string | `onprem-saml` | Provider id; recommend your IdP name (e.g. `okta`). Becomes the last path segment of the ACS URL, `<APP_BASE_URL>/api/auth/sso/saml2/sp/acs/<provider id>`. |
| `SSO_SAML_DEFAULT_ROLE` | string | `member` | Role granted to auto-provisioned SSO users |
| `SSO_ALLOWED_DOMAINS` | string | _(empty)_ | Comma-separated email domains allowed to sign in via SSO |
| `SSO_SAML_AUTHN_REQUESTS_SIGNED` | bool | `false` | Sign outbound AuthnRequests (requires the SP key below) |
| `SSO_SAML_SP_KEY_FILE` | string | _(empty)_ | Path to the mounted SP private key PEM (only when signing requests) |
| `SSO_SAML_ALLOW_IDP_INITIATED` | bool | `false` | Accept sign-ins started from the IdP's app dashboard. Off, such a sign-in is refused and users start from the Currents sign-in page |

### Docker Compose Configuration

Expand Down
12 changes: 10 additions & 2 deletions docs/sso-saml.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ SSO turns on automatically once the two required variables are set and the IdP m

You'll reuse these in both your IdP and your `.env`:

- **Provider id** (`SSO_SAML_PROVIDER_ID`) — set it to your IdP's service name, e.g. `okta`. It becomes the last path segment of the callback URL.
- **Provider id** (`SSO_SAML_PROVIDER_ID`) — set it to your IdP's service name, e.g. `okta`. It becomes the last path segment of the ACS URL.
- **Issuer** (`SSO_SAML_ISSUER`) — a **stable, opaque** identifier that names this deployment as a SAML Service Provider. Use the form `currents-onprem:<your-org>`, e.g. `currents-onprem:acme`.

> ⚠️ **The issuer is not a URL.** Use a stable string like `currents-onprem:acme`. It only has to match the "Audience / SP Entity ID" you enter in your IdP — it does not need to resolve, and using your deployment URL is discouraged because that URL can change.
Expand All @@ -25,13 +25,16 @@ Create a new **SAML 2.0** application. Using Okta as the example, set:

| IdP field | Value |
|-----------|-------|
| **Single Sign-On URL** (a.k.a. ACS / Assertion Consumer Service URL) | `<APP_BASE_URL>/api/auth/sso/saml2/callback/okta` |
| **Single Sign-On URL** (a.k.a. ACS / Assertion Consumer Service URL) | `<APP_BASE_URL>/api/auth/sso/saml2/sp/acs/okta` |
| **Recipient URL** and **Destination URL** | The Single Sign-On URL. In Okta, leave "Use this for Recipient URL and Destination URL" checked (the default) |
| **Audience URI** (a.k.a. SP Entity ID) | `currents-onprem:acme` |
| **Name ID format** | `Persistent` |
| **Application username** | Email |

Replace `<APP_BASE_URL>` with your deployment's base URL (the value of `APP_BASE_URL` in your `.env`, e.g. `https://currents.acme.com`), `okta` with your `SSO_SAML_PROVIDER_ID`, and `currents-onprem:acme` with your `SSO_SAML_ISSUER`.

Currents rejects an assertion whose Recipient or Destination is any URL other than the Single Sign-On URL.

Then **download the application's IdP metadata XML** (in Okta: the app's *Metadata URL*, or *SAML Setup Instructions → Identity Provider metadata*). This one file contains the IdP sign-on URL, entity ID, and signing certificate — Currents reads all of them from it.

## Step 3 — Provide the metadata file
Expand Down Expand Up @@ -68,6 +71,8 @@ Go to your dashboard and enter your email on the sign-in screen. If your email i

> New SSO users are added to your single on-prem organization automatically, with the role set by `SSO_SAML_DEFAULT_ROLE` (default `member`). The root admin account continues to sign in with its email + password.

Once SSO is on, Currents serves its SP metadata at `<APP_BASE_URL>/api/auth/sso/saml2/sp/metadata?providerId=okta` (with your `SSO_SAML_PROVIDER_ID`), for IdPs that import it.

## Optional configuration

| Variable | Purpose |
Expand All @@ -76,6 +81,7 @@ Go to your dashboard and enter your email on the sign-in screen. If your email i
| `SSO_ALLOWED_DOMAINS` | Comma-separated allow-list of email domains, e.g. `acme.com,acme.io`. Defense-in-depth on top of your IdP. |
| `SSO_SAML_AUTHN_REQUESTS_SIGNED` | Set `true` to sign outbound AuthnRequests. Requires an SP key (below). |
| `SSO_SAML_SP_KEY_FILE` | Path to your SP private key PEM (place it in `./data/sso` too). Only needed for signed requests. |
| `SSO_SAML_ALLOW_IDP_INITIATED` | Set `true` to accept sign-ins started from the IdP's app dashboard (an Okta tile, the Entra My Apps portal). Off by default: Currents can't match such a sign-in to a request of its own, so it refuses it and users start from the Currents sign-in page. |

### Signing AuthnRequests (advanced)

Expand All @@ -101,5 +107,7 @@ The metadata file is the single source for the IdP sign-on URL, entity ID, and s
- **Sign-in isn't redirecting to the IdP** — confirm both `SSO_SAML_IDP_METADATA_FILE` and `SSO_SAML_ISSUER` are set and the file exists at the mounted path; SSO is off until both are present. Check the API logs at startup for SSO config validation errors.
- **IdP rejects the request / "audience mismatch"** — the **Audience / SP Entity ID** in your IdP must exactly equal `SSO_SAML_ISSUER`.
- **Assertion signature invalid** — the metadata XML is stale or from the wrong app; re-download it from the IdP and replace the file.
- **"Recipient does not match" or "Destination does not match"** — the Recipient or Destination URL in your IdP is not the Single Sign-On URL above. Set both to it.
- **Sign-in from the IdP's app dashboard fails, sign-in from Currents works** — set `SSO_SAML_ALLOW_IDP_INITIATED=true`, or have users start from the Currents sign-in page.

See the [Configuration Reference](./configuration.md#saml-sso-optional) for the full list of SSO variables.
4 changes: 3 additions & 1 deletion on-prem/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ OAUTH_DYNAMIC_CLIENT_REGISTRATION_ENABLED=true
#
# Setup summary:
# 1. Create a SAML app in your IdP with:
# ACS / Single Sign-On URL: <APP_BASE_URL>/api/auth/sso/saml2/callback/okta
# ACS / Single Sign-On URL: <APP_BASE_URL>/api/auth/sso/saml2/sp/acs/<SSO_SAML_PROVIDER_ID>
# (also the Recipient and Destination URL)
# Audience / SP Entity ID: currents-onprem:your-org
# 2. mkdir -p ./data/sso, then download the app's metadata XML to
# ./data/sso/idp-metadata.xml (mounted read-only to /etc/currents/sso).
Expand All @@ -200,6 +201,7 @@ OAUTH_DYNAMIC_CLIENT_REGISTRATION_ENABLED=true
# SSO_ALLOWED_DOMAINS=acme.com,acme.io # restrict SSO to these email domains
# SSO_SAML_AUTHN_REQUESTS_SIGNED=false # sign AuthnRequests (requires the SP key below)
# SSO_SAML_SP_KEY_FILE=/etc/currents/sso/sp-key.pem
# SSO_SAML_ALLOW_IDP_INITIATED=false # accept sign-ins started from the IdP's app dashboard

# =============================================================================
# Docker Compose Configuration
Expand Down
10 changes: 9 additions & 1 deletion on-prem/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

### Breaking Changes
- **SAML SSO: the ACS URL moves** from `<APP_BASE_URL>/api/auth/sso/saml2/callback/<provider id>` to `<APP_BASE_URL>/api/auth/sso/saml2/sp/acs/<provider id>`. The old path answers `404`, so SSO sign-in fails until your IdP sends assertions to the new one. The root admin's email and password sign-in is unaffected. Schedule the IdP change for the upgrade window:
1. In the IdP's SAML app, set the Single Sign-On / ACS URL (Entra ID: Reply URL) to the new URL. Set the Recipient and Destination URLs to the new URL as well. In Okta, leave "Use this for Recipient URL and Destination URL" checked (the default) and both follow the Single Sign-On URL. Currents rejects an assertion whose Recipient or Destination is any other URL.
2. Keep the Audience / SP Entity ID. It must still equal `SSO_SAML_ISSUER`, which doesn't change.
3. Sign in from the Currents sign-in page to confirm.
- **SAML SSO: sign-ins started from the IdP's app dashboard** (an Okta tile, the Entra My Apps portal) are refused unless `SSO_SAML_ALLOW_IDP_INITIATED=true`. Sign-ins started from the Currents sign-in page work either way.

### Upgrade Notes
- The scheduler runs this release's database migrations when it starts, which can take several minutes on a large dataset. Check that the scheduler's last startup in `docker compose logs scheduler` ends with `Startup tasks complete` (see [Database migration issues](../docs/upgrading.md#database-migration-issues)). If it logs an error instead, such as a ClickHouse `Timeout error.`, the remaining migrations don't run until the scheduler starts again: run `docker compose restart scheduler`. Finished migrations are skipped, and the run backfills skip the runs they already updated.
- The scheduler runs this release's database migrations when it starts, which can take several minutes on a large dataset. This is not downtime: the dashboard, API and run recording keep working. Until the migrations finish, filtering runs by status and searching by CI build ID can leave out runs recorded before the upgrade, and new results can take longer to process. Check that the scheduler's last startup in `docker compose logs scheduler` ends with `Startup tasks complete` (see [Database migration issues](../docs/upgrading.md#database-migration-issues)). If it logs an error instead, such as a ClickHouse `Timeout error.`, the remaining migrations don't run until the scheduler starts again: run `docker compose restart scheduler`. Finished migrations are skipped, and the run backfills skip the runs they already updated.

### Compose File Changes
- The bundled RustFS image is `rustfs/rustfs:1.0.1` (requires `./scripts/generate-compose.sh` if using custom templates)
Expand All @@ -20,6 +27,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- `MCP_ENABLED` (default `true`), `MCP_ORIGINS` — remote MCP at `/mcp`
- `OAUTH_PROVIDER_ENABLED`, `OAUTH_DYNAMIC_CLIENT_REGISTRATION_ENABLED`, `OAUTH_CIMD_ENABLED`, `OAUTH_HTTP_ALLOWED_HOSTS`, `OAUTH_JWKS_URL` — sign-in to remote MCP with a Currents account. `.env.example` turns the provider on; an upgraded `.env` without `OAUTH_PROVIDER_ENABLED=true` keeps it off. It needs an `https` `APP_BASE_URL`, or `http` on `localhost` (or, for an evaluation, a `.localhost` name in `OAUTH_HTTP_ALLOWED_HOSTS`, with `OAUTH_JWKS_URL` on `localhost`): with `http` on any other host the API refuses to start, so set `OAUTH_PROVIDER_ENABLED=false`
- `API_BASE_URL` — public API origin, default `APP_BASE_URL`
- `SSO_SAML_ALLOW_IDP_INITIATED` — accept SAML sign-ins started from the IdP's app dashboard. Default `false`
- `DC_RUSTFS_DELETE_ARTIFACTS_AFTER_DAYS` — delete artifacts in the bundled RustFS after this many days. Unset keeps them

### Changed Environment Variables
Expand Down
Loading