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 id | Provider | Notes |
|---|---|---|
google | Standard OIDC, email_verified claim required | |
microsoft | Microsoft Entra ID | Entra emits no email_verified claim; the asserted email is trusted (Entra only returns addresses it controls) |
oidc | Generic OIDC | Any spec-compliant IdP, including a self-hosted European IdP: the recommended sovereign option |
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
}'
# 204emailTrust:0requires theemail_verifiedclaim (default policy),1trusts the provider's asserted email (needed for Microsoft Entra).- The
clientSecretis 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/connectionslists 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:
- The identity
(provider, subject)is already linked: direct sign-in. - 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. - A local account exists with that verified email: the identity is linked to it automatically.
- 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.