An identity provider is an upstream OIDC or SAML 2.0 provider, such as Okta, Microsoft Entra ID or Google Workspace, that your people already sign in with. You route verified email domains to it: when someone enters an email address in one of those domains at your portal, they’re sent there to sign in, and come back signed in to EZGH SSO. Your applications still trust only EZGH SSO.
A directory can have up to 10 identity providers. You need sso.identityProviders.create.
How federated sign-in works
- The person enters their email address at
https://<alias>.sso.ezgh.app. Its domain is verified and routed to an identity provider, so they’re sent there. Every address in the domain gets the same answer. - They sign in at the identity provider, which sends them back (OIDC: authorization code with PKCE; SAML: SP-initiated, HTTP-POST to the ACS).
- EZGH SSO checks the response and finds the user by the identity provider’s subject (OIDC
sub, SAMLNameID):- Already linked: that user signs in. The email address is never used to find them again, so a changed email at the identity provider doesn’t move the link.
- First sign-in: the asserted email’s domain must be verified and routed to this identity
provider. A user with that email in the directory is linked (a
pendingActivationuser becomesactive; a suspended user is refused). Otherwise a new user is created withsource: jit(just in time), counted against your user quota. - With SCIM: while a SCIM token names the identity provider, only users its SCIM provisioned can sign in through it, and nobody is created on first sign-in.
- Group mappings are applied, and the session starts.
Users created this way have no password, TOTP or passkeys, and sign in only through their identity provider. Suspending or deleting them in EZGH SSO works as for hosted users. Refused sign-ins show an error page on the portal and are recorded in Trails.
Add an identity provider
Add and verify the domains first, then create the identity provider, then route the domains to
it with domains. Each domain routes to one identity provider at most (409 domain_in_use),
and routes only while it’s verified.
In the console, use EZGH SSO > Identity providers. With the CLI:
ezgh sso identity-providers create --name Okta --protocol oidc --issuer https://acme.okta.com \
--client-id 0oa1b2c3 --client-secret-stdin --domains acme.com < okta-secret.txt
ezgh sso identity-providers get OktaWith the API, for each protocol:
Register an application at your provider, a web application with a client secret, and give it this redirect URI:
https://<alias>.sso.ezgh.app/federation/oidc/callbackThen create the identity provider:
curl -X POST https://sso.ezghcloud.com/v1/organizations/$ORG_ID/sso/identity-providers \
-H "Authorization: Bearer $EZGH_API_KEY" -H "Content-Type: application/json" \
-d '{
"name": "Okta",
"protocol": "oidc",
"oidc": { "issuer": "https://acme.okta.com", "clientId": "0oa…", "clientSecret": "…" },
"domains": ["acme.com"]
}'oidc field |
Description | Default |
|---|---|---|
issuer |
The provider’s https issuer. Its /.well-known/openid-configuration must name exactly this issuer. |
Required |
clientId |
The client ID you registered. | Required |
clientSecret |
Write-only: it’s never shown again or recorded. clientSecretSet says whether one is set; null removes it. |
|
tokenEndpointAuthMethod |
client_secret_basic, client_secret_post, or none for a public client. |
client_secret_basic with a secret, else none |
scopes |
1 to 20 scopes, including openid. |
openid email profile |
emailClaim |
The ID token claim with the email address. An email marked email_verified: false isn’t used. |
email |
groupsClaim |
The ID token claim with the person’s groups. | groups |
The answer adds redirectUri and, once fetched, the validated discovery. ID tokens must be
signed with RS256–512, PS256–512 or ES256–512.
SAML needs the identity provider’s metadata to create it, and the service provider values your identity provider needs contain its ID. So:
-
Create a SAML application at your identity provider. If it insists on an ACS URL and entity ID before it gives you metadata, enter temporary values.
-
Create the identity provider with
metadataUrl(the provider’s metadata URL) ormetadataXml(the metadata document, up to 256 KiB):curl -X POST https://sso.ezghcloud.com/v1/organizations/$ORG_ID/sso/identity-providers \ -H "Authorization: Bearer $EZGH_API_KEY" -H "Content-Type: application/json" \ -d '{ "name": "Entra ID", "protocol": "saml", "saml": { "metadataUrl": "https://login.microsoftonline.com/…/federationmetadata.xml?appid=…" } }' -
Enter the answer’s
saml.serviceProviderat your identity provider:entityIdas the entity ID (audience), andacsUrlas the ACS (reply) URL.metadataUrlserves the same as SAML metadata.Value Form Entity ID, metadata https://<alias>.sso.ezgh.app/federation/saml/metadata/<identity provider ID>ACS URL https://<alias>.sso.ezgh.app/federation/saml/acs/<identity provider ID> -
Route the domains: UpdateIdentityProvider with
domains.
saml field |
Description | Default |
|---|---|---|
metadataUrl |
An https URL of the identity provider’s metadata, fetched and refreshed. | One of the two is required |
metadataXml |
The metadata itself. It’s checked at once and never refreshed: update it when the provider’s certificate changes. | |
emailAttribute |
The attribute with the email address. Empty: the NameID when its format is email address, else a common email attribute (email, mail, emailaddress, the …/claims/emailaddress claim). |
Empty |
groupsAttribute |
The attribute with the person’s groups. | groups |
The metadata must describe one identity provider with an https HTTP-Redirect single sign-on URL
and 1 to 10 RSA (2048 bits or more) or ECDSA signing certificates. The answer shows its
entityId, ssoUrl and certificates with their expiry.
Requirements for responses:
- Both the response and its assertion must be signed, with SHA-256 or stronger (SHA-1 is refused). Encrypted assertions are refused.
- Only SP-initiated sign-in: a response that EZGH SSO didn’t ask for (IdP-initiated) is refused.
- The assertion must name the service provider’s entity ID as its audience, and a
NameIDthat isn’t transient.
Metadata and keys
EZGH SSO fetches OIDC discovery and keys, or SAML metadata, when you create the identity provider
or change its issuer or metadata URL, then every 6 hours (every 15 minutes after a failure).
RefreshIdentityProviderMetadata fetches within
seconds (409 metadata_by_value for SAML given as metadataXml). A failed fetch keeps the last
good copy and shows the reason in metadata.error. Until the first fetch succeeds, sign-in through
the identity provider is unavailable (503 identity_provider_unavailable). URLs must be public
https URLs.
Group mappings
groupMappings adds people to directory groups from the groups the identity provider sends (the
OIDC groupsClaim or SAML groupsAttribute): up to 100 { "idpGroup", "groupId" } pairs.
idpGroup is the value the provider sends (a name, or an ID for providers that send IDs), or *
for everyone who signs in through it. groupId is a directory group you created: groups pushed
by SCIM belong to SCIM.
Mappings are applied on every federated sign-in: the person is added to the mapped groups, and removed from groups this identity provider added that no longer match. Memberships you added by hand are left alone.
MFA
With trustUpstreamMfa off (the default) in the sign-in policy,
mfaRequired applies only to hosted sign-in, and federated sessions count as single-factor.
With it on, EZGH SSO reads the identity provider’s evidence of multi-factor sign-in: an OIDC amr
with mfa or two different methods, or a SAML authentication context for MFA (or Entra ID’s
authnmethodsreferences). A sign-in with that evidence is multi-factor (acr
urn:ezgh:sso:acr:mfa), and when the directory requires MFA, a sign-in without it is refused.
Provider guides
OIDC (recommended):
- In the Okta Admin Console, create an app integration: OIDC - OpenID Connect, Web Application.
- Sign-in redirect URIs:
https://<alias>.sso.ezgh.app/federation/oidc/callback. Assign the people or groups who should use it. - Copy the Client ID and Client secret. The issuer is your Okta domain, such as
https://acme.okta.com(or a custom authorization server’s issuer). - For groups, add a groups claim named
groupsto the ID token (in the app’s Sign On settings, a groups claim filter). If your setup puts it behind thegroupsscope, addgroupstoscopes.
SAML: create a SAML 2.0 app integration. Single sign-on URL is the ACS URL and
Audience URI the entity ID; Name ID format EmailAddress, Application username
Email. Keep the response and the assertion signed. Add a group attribute statement named
groups. Use the app’s metadata URL as metadataUrl.
OIDC:
- In the Entra admin center, App registrations > New registration, with a Web
redirect URI
https://<alias>.sso.ezgh.app/federation/oidc/callback. - Certificates & secrets: create a client secret. The client ID is the Application (client) ID.
- The issuer is
https://login.microsoftonline.com/<tenant ID>/v2.0. - Token configuration: add the optional
emailclaim to the ID token, and a groups claim. Entra sends group object IDs, so use those asidpGroupin group mappings.
SAML:
- Enterprise applications > New application > Create your own application (non-gallery), then Single sign-on > SAML.
- Identifier (Entity ID) is the entity ID and Reply URL the ACS URL.
- Under SAML Certificates, set Signing option to Sign SAML response and assertion. Entra signs only the assertion by default, which EZGH SSO refuses.
- For groups, add a group claim, and set
groupsAttributeto its name (http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsunless you rename it). - Use the App Federation Metadata Url as
metadataUrl.
SAML (sends groups):
- In the Google Admin console, Apps > Web and mobile apps > Add custom SAML app.
- Download the IdP metadata, and use it as
metadataXml(Google doesn’t publish a metadata URL, so update it when Google’s certificate changes). - ACS URL is the ACS URL and Entity ID the entity ID. Select Signed response.
Name ID format
EMAIL, Name ID the primary email. - Under Group membership, map the groups to an app attribute named
groups. - Turn the app on for the people who should use it.
OIDC (no groups): create an OAuth client in the Google Cloud console, type Web
application, with the redirect URI above. The issuer is https://accounts.google.com. Make the
OAuth consent screen Internal so only your Workspace’s accounts can sign in. Google’s ID
tokens carry no groups, so group mappings other than * don’t apply.
Provisioning
provisioning shows how people get into the directory through this identity provider:
{ "jit": true, "scim": false }: people are created on their first sign-in.{ "jit": false, "scim": true }: a live directory SCIM token names this identity provider. Its SCIM provisions people, and only provisioned people sign in through it. See Inbound SCIM.
Delete an identity provider
DeleteIdentityProvider unroutes its domains (people in them sign in hosted from then on), revokes the SCIM tokens that name it, removes the users’ links to it and the group memberships its mappings added, and ends federated sign-ins in progress. The users stay in the directory, and users and groups its SCIM provisioned are unlocked.