Authentication
All calls to the Management API should be authenticated. Any call with missing or incorrect authentication will result in a 401 error.
Authentication headers
The authentication method for this API is the HTTP Bearer method. The HTTP call should contain an Authorization header, with a Bearer JWT token. This token should be issued by the JWT issuer specified in the environment variables. See the API .envrc.example for this.
Environment variables: identity provider sync and JWT user lookup
Two environment variables control how the API relates to the identity provider (IdP) and how a validated JWT is mapped to a user in the Management API database. Both are documented in the Management API .envrc.example and can be set in deployment (for example Azure Web App application settings).
API_DISABLE_IDP_SYNC
When set to true, the Management API does not call the IdP to create, update, or delete user accounts. User rows are maintained in the database only.
Implications:
- User creation (and similar flows) must supply
identityProviderIdon the request body when creating a user, because the API will not create the user in the IdP nor fetch an IdP subject from there. IfidentityProviderIdis omitted while sync is disabled, the API responds with400 Bad Request. - When IdP sync is enabled (
API_DISABLE_IDP_SYNCunset orfalse), clients must not sendidentityProviderIdon create; the API creates the user in the IdP (or retrieves it if it already exists) and stores the returned identifier. - Initialization follows the same rule: when sync is disabled, the administrator’s external key must be provided (for example
adminIdpIdin the init payload, where applicable). - JWT signature validation (issuer, audience, JWKS) is unchanged: the API still validates Bearer tokens against the configured issuer. Disabling sync only removes provisioning calls to the IdP, not JWT verification.
- Features that require the API to create or manage IdP accounts while the caller is unauthenticated may be unavailable when sync is disabled (for example self-check-in flows that expect IdP user creation).
JWT_USER_LOOKUP_CLAIM
After the JWT signature and standard claims (issuer, audience, expiry) are validated, the API must resolve the caller to a single user row in the database. That resolution uses the value of one JWT payload claim, compared to the user’s stored identityProviderId column.
JWT_USER_LOOKUP_CLAIMnames which JWT claim provides that value. The default issub, the OIDC subject identifier. This matches the common case whereidentityProviderIdstores the IdP’s stable subject.- If you set it to another claim name (for example
email), the string value of that claim (after decoding the JWT) must exactly match theidentityProviderIdstored for the user when IdP sync is disabled (or whenever you rely on a non-subkey). There is no separate “login email” column used for this lookup. - If the chosen claim is missing, empty, or not usable as a non-empty string (for example an array of non-strings), the user is treated as unknown to the platform for that request: endpoints that require a resolved user will not succeed as that user.
- If the claim is present as a JSON array of strings (typical for Azure AD B2C
emails), the API uses the first non-empty string element when the configured claim name points at that array.
Changing JWT_USER_LOOKUP_CLAIM requires a process restart in typical deployments so the new value is picked up. Treat it as a security-sensitive setting: it defines how every authenticated request binds tokens to internal users.
Implementation
The authentication is verified using passportjs through the passport-jwt strategy. The call to the authentication provider's well-known URL is done through the helper library jwks-rsa.
Once the token validation has succeeded, the API reads the claim named by JWT_USER_LOOKUP_CLAIM (default sub) from the verified JWT payload and looks up the user whose identityProviderId equals that value.
If no matching user is found, the principal is not a known platform user for that request, and downstream handling will fail for routes that require an identified user (in line with missing authentication above).
If the user is found, the found User object is attached to the request for further processing. The next step is authorization.