Skip to main content

The SAML SSO round-trip

These four operations are the service-provider side of SAML 2.0 SSO — the route an organization’s IdP administrators walk to wire Okta, Entra ID, or similar into Orbit’s sign-in. The page’s {orgSlug} path segment selects the SAML configuration; it is a URL-safe organization slug, not an API-key credential, and the operations are deliberately unauthenticated because they begin a session rather than require one. Sequence the round-trip like this. (1) GET /auth/saml/{orgSlug}/metadata returns the service-provider XML the IdP admin imports to register Orbit: entity ID, ACS URL, and signing details. (2) GET /auth/saml/{orgSlug}/login?redirect=/settings/team issues the SP-initiated AuthnRequest and 302-redirects the browser to the IdP’s SSO URL; any redirect value is signed into RelayState and sweeps the post-auth destination back. (3) The IdP POSTs the signed SAMLResponse to /auth/saml/{orgSlug}/callback — the Assertion Consumer Service verifies signature, audience, and the replay window, provisions the user on first login, then 302-redirects to the dashboard sign-in ticket that mints the session cookie. (4) GET /auth/saml/{orgSlug}/logout?nameId=<subject> builds a LogoutRequest for the IdP’s SLO endpoint when single logout is configured; otherwise it answers 200 with a logged_out status, and the local ceremony destroys the session. A worked check-in sequence in cURL:
cURL
The four calls above do not return the normal JSON envelope — skip the read-ahead envelope check for them. Success on this page is a 302 redirect (login, logout, and the IdP-driven callback) or a Content-Type: application/xml metadata download (metadata), not a { "data": … } body. Only failures come back in the standard { "error": …, "meta": { "request_id" } } envelope (see the error shapes table) — or, when single logout is not configured on the org, logout answers 200 with { "data": { "status": "logged_out" } }, the one non-error JSON body on this page.
The first event and the first callback are both redirect-only flows — do not call them from your application; point users at them or let the IdP drive the callback.

Same round-trip from an SDK escape hatch

The SAML ceremonies are browser-first, so no SDK ships a typed helper for them — the Node, Python, and Go SDKs all reach the same four routes through their generic request escape hatch (orbit.request, client.request, client.Request). You keep the SDK’s auth, retries, and base URL against these raw paths. The practical server-side uses are the two machine-readable steps — fetch the SP metadata XML to ship to the IdP admin, and probe the login redirect in a smoke check (the client must not auto-follow the 302 because it targets the IdP, not Orbit). The callback remains the IdP’s own POST; there is nothing to call from your side.
Everything else in the round-trip (the browser hop to the IdP, the IdP’s signed ACS POST, the Clerk ticket swap) happens between the user’s browser and the IdP — the SDK can fetch metadata and probe the redirect, but it cannot stand in for the browser ceremony.

Error shapes

Failures land in the standard { "error": …, "meta": { "request_id", "timestamp" } } envelope — meta.request_id is the one handle support needs. The request ids pinned below (req_auth_saml_login, req_auth_saml_logout, req_auth_saml_callback, req_auth_saml_metadata) are worked examples of that handle, one per operation. The envelope’s error.code is the machine-readable discriminator; the callback rejections also carry a details.reason for the exact check that failed. Plan for these on the round-trip: ERR_BAD_RELAY_STATE, AUTH_METADATA_MISMATCH, and CHALLENGE_TERMINAL are the three rejection classes the onboarding round-trip hits most — RelayState forgery/staleness on the ACS POST, IdP-side metadata drift, and a second submit of a single-use ceremony. All three recover the same way: never retry the same payload, restart GET …/login.