//SSO via OIDC

Single Sign-On via OIDC

Connect enterprise identity providers using OpenID Connect (OIDC) / OAuth 2.0.

All management endpoints require Authorization: Bearer <admin-jwt>.

Connection Management

POST /api/admin/organizations/:org_uuid/oidc-connections

Creates a new OIDC connection for an organization.

Input: ~~~json { "name": "Acme Google Workspace", "provider": "google", "client_id": "your-google-client-id", "client_secret": "your-google-client-secret", "discovery_url": "https://accounts.google.com/.well-known/openid-configuration", "allowed_domains": ["acme.com"] } ~~~

Output: ~~~json { "connection_uuid": "conn-uuid-123", "redirect_uri": "https://api.buttrbase.com/auth/oidc/conn-uuid-123/callback" } ~~~

Register the redirect_uri in your identity provider's application settings.

GET /api/admin/organizations/:org_uuid/oidc-connections

Lists all OIDC connections for an organization.

DELETE /api/admin/organizations/:org_uuid/oidc-connections/:connection_uuid

Deletes an OIDC connection.

Login Flow

GET /api/auth/oidc/:connection_uuid/authorize

Initiates the OIDC login flow. Redirect the user's browser to this URL.

Query Parameters:

  • redirect_uri — where to send the user after login.
  • state — opaque value for CSRF protection (recommended).
  • Response: 302 redirect to the identity provider's authorization endpoint.

    GET /api/auth/oidc/:connection_uuid/callback

    The callback endpoint that handles the authorization code from the identity provider.

    On success: Redirects to your redirect_uri with a session_token query parameter.

    SDK Examples

    Python

    ~~~python url = client.oidc_authorize_url("conn-uuid-123")

    redirect user to url["redirect_url"]

    On callback:

    resp = client.oidc_callback({"code": "auth-code", "state": "state-value"}) ~~~

    Node.js / TypeScript

    ~~~typescript const url = await client.oidcAuthorizeUrl('conn-uuid-123'); // redirect user to url.redirect_url

    // On callback: const resp = await client.oidcCallback('conn-uuid-123', { code: 'auth-code', state: 'state-value' }); ~~~

    Go

    ~~~go url, err := client.OidcAuthorizeURL(ctx, "conn-uuid-123") // redirect user to url["redirect_url"]

    // On callback: resp, err := client.OidcCallback(ctx, "conn-uuid-123", map[string]any{"code": "auth-code"}) ~~~

    Rust

    ~~~rust let auth = client.oidc_authorize_url("conn-uuid-123").await?; // redirect user to auth["redirect_url"]

    // On callback: let mut params = std::collections::HashMap::new(); params.insert("code".into(), "auth-code".into()); let result = client.oidc_callback(¶ms).await?; ~~~

    Endpoint Summary

    | Method | Path | Purpose | |---|---|---| | GET | /api/auth/oidc/{connection_uuid}/authorize | 302 redirect to IdP | | GET | /api/auth/oidc/{connection_uuid}/callback | Handle IdP callback | | POST | /api/admin/organizations/{org}/oidc-connections | Create connection | | GET | /api/admin/organizations/{org}/oidc-connections | List connections | | DELETE | /api/admin/organizations/{org}/oidc-connections/{uuid} | Delete connection |

    When to Use

  • Standard B2B sign-in with corporate IdPs
  • Anything where SAML feels too heavy

Error Reference

| Status | Code | Meaning | |--------|------|---------| | 400 | invalid_connection | OIDC connection ID does not exist | | 400 | invalid_state | OAuth state parameter mismatch (CSRF) | | 400 | missing_code | Authorization code not present in callback | | 401 | token_exchange_failed | Provider rejected the code exchange | | 403 | domain_mismatch | Email domain not allowed for this connection | | 404 | org_not_found | org_uuid does not exist | | 502 | provider_error | Identity provider returned an error |