Organizations — B2B Login

Org selection on login · invitation-based signup · org_id claim and org-scoped roles

Org Login & Invitation Flow

Login /authorize

organization=org_id on the authorization request

ID Token

org_id claim (+ org_name if enabled tenant-wide)

Session

every request in this session is scoped to that org

1A direct member logs in with organization=org_id already known to the app — this demo's app is configured organization_require_behavior: no_prompt, so it decides when to pass it.
2An invited user instead follows an invitation link: /authorize?invitation=...&organization=org_id&organization_name=... — Auth0 generates this from the Management API's Create Invitation call.
3Redeeming the invitation signs the invitee up (or logs them in, if they already exist), marks their email verified, and auto-joins them to the org with whatever roles the invitation specified.
4Roles assigned via organizations/{id}/members/{user}/roles are org-scoped — they only apply while authenticated through that org, not globally.

Three login-flow modes, one app can only run one

organization_require_behavioris configured per Auth0 application, not tenant-wide — but it's still static per app, not something that flips per request. This demo's app runs no_prompt (the app always decides). The other two modes: post_login_prompt (user logs in first, then picks an org via Auth0's org selector) and pre_login_prompt (user picks or is routed to an org before Universal Login even starts, via Organization Discovery by email domain or name).

Auth0 docs — Login Flows for Organizations.

Not signed in

This demo's tenant has one Organization pre-provisioned with an existing member and an org-scoped admin role already assigned. Sign in as that member, or use the invitation link below to see the other half of the flow — a brand new user joining the same org via redemption.

Log in to the organization →
Try the invitation flow

The invitation used here was created once via the Management API during setup — this demo doesn't mint invitations on demand, to avoid needing a Management-API-calling credential just to show what redemption looks like. Following the link takes you through /authorize?invitation=...&organization=..., where Auth0 signs the invitee up, verifies their email, and joins them to the org with the role the invitation specified — all in one step, before this app sees any of it.

Ask whoever set up this demo for a live invitation link — it's tenant-specific and expires, so it isn't hardcoded into this page.

What this pattern demonstrates

Org selection happens at login, not after — the organization parameter travels on the authorize request itself. This app is configured organization_require_behavior: no_prompt, so it decides when to pass it, rather than Auth0 prompting the user to pick.

Invitations are a complete onboarding step, not just an email — redeeming one signs up the invitee, verifies their email, and applies roles in a single Auth0-hosted flow, with no follow-up API call from this app required.

Roles are scoped to the org, not the user globally — a role assigned via organizations/{id}/members/{user}/roles only applies while the session is authenticated through that specific org. The roles shown above come from event.authorization.rolesin a dedicated post-login Action, gated to this demo's own client_id so it has no effect on any other demo's login.

Security Implications

Strengths

  • Membership and org-scoped roles live at the platform layer (Auth0's own organization/member/role model), not bolted on via app-level custom claims or a separate authorization store the app has to keep in sync itself.
  • Invitation-based signup marks the invitee's email verified and applies the intended roles atomically on redemption — there is no window where an invited user exists without the access the inviter intended, or needs a second manual step.

Risks & Considerations

  • organization_require_behavior is configured per Auth0 application, not tenant-wide, and is static per app — it is easy to assume a single client can flip between the three login-flow modes per request when it cannot; the app has to be provisioned for the mode it needs.
  • An org-scoped role only applies while a session is authenticated through that org — a stale assumption that a role check is global (rather than checked against the currently active org_id) can silently under- or over-authorize a user who is a member of more than one org.
  • Invitation links carry a ticket that grants real signup + role assignment on redemption — treat them with the same handling care as a password-reset link, not as a low-sensitivity URL.

Hardening Checklist

  • Validate org_id, not org_name, when checking token claims — Auth0's own guidance is explicit that org_id is the preferred identifier for authorization decisions, with org_name treated as a secondary, use-case-specific option.
  • Re-derive the active org from the validated session/token on every request that makes an authorization decision, rather than caching "which org is the user in" client-side across a session that could switch orgs.
  • Set a short, deliberate ttl_sec on invitations (this demo uses 7 days) and treat unredeemed invitations as an attack surface worth expiring, not a fire-and-forget artifact.