Enterprise SSO Configuration
Initial configuration
This section describes how to configure Defakto to use Enterprise SSO.
Defakto supports OAuth+OIDC authentication using the Authorization Code Flow. To configure it, follow these steps:
Add Defakto URLs to your IdP
Add the Defakto Application in your IdP provider settings using the following Redirect URLs:
https://auth.api.defakto.security/auth/web/finish
https://auth.api.defakto.security/auth/cli/finish
The web console and the defakto CLI both authenticate through the auth.api.defakto.security domain. The spirlctl CLI still authenticates through auth.api.spirl.com; if you use spirlctl, also allow-list its Redirect URL:
https://auth.api.spirl.com/auth/cli/finish
If you are migrating from the legacy console at https://app.spirl.com, see Migrating to the new console domain below.
Provide us with your IdP information
After configuring your IdP, contact us to provide the following information:
- Issuer URL
- Client ID
- Client Secret (if required)
Defakto uses the Issuer URL as the base to retrieve the OIDC Discovery document (i.e. /.well-known/openid-configuration), which contains the JWKS URL that Defakto uses to verify ID tokens issued by the enterprise IdP. Both the Discovery Document URL and the JWKS URL must be accessible from Defakto.
CLI Login Flow with Enterprise SSO
When logging in with the Defakto CLI using Enterprise SSO, provide the --org flag with the organization name. The organization name is provided to you by the Defakto support team.
defakto login --org <organization-name>
After running this command, defakto will open the browser and redirect you to the IdP login page. After successful login, you will be redirected back to the CLI.
spirlctl login follows the same flow, with URLs on auth.api.spirl.com instead of auth.api.defakto.security.
Here is what is happening during the login flow with Enterprise SSO:
- The CLI opens a gRPC stream to the Defakto Control Plane and sends a login request
- Defakto identifies which social login or organization to log in with and starts an OAuth2 authorization code grant session
- Defakto sends back a URL in the format
https://auth.api.defakto.security/auth/cli/start?session_id=<session_id> - The CLI opens the Web Browser with the session URL, or the user can copy and paste the URL to the browser on a different device
- The Web Browser navigates to the session URL and Defakto redirects to the IdP login page
- The user logs in with the IdP
- The IdP responds with a redirect back to the URL that points to Defakto
https://auth.api.defakto.security/auth/cli/finish - The Web Browser performs the redirect, passing the authorization code to Defakto
- Defakto extracts all required parameters from the received URL. It performs a token exchange with the IdP, parses and verifies the ID token, uses the claims to authenticate the user, and generates a Defakto token.
- Defakto returns the token to the CLI, completing the login session and closing the gRPC stream.
Web UI Login Flow with Enterprise SSO
When logging in with the Defakto Web UI using Enterprise SSO, you should provide the organization name. The organization name is provided to you by the Defakto support team.
- The user opens the browser and loads https://console.defakto.security
- The user chooses
Sign in with enterprise identity, enters an organization name, and clicks theSign Inbutton - Web Browser sends a request to
https://auth.api.defakto.security/auth/web/start - Defakto identifies which organization to log in the user with and starts an OAuth2 authorization code grant session
- Defakto sends back an authorization URL pointing to the IdP
- The Web Browser opens the authorization URL
- The user logs in with the Enterprise IdP
- After authentication, the Enterprise IdP returns a redirect URL that points to Defakto
https://auth.api.defakto.security/auth/web/finish - Web Browser navigates to the redirect URL that creates a user session
- Defakto sends the user session back to the Web Browser
User provisioning
By default, joining an organization is invitation-only: an organization Owner invites each user by email, and the account is created with a pre-selected role on the invitee's first SSO login. The user provisioning settings on your enterprise SSO configuration let you switch to a directory-driven model instead, built from two pieces:
- Just-in-time (JIT) provisioning: Accounts are created automatically on a user's first SSO login through your identity provider (IdP). No invitations, no pre-provisioning.
- Group discovery: Defakto reads group memberships from your IdP's OIDC tokens and stores them on the user. Discovered groups can then be used as principals in realm role assignments, so access follows group membership in your IdP.
The enterprise SSO connection itself (issuer, client) remains managed by Defakto staff as described above. The provisioning settings are self-service.
Both settings require an OIDC enterprise SSO connection. Neither can be enabled on a SAML connection, and attempting it is rejected.
Just-in-time (JIT) provisioning
When a user authenticates through your organization's enterprise OIDC provider, Defakto matches them to an account by their SSO identity. Without JIT, an authenticated user without an account or invitation is rejected. With JIT enabled, that same login creates the account instead:
JIT-created accounts land on the Member role, which grants nothing by default. All of a Member's access comes from the grants you assign to them or to their groups, so an automatically created account carries no organization-wide visibility.
Two properties are worth calling out:
- Invitations keep working. JIT is additive: an invitation-based login follows the existing flow unchanged, and organizations that never opt in keep the current behavior exactly.
- Your IdP is the first gate. Anyone your IdP authenticates for the Defakto application can create an account, so control account creation with your IdP-side application assignment.
Group discovery
When group discovery is enabled, Defakto discovers a user's groups from the OIDC login itself:
- ID token claim: Defakto reads the configured groups claim (default
groups) from the verified ID token. - UserInfo fallback: If the ID token does not carry the claim at all, Defakto queries the provider's UserInfo endpoint and reads the claim from there. Many IdPs keep the ID token lean and serve scope-requested claims from UserInfo, so this fallback makes discovery work with providers' default configurations. A claim that is present in the ID token but empty counts as an answer, so it is taken as "no groups" and no UserInfo lookup is made.
Group discovery therefore adds a third endpoint to the reachability requirements: The provider's UserInfo endpoint must also be accessible from Defakto. If it is blocked, a login whose ID token omits the claim resolves to no groups rather than failing, so group-derived access disappears with no error shown to the user.
Discovered groups are stored on the user, visible on the Users with access tab under Organization → Users in the Console, and refreshed on every login, not only the first. A user's stored groups always reflect their IdP groups as of their last login, so team moves in your IdP propagate without any action on Defakto.
Discovered groups carry no access by themselves. Groups are informational until an administrator grants a role to a group, at which point every member inherits that grant. Authorization is always the union of the user's direct grants and the grants held by groups they belong to. There is no group list to import and no membership to sync: Defakto never needs your directory's full group catalog, only the group values that appear on your users' logins and the grants you choose to attach to them.
If discovery is enabled and neither the ID token nor UserInfo carries the claim, the login still succeeds: The user's stored groups are replaced with an empty set, and they keep only their direct grants. Turning discovery off stops the refresh without erasing what is already stored.
Group discovery runs for every user on every login, regardless of how the account was created. An invited Administrator's group memberships are discovered and can carry grants just the same as a JIT-created Member's.
IdP-specific notes
No provider emits groups without configuration. Before enabling discovery, confirm the claim reaches the ID token or UserInfo response for the Defakto application.
| IdP | What to know |
|---|---|
| Okta | The org authorization server only returns groups when the client requests the groups scope, which Defakto does not. Use a custom authorization server instead, which needs Okta API Access Management, and add a Groups claim that is included in the ID Token and always emitted rather than tied to a scope, filtered to the groups you need. Its issuer is https://<okta-domain>/oauth2/<auth-server-id>, not https://<okta-domain>, so give Defakto support that URL. See Customize tokens returned from Okta with a groups claim for the steps. |
| Microsoft Entra ID | No groups claim is sent until you add one as a groups optional claim, or set groupMembershipClaims in the app manifest. It must go in the ID token, as Entra's UserInfo never returns groups. Values are object IDs by default. For readable names, configure group claims with type Groups assigned to the application and source cloud-only group display names (AD-synced groups can use sAMAccountName), which needs Entra ID P1 or P2. That mode omits nested groups, and display names are mutable, so renaming a group breaks grants keyed to the old name. |
| Keycloak | Add a Group Membership protocol mapper to the Defakto client or its client scope, with Add to ID token enabled and Token Claim Name matching the claim Defakto reads (groups by default). Full group path is on by default and emits /platform-admins rather than platform-admins. Turn it off, or use the full path as the grant principal. |
| Google Workspace | No group membership in its OIDC tokens or UserInfo response, so neither discovery path works. Groups are only reachable through the Admin SDK Directory or Cloud Identity APIs, which Defakto does not call. |
The grant principal is the exact string your IdP emits in the claim. Matching is byte-exact and case-sensitive, so Platform-Admins and platform-admins are different principals. When creating a group grant, copy the value as it appears on a user who has logged in (visible on their user detail in the Console), not the display name in your IdP's admin console.
Two separate limits apply to users who belong to a very large number of groups, and both fail quietly:
- Your IdP may stop sending groups. Microsoft Entra ID stops sending group values once a user exceeds 200 groups (150 for SAML), sending a pointer to the Microsoft Graph API instead. Defakto does not follow that pointer, and Entra does not serve groups from its UserInfo endpoint either, so such a login resolves to no groups and the user loses their group-derived access. Emitting only the groups assigned to the Defakto application keeps you under the limit.
- Defakto stores at most 1,000 groups per user. Past that, the login still succeeds and Defakto keeps the first 1,000 values in the order your IdP sent them. A grant naming a group beyond that cut-off silently has no effect.
Configuring provisioning
Provisioning is configured per organization by an Owner.
| Setting | Values | Default | Notes |
|---|---|---|---|
| Automatic provisioning | invitation-only, jit | invitation-only | How accounts are created for users without an invitation. |
| Group discovery | off, token-claim + claim name | off | When enabled, the named token claim (default groups) is read on every login, with UserInfo fallback. Disabling stops refreshing but does not erase stored groups. |
The role JIT-created users land on is always Member and is not configurable: Member is the safe default, and access is meant to come from grants.
Console: Open Organization → Users → Provisioning. The Automatic provisioning and Group discovery cards are configured and saved independently. Automatic provisioning offers Disabled (invitation-only) or Just-in-time (JIT). Group discovery offers Disabled or Read from OIDC claim with a Groups claim name field. Saving requires the Owner role.
CLI: Use the spirlctl iam provisioning commands. Only the flags you pass are changed. Unspecified settings are preserved. The groups default applies to the API and the Console: spirlctl does not assume it, so --group-discovery token-claim must always be paired with an explicit --group-claim-name.
# Inspect the current configuration
spirlctl iam provisioning status
# Automatic provisioning: Invitation-only
# Group discovery: Disabled
# Enable group discovery from the "groups" claim
spirlctl iam provisioning set --group-discovery token-claim --group-claim-name groups
# Switch to just-in-time provisioning
spirlctl iam provisioning set --mode jit
# Roll back to invitation-only and stop discovering groups
spirlctl iam provisioning set --mode invitation-only --group-discovery off
Verifying the setup
Have a test user who is not yet in your organization log in through SSO. Their account is created with the Member role, and their discovered groups appear on their user detail. If you have granted realm roles to their groups, they see exactly the realms those grants cover.
The Users with access tab under Organization → Users has a Groups column showing each user's discovered IdP groups, and a Provisioning column showing how the account was created, so you can audit which accounts arrived through which path. An account created by JIT reads Just-in-time (SSO), and an invited one reads Invitation. Clicking a user opens a detail view listing every group they carry, which is where to read off the exact group string when creating a grant.
Lifecycle in your IdP
With JIT and group-based grants in place, joiners, movers, and leavers are handled where they already happen today:
- Joiners: Adding someone to a group in your IdP gives them that group's access on their first login. No Defakto action is needed.
- Movers: Changing someone's groups in your IdP moves their access on their next login. No Defakto action is needed.
- Leavers: Removing someone from your IdP, or from the Defakto application, stops them logging in. Any session they already hold stays valid until it expires, up to 72 hours, so delete the user in Defakto as well when you need access cut immediately. The account row remains in the users list until deleted.
Migrating to the new console domain
The Defakto web console is available at https://console.defakto.security. The legacy console at https://app.spirl.com continues to work, but new sign-ins should use the new domain.
The console experience is determined by the URL you visit. https://console.defakto.security uses the auth.api.defakto.security authentication and api.defakto.security API endpoints, while https://app.spirl.com uses the auth.api.spirl.com and api.spirl.com endpoints. The originating domain also determines the invitation URLs generated when you invite a user to your organization. The CLIs follow the same split: the defakto CLI uses the defakto.security endpoints, while spirlctl uses the spirl.com endpoints.
To start using the new console and the defakto CLI with Enterprise SSO, add the new web and CLI redirect URLs to your IdP's allowlist:
https://auth.api.defakto.security/auth/web/finish
https://auth.api.defakto.security/auth/cli/finish
If you also use the spirlctl CLI, keep the spirl.com CLI redirect URL allowlisted:
https://auth.api.spirl.com/auth/cli/finish
After your IdP configuration is updated, sign in at https://console.defakto.security.