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_SIGNEDis set totrue, rejecting unsigned SAML assertions. - IdP-Initiated Logins Allowed:
ORGANIZATION_SAML_ALLOW_IDP_INITIATEDpermits identity provider-initiated authentication flows. - Timestamp Enforcement:
ORGANIZATION_SAML_REQUIRE_TIMESTAMPSensures strict validation ofNotBeforeandNotOnOrAfterconditions. - Algorithm Security:
ORGANIZATION_SAML_DEPRECATED_ALGORITHM_BEHAVIORis 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
ssoentitlement is verified viacheckEntitlementinee/apps/den-api/src/routes/org/sso.tsbefore processing any configuration requests. - Provider registration: Use POST
/v1/sso/samlor POST/v1/sso/oidc, handled byregisterOrganizationSsoConnectionandauth.api.registerSSOProviderinee/apps/den-api/src/sso.ts. - SAML security: Strict defaults in
ee/apps/den-api/src/sso-saml-policy.tsrequire signed assertions, timestamps, and reject deprecated algorithms. - OIDC flexibility: Automatic discovery via
resolveOidcEndpointsor manual endpoint configuration usingskipDiscovery. - Domain trust: DNS TXT verification via POST
/v1/sso/request-domain-verificationand POST/v1/sso/verify-domainprevents 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →