SSO & Identity Providers

OIDC/OAuth2 SSO architecture, PKCE flow, identity provider model, external login linking, OAuth applications, claim mapping, and full API reference.

SCRIPE supports Single Sign-On (SSO) via external OIDC identity providers. This page covers the complete SSO architecture: PKCE authorization code flow, the identity provider entity model, external login linking, OAuth application registration, claim mapping, tenant scoping, and all API endpoints.

How SSO Works

SCRIPE uses the Authorization Code Flow with PKCE (Proof Key for Code Exchange) for SSO. This is the most secure OAuth2 flow, recommended by the OAuth 2.1 specification for all client types.

  1. 1. Provider Discovery

    The login page fetches available SSO providers via GET /auth/oidc/providers/admin. Only enabled providers for the current login context (admin/user) are returned.
  2. 2. PKCE Challenge

    When the user clicks an SSO button, the frontend calls POST /auth/oidc/challenge. The backend generates a code_verifier, computes the code_challenge (SHA-256), and returns the authorization URL.
  3. 3. IdP Redirect

    The frontend stores PKCE state (code_verifier, state, providerId) in sessionStorage, then redirects the user to the external IdP's authorization endpoint.
  4. 4. User Authentication

    The user authenticates at the external IdP (Azure AD, Google, Okta, etc.) and grants consent for the requested scopes.
  5. 5. Callback & Token Exchange

    The IdP redirects to /sso/callback with an authorization code. The frontend retrieves PKCE state from sessionStorage, validates the state parameter, and calls POST /auth/oidc/callback. The backend exchanges the code for tokens using the code_verifier.

PKCE Security Model

PKCE prevents authorization code interception attacks by ensuring that only the client that initiated the flow can exchange the code. The code_verifier is never sent over the network — only its SHA-256 hash (code_challenge) is sent during the challenge step.

ComponentPurpose
code_verifierCryptographically random string stored in sessionStorage
code_challengeSHA-256 hash of the code_verifier sent to the IdP
stateCSRF protection — verified on callback
Authorization CodeOne-time code from IdP, exchanged server-side

Identity Provider Entity

The IdentityProvider entity stores all OIDC configuration for an external IdP. Each provider is optionally scoped to a tenant (TenantId = null means system-wide).

FieldTypeDescription
Namestring(100)Human-readable provider name (e.g., 'Corporate Azure AD')
Slugstring(50)URL-safe identifier, unique per tenant
Protocolstring(30)Authentication protocol: oidc, oauth2, saml (future)
TenantIdGuid?Null = system-wide; set = tenant-specific provider
Authoritystring(500)OIDC issuer URL for discovery document
ClientIdstring(200)OAuth2 client_id registered with the provider
ClientSecretEncryptedstring(500)AES-encrypted secret, never returned to frontend
Scopesstring(1000)OAuth2 scopes (default: openid profile email)
ClaimMappingJsonstring(2000)JSON mapping external claims to SCRIPE claims
EnabledForAdminsboolShow on admin login page
EnabledForUsersboolShow on user login page
ButtonColorstring(50)CSS color for branded login button
ButtonLabelstring(200)Custom button text
DisplayOrderintSort order on the login page

External Login Linking

Before SSO login works, a SCRIPE admin/user must link their account to the external identity. This creates an ExternalLogin record mapping the provider's subject ID to the SCRIPE account.

  1. Admin signs in with username/password
  2. Navigates to Profile → Security → External Logins
  3. Clicks 'Link External Account' for the desired provider
  4. Completes OIDC authentication with the external IdP
  5. SCRIPE creates an ExternalLogin record linking the provider identity to the admin
  6. From now on, the admin can sign in using the SSO button on the login page

OAuth Applications

OAuth Applications are third-party apps that authenticate against SCRIPE as an OIDC server. Each app gets a Client ID and Client Secret, with configurable redirect URIs, scopes, and PKCE enforcement.

FieldDescription
Application NameDisplay name for the third-party app
Client IDAuto-generated unique identifier
Client SecretAuto-generated secret (shown only once on creation)
Client TypeConfidential (server-side) or Public (SPA/mobile)
Redirect URIsAllowed callback URLs
Allowed Scopesopenid, profile, email, roles (configurable)
PKCE RequiredEnforce Proof Key for Code Exchange

Claim Mapping

When an external IdP uses non-standard claim names, the ClaimMappingJson field maps them to SCRIPE's expected claims. If null, standard OIDC claim names (sub, email, name) are used.

JSON
{
  "sub": "oid",
  "email": "preferred_username",
  "name": "display_name",
  "given_name": "first_name",
  "family_name": "last_name"
}

Tenant Scoping

Identity Providers are tenant-scoped via the TenantId column. Providers with null TenantId are system-wide (available to all tenants). The backend automatically filters providers by the current admin's tenant context.

API Endpoints

MethodEndpointDescriptionAuth
GET/api/v1/auth/oidc/providers/adminList SSO providers for admin login🔒 Required
GET/api/v1/auth/oidc/providers/userList SSO providers for user login🔒 Required
POST/api/v1/auth/oidc/challengeGenerate PKCE authorization URL🔒 Required
POST/api/v1/auth/oidc/callbackExchange auth code for linked account🔒 Required
GET/api/v1/IdentityProvidersList identity providers (CRUD)🔒 Required
POST/api/v1/IdentityProvidersCreate identity provider🔒 Required
PUT/api/v1/IdentityProviders/{id}Update identity provider🔒 Required
DELETE/api/v1/IdentityProviders/{id}Delete identity provider🔒 Required
POST/api/v1/IdentityProviders/{id}/testTest OIDC connection🔒 Required
GET/api/v1/OAuthApplicationsList OAuth applications (CRUD)🔒 Required
POST/api/v1/OAuthApplicationsCreate OAuth application🔒 Required
POST/api/v1/OAuthApplications/{id}/regenerate-secretRegenerate client secret🔒 Required