Skip to content

Sign in with your identity provider

An App names an OpenID Connect provider; the config file says where it is.

auth: {
providers: ['email', oidc('workforce')],
signUp: { providers: [oidc('workforce')] }, // or { domains: ['contoso.com'] }
roles: { underwriter: { read: true, commands: ['approve-loan'], from: { groups: ['2f1c…'] } } },
},
{ "name": "workforce", "kind": "oidc", "label": "Contoso", "issuer": "https://login.microsoftonline.com/<tenant id>/v2.0",
"clientId": "<application id>", "clientSecret": "${WORKFORCE_CLIENT_SECRET}" }

Register <public URL>/api/auth/callback/workforce at the provider. The entry also takes scopes (default email profile), discovery (a metadata URL other than <issuer>/.well-known/openid-configuration), verifiedEmails (it asserts only verified addresses), mfa (the acr values that mean more than one factor) and insecure (an http issuer on a trusted network). The secret is only ever a ${VARIABLE}. The browser sees the label, nothing else.

Every sign-in is a code with PKCE, state and nonce; the ID token’s signature, issuer, audience, azp, age (a minute of skew) and nonce are checked. A person is the issuer’s subject, never an email. Only the ID token is kept.

  • from: { <claim or dotted.path>: [values] } gives a role at every sign-in; roles without it stay as an administrator set them. Changes go to the account’s history; each value is a line of authority.lock. A sign-in that leaves no declared role is refused with a sentence. Not in a tenant App.
  • signUp: 'invite' makes no account at a first sign-in, 'open' does, and { providers, domains } does through those providers, or for a verified email in those domains (both, when both are said).
  • An address links to an existing account only if verified (email_verified, or verifiedEmails). An account whose own address was never verified loses its password, other links and sessions when a verified sign-in claims it.

Opening a support session needs a sign-in from the last five minutes: for a provider, when it authenticated the person (auth_time). With mfa listed, only a sign-in with more than one factor counts (amr, or a listed acr). Otherwise the dialog offers Confirm at your sign-in provider (prompt=login, max_age=0, acr_values).

oidc.tenants() offers Sign in with your work email: an entry with "tenant": "north", "domains": ["north-motors.com"] is that tenant’s. It is set in the deployment’s config file by whoever runs the platform, never by a tenant or a route; it signs in only verified addresses in its domains, gives no membership (the tenant store does), and a domain is one tenant’s.

  • Entra ID: issuer https://login.microsoftonline.com/<tenant id>/v2.0; add the email optional claim; from: { groups: [...] } or { roles: [...] }. It sends no email_verified: verifiedEmails only for a single-tenant app.
  • Entra External ID: issuer https://<tenant id>.ciamlogin.com/<tenant id>/v2.0.
  • Azure AD B2C: a user flow is its issuer. Pick the token issuer with the policy, https://<name>.b2clogin.com/tfp/<tenant id>/<policy>/v2.0/, and set discovery to https://<name>.b2clogin.com/<name>.onmicrosoft.com/<policy>/v2.0/.well-known/openid-configuration.
  • Okta: issuer https://<org>.okta.com/oauth2/default; add a groups claim; "mfa": ["urn:okta:loa:2fa:any"].
  • Keycloak: issuer https://<host>/realms/<realm>; turn on Add to ID token in the roles mapper, then from: { 'realm_access.roles': [...] }.