Identity providers
An identity provider adds a "Continue with …" button to an issuer's login page, so your users can sign in through an upstream provider instead of (or alongside) a password. auth.is speaks OIDC upstream — it federates to Google, Microsoft Entra ID, Kenni, or any standards-compliant OIDC provider, using the authorization-code flow with PKCE, state, and nonce on every request.
Providers are configured per issuer. Each one is an issuer_provider row with a URL-safe key (used in the /federate/:key and /callback/:key routes), a display name, the upstream clientId / clientSecret, and type-specific config. You manage them from the MCP — this page covers the concepts and a walkthrough; exact tool parameters live in the MCP tool reference.
The four provider types
| Type | Type-specific field | Notes |
|---|---|---|
google | hostedDomains (optional) | The issuer URL is fixed (accounts.google.com). If you set hostedDomains, a login is rejected unless the token's hd claim is on the list — Google Workspace domain restriction. |
entra | tenantId (required) | Microsoft Entra ID. A GUID, or common / organizations / consumers. The issuer URL is derived from the tenant, and a token from a different tenant is rejected. The stable per-user id is oid. |
kenni | issuerUrl (required) | A Kenni issuer, standard OIDC. |
oidc | issuerUrl (required) | Any other compliant OIDC provider. |
For google you never pass an issuer URL (it is preset); for entra the URL comes from the tenant; for kenni/oidc you must supply it. The contract enforces the right field per type, so a wrong combination (an issuerUrl on Google, a missing tenantId on Entra) fails validation before it reaches the server. In production, issuerUrl must be https.
The upstream client secret is write-only. You supply it when adding or updating a provider; no read ever returns it — a listed provider reports only hasSecret: true/false, never the value, not even masked. Public upstreams (which have no secret) simply omit it.
Adding a provider — walkthrough
Ask your AI tool to add a provider with add_identity_provider. A minimal Google example needs the issuer id, a key, a display name, the type, and the upstream clientId / clientSecret you registered at Google:
Add a Google identity provider to my issuer — key
Add Entra with a tenantId, or Kenni/generic OIDC with an issuerUrl, the same way. Every provider is registered with a redirect/callback URL of https://{slug}.auth.is/callback/{key} — register that exact URL at the upstream provider. The change bumps the issuer's configVersion and is live on the login page immediately (see Issuers).
Toggle a provider without deleting it via the enabled flag (on add_identity_provider or update_identity_provider); remove_identity_provider deletes it. list_providers shows the masked config for each.
What happens at login
The one-time PKCE/state/nonce bundle rides the interaction, so an intercepted callback cannot be replayed and state is a pure CSRF nonce. auth.is reads the upstream user from the returned ID token, then decides which local account the login belongs to.
Account linking (verified-email matching)
On callback, auth.is matches the upstream user to a local account by the pair (issuer provider, upstream user id). If that link already exists, the user signs into the same account as last time. If it does not:
- It links to an existing local user only when both the local email and the upstream email are verified.
- Otherwise it creates a fresh, separate user.
An unverified upstream email is never auto-linked to an existing account. This blocks an attacker from claiming someone else's account by signing up at an upstream provider with an unverified copy of their email — a new user is created instead. This is the same rule described in Users & identities; it is why email verification (on both sides) matters.
Mapping upstream claims
When auth.is reads the upstream ID token, it maps a standard set of claims onto the local account: the upstream sub (or oid for Entra) becomes the identity link, and email / name are read where present. You can steer that mapping — and pull in a profile picture — with an optional claimMappings on any provider:
claimMappings: {
name: "display_name", // local name ← upstream `display_name`
picture: "picture", // local picture ← upstream `picture`
email: "work_email" // local email ← upstream `work_email`
}
Each key is a local claim (name, picture, or email); each value is the flat upstream claim name to read it from (v1 supports single claim names, not nested dot-paths). A mapping only overrides what it names — unmapped claims keep their default. Values must be strings; a picture value must be an http(s) URL (it is stored as-is, not re-hosted). A missing, non-string, or invalid value is skipped, so a mapping never blanks a good default.
Preset rules always run first and are never weakened by a mapping: Entra still enforces its tid, Google still enforces hostedDomains, and the identity link is always the preset's stable user id.
When mapped values are written
- First sign-in (new user): the mapped
name,picture, andemailare written onto the freshly-created account. - Email is mapped at create/link time only. An
emailmapping is used when the account is first created or linked; it is never changed on a later login (a per-issuer address is unique and verification-bound, so login-time email swaps are disallowed). - Repeat sign-in: by default the local record wins — a returning user's stored
name/pictureare left untouched, even if they changed upstream. SetrefreshOnLogin: trueto make the upstream authoritative: on every login the mappednameandpictureare refreshed from the fresh token when they differ.emailis still never refreshed.
refreshOnLogin suits tenants whose upstream IdP is the source of truth (an Entra shop, say); leave it off (the default) when users edit their profile on your side. Both claimMappings and refreshOnLogin are set on add_identity_provider / update_identity_provider and take effect on the next login immediately.
Restricting providers per client
By default, every provider you enable on the issuer shows on every client's login page. To narrow which providers a specific client offers, use set_client_providers — it sets that client's enabled_providers to a list of provider keys. A client whose list is unset is unrestricted (shows all enabled providers); an empty or specific list shows only what you name. This lets one issuer host, say, an internal app that only offers Entra alongside a public app that offers Google.
Next steps
Users & identities
the linking rule and per-issuer pools in full.
Username & password
the credential federation sits alongside.
Clients
`set_client_providers` and per-client provider restriction.
Issuers
where providers are configured and why edits are instant.
MCP tool reference
exact params for the provider tools.