Obexal Docs

Docs/Authentication/Social login

Social login (Google, Microsoft, generic OIDC)

Inbound federation with Obexal as relying party: per-organization connections, verified-email account linking, no external token stored, MFA still applies.

Social login delegates the primary authentication to an external OpenID Connect provider ("Sign in with..."), then opens a regular Obexal session. The Authorization Code + PKCE flow runs entirely server-side; the browser only performs top-level navigations.

Supported providers

Provider idProviderNotes
googleGoogleStandard OIDC, email_verified claim required
microsoftMicrosoft Entra IDEntra emits no email_verified claim; the asserted email is trusted (Entra only returns addresses it controls)
oidcGeneric OIDCAny spec-compliant IdP, including a self-hosted European IdP: the recommended sovereign option
Note

Self-hosted deployments can set SOCIAL_SOVEREIGN_ONLY=true: Google and Microsoft are then not mounted at all, even if configured, leaving only the generic OIDC connector and enterprise SAML. See Configuration.

Configure a connection for your organization

Connections are configured per organization and override the platform-wide provider with the same id (or add one that is not enabled globally). Register this redirect URI at your provider: https://accounts.obexal.com/v1/auth/social/{provider}/callback (your custom sign-in domain replaces accounts.obexal.com if you use one).

curl -sS -X POST https://accounts.obexal.com/v1/admin/social/connections \
  -H "Authorization: Bearer $OBEXAL_API_TOKEN" -H 'Content-Type: application/json' \
  -d '{
    "provider": "oidc",
    "displayName": "IdP interne",
    "issuer": "https://idp.example.eu",
    "clientId": "obexal-rp",
    "clientSecret": "'$IDP_CLIENT_SECRET'",
    "scopes": ["openid", "email", "profile"],
    "emailTrust": 0
  }'
# 204
  • emailTrust: 0 requires the email_verified claim (default policy), 1 trusts the provider's asserted email (needed for Microsoft Entra).
  • The clientSecret is encrypted at rest. If it can no longer be decrypted, the connection becomes unavailable (fail-closed) rather than falling back to another configuration.
  • GET /v1/admin/social/connections lists connections (known provider ids: google, microsoft, oidc); DELETE /v1/admin/social/connections/{provider} removes one.

The sign-in flow

# 1. List the providers enabled for the organization (the sign-in UI renders the buttons).
curl -sS https://accounts.obexal.com/v1/auth/social/providers
# 200 -> {"providers":[{"id":"google","displayName":"Google"},{"id":"oidc","displayName":"IdP interne"}]}

# 2. Start: the browser is redirected (302) to the provider.
#    redirect_uri is the post-login target, validated against the allowed origins.
curl -sSi "https://accounts.obexal.com/v1/auth/social/google/start?redirect_uri=https://portal.example.eu/"
# 302 Location: https://accounts.google.com/o/oauth2/v2/auth?...&code_challenge_method=S256&state=...

# 3. Callback (called by the provider): GET /v1/auth/social/{provider}/callback
#    Success -> 302 to redirect_uri with the session cookie set.
#    Failure -> 302 to redirect_uri?error=social_*  (no session).

Between start and callback, the flow is protected by a single-use state stored hashed server-side (10 minute TTL), a code_verifier for PKCE (S256), a nonce bound to the id_token, and an obexal_social_state binding cookie that ties the callback to the browser that initiated the flow. On callback, the server exchanges the code and validates the id_token: RS256 signature against the provider's JWKS, iss, aud, exp and nonce.

Account resolution

The account is resolved with a strict verified-email policy:

  1. The identity (provider, subject) is already linked: direct sign-in.
  2. First time for this identity: the provider must assert a verified email, otherwise the flow fails with error=social_email_unverified. No account is ever created or linked on an unverified address.
  3. A local account exists with that verified email: the identity is linked to it automatically.
  4. No account exists: one is provisioned, with the email already marked verified.

Users can review and remove their linked identities: GET /v1/auth/identities and DELETE /v1/auth/identities/{provider}. Unlinking is refused (409 last_credential) if it would remove the account's last sign-in method.

MFA applies after federation

Social login is a primary factor, exactly like a password: if the resolved account has an active MFA factor, no session is opened at the callback. The browser is redirected to the sign-in UI's MFA screen with a single-use challenge token, and the session only exists once the second factor is validated. See the MFA chokepoint.

What Obexal stores

Obexal persists only the identity link: (provider, subject) plus the email, scoped to your organization. No external access token, refresh token or id_token is ever stored: the id_token is verified in memory during the callback and discarded. The proof of identity is the verification itself, not a saved credential.

Honest limits

  • The upstream id_token must be signed with RS256. Any other algorithm (ES256, HS256, ...) is explicitly refused, as a defense against algorithm confusion, and only RSA keys are read from the provider's JWKS. An IdP that signs with elliptic curves only cannot be connected today.
  • The flow is browser-based (GET navigations); there is no API-only variant of social login.