How to Configure Single Sign-On (SSO) using SAML or OIDC with OpenWork

Enterprises configure SSO in OpenWork by registering a SAML or OIDC provider through the Den API, verifying domain ownership via DNS TXT records, and enabling automatic discovery or manual endpoint configuration.

OpenWork, developed under the different-ai/openwork repository, implements enterprise Single Sign-On through its Den API service. The architecture separates provider registration, connection metadata storage, and domain verification into distinct stages, enabling secure integration with corporate identity providers. This guide details the exact API endpoints, source file locations, and security policies required to configure SSO using SAML or OIDC protocols.

Architecture and Prerequisites

OpenWork stores SSO configuration across two database tables managed by the Den API: the SsoProvider table holds the Better Auth provider credentials, while the SsoConnection table maintains organizational metadata including issuer, domain, and endpoint URLs. Before configuring SSO, the system validates that the workspace holds the appropriate enterprise entitlement.

Verify Enterprise Entitlements

Only workspaces with the sso entitlement can manage SSO connections. The API rejects unauthorized requests early in the middleware chain via the checkEntitlement(..., "sso") function defined in ee/apps/den-api/src/routes/org/sso.ts.

Configuring SAML SSO

SAML integration requires registering the identity provider, retrieving Service Provider metadata, and enforcing strict security policies.

Register the SAML Provider

Send a POST request to /v1/sso/saml with the identity provider's metadata. The route handler in ee/apps/den-api/src/routes/org/sso.ts validates the payload using Zod, then invokes registerOrganizationSsoConnection, which calls auth.api.registerSSOProvider to persist the configuration in the SsoProvider table.

curl -X POST https://<openwork-host>/api/v1/sso/saml \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "issuer": "https://saml-idp.example.com",
    "domain": "example.com",
    "entryPoint": "https://saml-idp.example.com/sso",
    "cert": "MIID...AB",
    "audience": "https://app.openwork.com"
}'

Retrieve SAML Service Provider Metadata

After registration, obtain the SP metadata to upload to your identity provider by calling GET /v1/sso/metadata?format=xml (or json). This endpoint, also defined in ee/apps/den-api/src/routes/org/sso.ts, returns the metadata generated by Better Auth.

curl -H "Authorization: Bearer <admin-token>" \
  "https://<openwork-host>/api/v1/sso/metadata?format=xml"

Configuring OIDC SSO

OIDC configuration supports automatic discovery of endpoints or manual specification for air-gapped environments.

Register the OIDC Provider

Submit a POST request to /v1/sso/oidc with the issuer URL, client credentials, and desired scopes. The handler processes the request through the same registerOrganizationSsoConnection flow in ee/apps/den-api/src/sso.ts.

curl -X POST https://<openwork-host>/api/v1/sso/oidc \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "issuer": "https://login.microsoftonline.com/<tenant-id>/v2.0",
    "domain": "example.com",
    "clientId": "<client-id>",
    "clientSecret": "<client-secret>",
    "scopes": ["openid","email","profile"],
    "skipDiscovery": false
}'

OIDC Discovery and Endpoint Resolution

When skipDiscovery is false (default), OpenWork automatically fetches the discovery document from /.well-known/openid-configuration and validates that the issuer matches the configured value. If discovery is disabled, you must supply authorizationEndpoint, tokenEndpoint, and jwksEndpoint. This logic resides in the resolveOidcEndpoints function within ee/apps/den-api/src/sso.ts.

Domain Verification Workflow

Before users can authenticate, OpenWork verifies domain ownership to prevent unauthorized SSO claims.

Request a DNS Verification Token

Call POST /v1/sso/request-domain-verification to receive a unique DNS TXT token. The endpoint is implemented in ee/apps/den-api/src/routes/org/sso.ts.

curl -X POST https://<openwork-host>/api/v1/sso/request-domain-verification \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{"providerId":"openwork-sso-42"}'

