Single sign-on (SSO)
Blobify supports one OpenID Connect identity-provider configuration per organization. Okta, Auth0, Microsoft Entra ID, Google Workspace, Keycloak, and other OIDC-compliant providers can use the same authorization-code flow with PKCE.
An organization admin configures SSO. Blobify maps identity-provider group claims to a Blobify role and space access on every SSO login.
Sign-in flow
- The user enters an email on the Blobify login page.
- The dashboard calls
POST /v1/auth/sso/lookup. - Blobify returns enabled SSO organizations for existing memberships and whether password login is allowed.
- The browser navigates to
GET /v1/auth/sso/start?orgId=...&redirectUri=.... - Blobify performs OIDC discovery and redirects to the provider with a PKCE S256 challenge,
state, andnonce. - A short-lived HTTP-only cookie stores the PKCE verifier, state, nonce, and target organization.
- The provider redirects the browser to the dashboard callback with an authorization code.
- The dashboard posts the code and state to
POST /v1/auth/sso/callback. - Blobify verifies the callback, exchanges the code, reads claims, resolves access, links or creates the membership, and returns a normal Blobify session.
The SSO state cookie uses SameSite=Lax, expires after 10 minutes, and is cleared after callback completion.
Register Blobify with the provider
Create an OIDC web application and register this redirect URI:
https://blobify.io/auth/sso/callbackCopy the exact value shown in Settings > SSO if you use another dashboard origin.
Collect the issuer URL, client ID, and client secret. Blobify runs discovery against the issuer's standard OpenID configuration. Ensure the ID token contains the configured email claim and, when you use group mappings, the configured groups claim.
Configure the organization
Use Settings > SSO or the admin-only IDP endpoint. Creating a configuration requires a client secret. On a later update, omit clientSecret to keep the stored value.
curl -X PUT "https://api.blobify.io/v1/orgs/$ORG_ID/idp" \
-H "Authorization: Bearer $ADMIN_JWT" \
-H "Content-Type: application/json" \
-d '{
"provider": "oidc",
"enabled": true,
"enforced": false,
"issuer": "https://acme.okta.com/oauth2/default",
"clientId": "YOUR_CLIENT_ID",
"clientSecret": "YOUR_CLIENT_SECRET",
"scope": "openid profile email groups",
"emailClaim": "email",
"groupsClaim": "groups",
"defaultRole": null,
"groupRoleMap": [
{ "group": "blobify-admins", "role": "admin", "spaces": ["*"] },
{ "group": "content-team", "role": "editor", "spaces": ["main"] }
]
}'| Field | Behavior |
|---|---|
provider | Must be oidc |
enabled | Makes this configuration available for SSO login |
enforced | Blocks password login as described below |
issuer | Base issuer URL used for OIDC discovery |
clientId | OIDC client ID |
clientSecret | Required on create, optional on update, encrypted at rest, and never returned |
scope | Space-separated scopes; Blobify ensures openid is first; default is openid profile email |
emailClaim | Direct claim name for the user's email; default is email |
groupsClaim | Direct claim name for groups; default is groups; accepts a string or string array |
defaultRole | Fallback role when no group matches; null denies access |
groupRoleMap | Group-to-role and space-scope mappings |
GET /v1/orgs/{orgId}/idp never returns the secret. It returns hasClientSecret. If the stored secret cannot be decrypted, it also returns secretDecryptionFailed: true so an admin can replace it.
Resolve roles and spaces
Blobify ranks roles as viewer < editor < developer < admin.
For each login:
- Every map entry whose
groupoccurs in the provider claim matches. - The highest-ranked matching role wins.
- Spaces from all matches are unioned.
- A
*in any matching entry grants all spaces. - If nothing matches, Blobify assigns
defaultRolewith all spaces when a default exists. - If nothing matches and
defaultRoleisnull, sign-in returns403 Forbidden.
Role and space access are synchronized from the claims on every SSO login.
Provision users and memberships
The provider's sub claim is the stable external identity.
- If an organization membership already has that
sub, Blobify updates its role and spaces. - If the email belongs to an existing member, Blobify links the
subto that membership. - Otherwise Blobify creates a passwordless user when needed and adds the organization membership just in time.
Subsequent logins match the membership by sub, so a later email change does not create a second membership for the same provider identity.
Enforced mode
Set enforced: true only after testing the provider and group mapping. Blobify enforces this on the API, not only in the login form.
The current password-login rule is account-wide: if any enabled SSO organization that the user belongs to has enforcement enabled, password login is blocked for that user. The login lookup returns passwordAllowed: false, and direct password authentication is also rejected by the API.
Leave enforcement off during rollout if existing password access must continue.
Security properties
- PKCE S256 protects the authorization-code exchange.
stateprotects the browser flow, andnoncebinds the ID token.- The state cookie is HTTP-only, short-lived, and single-use.
- The client secret is encrypted before persistence and omitted from API responses.
- The requested dashboard callback must match Blobify's server-side origin allowlist.
- Organization IDP read, write, and delete endpoints require
admin.
Remove SSO
curl -X DELETE "https://api.blobify.io/v1/orgs/$ORG_ID/idp" \
-H "Authorization: Bearer $ADMIN_JWT"The endpoint returns 204 No Content. Removing the configuration disables SSO for the organization. Existing users with passwords can use password login again. Users created only through SSO do not have a password until they complete a password setup or recovery flow.