Skip to main content

Route Groups

groups selects which bundles a mount registers. Each group is self-sufficient: mounting one never leaves a flow half-served.

{ prefix: 'mobile/auth', delivery: 'json', groups: ['core', 'profile', 'mfa'] }
GroupRoutesDefaultPurpose
core12YesSign-up, sign-in, refresh, sign-out, password recovery, and the challenge responses that complete them
profile3YesThe caller's own profile and password
mfa7YesThe caller's own MFA enrolment and devices
social10YesOAuth sign-in, account linking, and credentials for social-origin users
device4YesSessions and device trust
audit1YesThe caller's own sign-in history
apiKeys5YesThe caller's own API keys
admin25NoOperations on other users' accounts
apiKeysAdmin5NoAPI keys on another user's behalf

Omit groups and every non-admin group is mounted.

Admin groups are opt-in and gated

admin and apiKeysAdmin are never mounted by default, and mounting either requires an authorization provider — the toolkit ships no role model, so without one those endpoints would be reachable by any authenticated caller. Mounting them without a provider is refused at startup.

core​

Everything needed to get a user signed in, and signed out again.

Challenge responses live here, not in a group of their own. login can answer with VERIFY_EMAIL, MFA_REQUIRED or FORCE_CHANGE_PASSWORD, and a caller with no way to respond could not finish signing in. Splitting them would let you mount a login that cannot complete.

KeyMethodPathAccess
signupPOSTsignuppublic
loginPOSTloginpublic
refreshPOSTrefreshpublic
logoutGETlogoutauthenticated
logoutAllPOSTlogout/allauthenticated
forgotPasswordPOSTforgot-passwordpublic
confirmForgotPasswordPOSTforgot-password/confirmpublic
confirmAdminResetPasswordPOSTreset-password/confirmpublic
respondChallengePOSTrespond-challengepublic
resendCodePOSTchallenge/resendpublic
getSetupDataPOSTchallenge/setup-datapublic
getChallengeDataPOSTchallenge/challenge-datapublic

reset-password/confirm is public by necessity: an administrator initiates the reset, but the end user completes it from an emailed link with no session yet. The same is true of challenge/setup-data and challenge/challenge-data, which are reached mid-sign-in and authorized by the challenge session token rather than a JWT.

profile​

The caller acting on their own account.

KeyMethodPathAccess
profileGETprofileauthenticated
updateProfilePUTprofileauthenticated
changePasswordPOSTchange-passwordauthenticated

change-password requires the current password. A user who signed up through a social provider has none — see social for how they add one.

mfa​

Enrolment and device management for the signed-in user. Requires MFA to be configured; the routes are dropped from the mount otherwise.

KeyMethodPathAccess
mfaStatusGETmfa/statusauthenticated
mfaAvailableMethodsGETmfa/available-methodsauthenticated
mfaSetupDataPOSTmfa/setup-dataauthenticated
mfaVerifySetupPOSTmfa/verify-setupauthenticated
mfaBackupCodesPOSTmfa/backup-codes/generateauthenticated
mfaDevicesGETmfa/devicesauthenticated
mfaPreferredPOSTmfa/devices/:deviceId/preferredauthenticated
mfaRemoveDeviceDELETEmfa/devices/:deviceIdauthenticated

These are for a user managing their factors while signed in. The MFA steps during sign-in are challenge responses, and live in core.

social​

OAuth sign-in, linking providers to an existing account, and the credential routes that only apply to social-origin users.

KeyMethodPathAccess
socialLinkedGETsocial/linkedauthenticated
socialLinkPOSTsocial/linkauthenticated
socialUnlinkPOSTsocial/unlinkauthenticated
socialCanSetPasswordGETsocial/can-set-passwordauthenticated
socialSetPasswordPOSTsocial/set-passwordauthenticated
socialExchangePOSTsocial/exchangepublic
socialRedirectStartGETsocial/:provider/redirectpublic
socialCallbackGETsocial/:provider/callbackpublic
socialCallbackPostPOSTsocial/:provider/callbackpublic
socialVerifyPOSTsocial/:provider/verifypublic

Why set-password is here rather than in profile. It is self-service — it refuses unless the caller is acting on their own account — but it only applies to a user who has no password, which in practice means they signed up through a provider. It refuses outright if a password already exists. can-set-password tells the frontend which of the two to offer. Mount profile without social and no such user can exist.

:provider/callback is where the OAuth provider redirects the browser back to; it must match the callbackUrl you register with that provider. Both verbs are served because Apple posts a form when name scopes are requested.

device​

KeyMethodPathAccess
sessionsGETsessionsauthenticated
logoutSessionDELETEsessions/:sessionIdauthenticated
trustDevicePOSTtrust-deviceauthenticated
isTrustedDeviceGETis-trusted-deviceauthenticated
trustedDevicesGETtrusted-devicesauthenticated
revokeAllTrustedDevicesDELETEtrusted-devicesauthenticated
revokeTrustedDeviceDELETEtrusted-devices/:deviceIdauthenticated

audit​

KeyMethodPathAccess
auditHistoryGETaudit/historyauthenticated

Reading another user's history is admin.audit.read and lives in admin.

apiKeys​

The caller's own keys. Requires apiKeys.enabled; dropped from the mount otherwise.

KeyMethodPathAccess
apiKeyCreatePOSTapi-keysauthenticated
apiKeyListGETapi-keysauthenticated
apiKeyUpdatePATCHapi-keys/:keyIdauthenticated
apiKeyRevokePOSTapi-keys/:keyId/revokeauthenticated
apiKeyDeleteDELETEapi-keys/:keyIdauthenticated

Every route denies API-key authentication, so a key cannot mint another key.

admin​

Operations one user performs on another's account. Not mounted by default, and refused at startup without an authorization provider.

Mount it on its own prefix so the surface can be firewalled off separately:

{ prefix: 'admin', groups: ['admin'] }
KeyMethodPathAccess
adminSignupPOSTsignupadmin
adminSignupSocialPOSTsignup-socialadmin
adminSetPasswordPOSTset-passwordadmin
adminResetPasswordInitiatePOSTreset-password/initiateadmin
adminGetUserByEmailGETusers/by-emailadmin
adminGetUsersGETusersadmin
adminGetUserGETusers/:subadmin
adminUpdateUserPUTusers/:subadmin
adminDeleteUserDELETEusers/:subadmin
adminDisableUserPOSTusers/:sub/disableadmin
adminEnableUserPOSTusers/:sub/enableadmin
adminForcePasswordChangePOSTusers/:sub/force-password-changeadmin
adminUpdateVerifiedStatusPOSTusers/:sub/verified-statusadmin
adminGetUserTrustedDevicesGETusers/:sub/trusted-devicesadmin
adminRevokeAllUserTrustedDevicesDELETEusers/:sub/trusted-devicesadmin
adminRevokeUserTrustedDeviceDELETEusers/:sub/trusted-devices/:deviceIdadmin
adminGetUserSessionsGETusers/:sub/sessionsadmin
adminRevokeUserSessionDELETEusers/:sub/sessions/:sessionIdadmin
adminLogoutAllPOSTusers/:sub/logout-alladmin
adminSetMfaExemptionPOSTmfa/exemptionadmin
adminRemoveMfaDeviceDELETEmfa/devices/:deviceIdadmin
adminGetMfaStatusGETusers/:sub/mfa/statusadmin
adminGetMfaDevicesGETusers/:sub/mfa/devicesadmin
adminSetPreferredMfaDevicePOSTusers/:sub/mfa/devices/:deviceId/preferredadmin
adminGetEventsByTypeGETaudit/eventsadmin
adminGetSuspiciousActivityGETaudit/suspiciousadmin
adminGetRiskAssessmentHistoryGETaudit/riskadmin
adminGetAuditHistoryGETaudit/historyadmin

Every route denies API-key authentication, so apiKeys.globalAllowlist cannot expose them. Each declares an action that the service authorizes independently — so the same policy covers a controller you write and a script, not just these routes.

apiKeysAdmin​

API keys issued and managed on another user's behalf. Same gating as admin.

APIKEYS| Key | Method | Path | Access | | --- | --- | --- | --- | | adminSignup | POST | signup | admin | | adminSignupSocial | POST | signup-social | admin | | adminSetPassword | POST | set-password | admin | | adminResetPasswordInitiate | POST | reset-password/initiate | admin | | adminGetUserByEmail | GET | users/by-email | admin | | adminGetUsers | GET | users | admin | | adminGetUser | GET | users/:sub | admin | | adminUpdateUser | PUT | users/:sub | admin | | adminDeleteUser | DELETE | users/:sub | admin | | adminDisableUser | POST | users/:sub/disable | admin | | adminEnableUser | POST | users/:sub/enable | admin | | adminForcePasswordChange | POST | users/:sub/force-password-change | admin | | adminUpdateVerifiedStatus | POST | users/:sub/verified-status | admin | | adminGetUserTrustedDevices | GET | users/:sub/trusted-devices | admin | | adminRevokeAllUserTrustedDevices | DELETE | users/:sub/trusted-devices | admin | | adminRevokeUserTrustedDevice | DELETE | users/:sub/trusted-devices/:deviceId | admin | | adminGetUserSessions | GET | users/:sub/sessions | admin | | adminRevokeUserSession | DELETE | users/:sub/sessions/:sessionId | admin | | adminLogoutAll | POST | users/:sub/logout-all | admin | | adminSetMfaExemption | POST | mfa/exemption | admin | | adminRemoveMfaDevice | DELETE | mfa/devices/:deviceId | admin | | adminGetMfaStatus | GET | users/:sub/mfa/status | admin | | adminGetMfaDevices | GET | users/:sub/mfa/devices | admin | | adminSetPreferredMfaDevice | POST | users/:sub/mfa/devices/:deviceId/preferred | admin | | adminGetEventsByType | GET | audit/events | admin | | adminGetSuspiciousActivity | GET | audit/suspicious | admin | | adminGetRiskAssessmentHistory | GET | audit/risk | admin | | adminGetAuditHistory | GET | audit/history | admin |