Publish the returned token as a TXT record under _openwork-sso.example.com.

Verify Domain Ownership

Once DNS propagates, confirm ownership by calling POST /v1/sso/verify-domain. A 204 No Content response indicates successful verification, enabling the connection for production use.

curl -X POST https://<openwork-host>/api/v1/sso/verify-domain \
  -H "Authorization: Bearer <admin-token>"

SAML Security Policies

OpenWork enforces strict SAML security defaults defined in ee/apps/den-api/src/sso-saml-policy.ts. These constants are referenced during provider creation in registerBetterAuthSsoProvider within ee/apps/den-api/src/sso.ts:

  • Signed Assertions Required: ORGANIZATION_SAML_WANT_ASSERTIONS_SIGNED is set to true, rejecting unsigned SAML assertions.
  • IdP-Initiated Logins Allowed: ORGANIZATION_SAML_ALLOW_IDP_INITIATED permits identity provider-initiated authentication flows.
  • Timestamp Enforcement: ORGANIZATION_SAML_REQUIRE_TIMESTAMPS ensures strict validation of NotBefore and NotOnOrAfter conditions.
  • Algorithm Security: ORGANIZATION_SAML_DEPRECATED_ALGORITHM_BEHAVIOR is set to "reject", blocking signatures using weak hash algorithms.

Initiating User Authentication

End-users begin SSO authentication by navigating to /sso/:orgSlug. This route utilizes the startOrganizationSsoSignIn helper in ee/apps/den-api/src/sso.ts, which forwards the request to auth.api.signInSSO (Better Auth) to initiate the SAML or OIDC flow. After successful identity provider validation, users are redirected to the organization's callback URL.

Summary

  • Entitlement enforcement: The sso entitlement is verified via checkEntitlement in ee/apps/den-api/src/routes/org/sso.ts before processing any configuration requests.
  • Provider registration: Use POST /v1/sso/saml or POST /v1/sso/oidc, handled by registerOrganizationSsoConnection and auth.api.registerSSOProvider in ee/apps/den-api/src/sso.ts.
  • SAML security: Strict defaults in ee/apps/den-api/src/sso-saml-policy.ts require signed assertions, timestamps, and reject deprecated algorithms.
  • OIDC flexibility: Automatic discovery via resolveOidcEndpoints or manual endpoint configuration using skipDiscovery.
  • Domain trust: DNS TXT verification via POST /v1/sso/request-domain-verification and POST /v1/sso/verify-domain prevents domain hijacking.

Frequently Asked Questions

Does OpenWork support both IdP-initiated and SP-initiated SAML flows?

Yes. The ORGANIZATION_SAML_ALLOW_IDP_INITIATED constant in ee/apps/den-api/src/sso-saml-policy.ts is set to true by default, allowing identity providers to initiate login flows while maintaining security through strict assertion validation and audience checks.

What happens if OIDC discovery fails during provider registration?

If skipDiscovery is false (the default), OpenWork attempts to fetch the discovery document from /.well-known/openid-configuration using the resolveOidcEndpoints function in ee/apps/den-api/src/sso.ts. If discovery fails, registration returns an error. Administrators can set skipDiscovery: true and manually provide authorizationEndpoint, tokenEndpoint, and jwksEndpoint to bypass discovery.

Where is the SSO provider data stored in OpenWork?

Provider credentials and protocol-specific settings are stored in the SsoProvider table, while organizational connection details—including domain, verification status, and issuer metadata—are maintained in the SsoConnection table. These records are created and managed by the business logic in ee/apps/den-api/src/sso.ts.

How do I obtain the Service Provider metadata for my SAML identity provider?

Make an authenticated GET request to /v1/sso/metadata?format=xml (or format=json) as defined in ee/apps/den-api/src/routes/org/sso.ts. This returns the SP metadata generated by Better Auth, which you must upload to your SAML IdP to establish the trust relationship.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →