processOS uses Auth0 with Organizations for identity and affiliation. Deployment ownership and app grants come from the required deployment policy, not Auth0 organization roles. Keep existing participant accounts and organizations in place.
/login identity sign-in, no organization ↓ /login/select-org pick an org ↓ silent org-scoped authorize (organization + prompt=none) ↓ app token now carries org_id + permissions
Two authorizations. The first proves who you are; the second scopes you to an org and is
what puts permissions in the access token.
Invite links skip the first: one org-scoped authorize with organization + invitation.
Client state lives in localStorage as process-platform-token and
process-platform-organization, per app origin.
Access tokens last 24 hours and each app keeps its own. So users are not sent to the sign-in page every day on every app, an app renews silently against the Auth0 session instead:
401 from the API. Never on a timer, so a form the user
is filling in is not navigated away from./api/auth/login?prompt=none&renew=1&returnUrl=… (plus organization
when the app already has one). With a live Auth0 session (signed in to any processOS app on the
same tenant) the user comes straight back to the same page, signed in, with no click. A new app
goes through identity renewal and then the usual organization step.login_required, consent_required or interaction_required,
the callback sends the user to this app's /login with the same return path. The signed OAuth
state carries renew, so a failed renewal never escalates to an interactive login on its own.sessionStorage process-platform-silent-renewal).process-platform-signed-out, which suppresses renewal on
that app until the next completed sign-in (the callback clears it).Nothing about Auth0 changes: same tenant settings, token lifetime and sign-in methods.
Enable Organizations. Create each org and note its org_… id.
Enable Organizations for the app. Set Application Login URI to
{APP_BASE_URL}/login.
Add every web origin — default and each skin — to:
Configure the audience and supported Auth0 login/access-token flow. Existing RBAC claims can remain for compatibility with other consumers, but this executor ignores them as authorization grants.
Provision the deployment policy with verified-email memberships and explicit operator grants.
Do not fix an app 403 by adding a global Auth0 role; check the target app's declaration instead.
Unverified identities are denied even if their email is declared. An empty unscoped /api/me.permissions
can be normal: app grants are in applicationPermissions or the target-scoped identity response.
AUTH0_MGMT_CLIENT_ID / AUTH0_MGMT_CLIENT_SECRET, an M2M application with scopes:
read:organizations read:users read:roles
Used for the org picker and verified identity/member resolution, including impersonation. App permissions come from declarations, not provider role lookup. Explicit invitations require additional provider permissions and configured onboarding settings; startup never sends them.
On the API, Settings → Allow Skipping User Consent.
Without it the silent org-scoped authorize returns consent_required and login stalls.
The app retries without prompt=none so Auth0 can show the consent screen once, but
enabling the setting avoids that path.
curl -s $BASE/api/me -H "Authorization: Bearer $TOKEN" \
-H "X-Process-Platform-Organization: org_..."
{ "userId": "auth0|…", "organizationId": "org_…",
"permissions": ["templates:read", "processes:read", …],
"canImpersonate": false, "impersonating": false }
An empty permissions array with a valid token means step 4 — roles assigned globally
rather than at org level.
Requires user:impersonate. The caller sends:
X-Impersonate-User-Id: auth0|… X-Impersonate-Organization-Id: org_… (optional, defaults to the actor's org)
The effective principal becomes that user in the target org, with that user's
org-role permissions fetched via the Management API. The actor's own JWT and
X-Process-Platform-Organization stay on the actor's org — only the effective principal
switches.
The real actor is preserved on principal.impersonatedBy and surfaced as actor on
/api/me.
Because it resolves permissions live, user:impersonate is effectively tenant-wide
access to every org the target belongs to. Grant it narrowly. Note also that
impersonated writes use the effective user id in canonical field/completion audit. The
original actor is exposed on /api/me, but that is not a separately persisted original-actor
audit guarantee for every command.
Use the supported Auth0 login/access-token flow for application and script requests. There is no
packaged token-minting CLI: the former mint-long-lived-jwt package command pointed to a missing
script and has been removed. JWT_SECRET is not an application access-token setting.
Repository verification uses controlled synthetic identities/JWKS on isolated local origins. Those fixtures test permission and organization behavior without live credentials; they are not a production authentication bypass or a substitute for verifying the actual Auth0 configuration.