The security module under /profile/security/* adds optional second-factor authentication, device tracking, and session management. Once 2FA is enabled, the sign-in flow gains a challenge step before issuing a full token.
2FA methods
TwoFactorService supports multiple second-factor methods, configured per-User:
- TOTP (authenticator apps — Google Authenticator, Authy, 1Password)
- Email code
- SMS code (requires a phone number on the User record and a connected SMS provider)
Each method goes through the same lifecycle: setup → verify-setup → enable. Once enabled, sign-in returns a partial token instead of a full one, and the client must complete a challenge before the session unlocks.
Status
/profile/security/2fa/statusJWTReturns the current 2FA configuration for the signed-in User — which methods are enabled, how many backup codes remain.
{
"enabled": true,
"methods": [{ "type": "totp", "enabled": true, "verifiedAt": "..." }],
"backupCodesRemaining": 8
}
Setup flow
- 1
Initiate setup
Pick a method and start the setup. For TOTP, the response includes the QR code and the shared secret.
POST/profile/security/2fa/setup/{method}JWTcurl -X POST https://appengine.appmint.io/profile/security/2fa/setup/totp \ -H "orgid: my-org" -H "Authorization: Bearer $JWT"{ "method": "totp", "secret": "JBSWY3DPEHPK3PXP", "qrCode": "data:image/png;base64,...", "otpauthUrl": "otpauth://totp/AppMint:[email protected]?..." } - 2
Verify the user can produce a code
Have the user enter a code from their app, post it back to confirm the setup is wired correctly.
POST/profile/security/2fa/verify-setupJWT{ "method": "totp", "code": "123456" }Returns
{ verified: true }on success. - 3
Enable
Flip 2FA on for the account. Subsequent sign-ins require the second factor.
POST/profile/security/2fa/enableJWT{ "method": "totp" }
Disabling 2FA
/profile/security/2fa/disableJWTRequires the current password (or a recent recent challenge — depends on org policy). Disables all methods at once; if you want to swap methods, set up the new one first then disable the old.
Backup codes
Single-use recovery codes for when the user loses their TOTP device.
/profile/security/2fa/backup-codesJWTGenerates a fresh batch (typically 10) and invalidates any previous batch. Show the codes to the user once; they're stored hashed.
/profile/security/2fa/backup-codes/countJWTReturns the number of unused codes left. Prompt the user to regenerate when this gets low.
Sign-in challenge
When 2FA is enabled, POST /profile/signin returns a challenge token instead of a full session:
{
"data": {
"challenge": { "token": "...", "methods": ["totp", "email"] },
"user": { "pk": "...", "email": "..." }
}
}
Send the challenge token to the user's chosen method, then verify the code:
/profile/security/challenge/sendNo auth/profile/security/challenge/verifyNo auth/challenge/send is for methods that need a server-issued code (email, SMS). TOTP doesn't need a send step — the user already has the code in their authenticator app.
/challenge/verify exchanges the challenge token + the verification code for a real JWT pair. From here on, the session works like any other.
curl -X POST https://appengine.appmint.io/profile/security/challenge/verify \
-H "orgid: my-org" -H "Content-Type: application/json" \
-d '{ "challengeToken": "...", "method": "totp", "code": "123456" }'
Devices
Every successful sign-in registers a device record — user agent, IP, location (geo-IP), trust state. Users (and admins) can review and revoke them.
/profile/security/devicesJWT/profile/security/devices/currentJWT/profile/security/devices/{deviceId}/trustJWT/profile/security/devices/{deviceId}/blockJWT/profile/security/devices/{deviceId}JWTtrust marks a device as remembered — subsequent sign-ins from the same device may skip the 2FA challenge depending on policy. block hard-locks a device; DELETE revokes the active session and forgets the device record.
devices/current returns just the device handling the current request — handy for "is this device trusted" UI prompts.
Login history
/profile/security/login-historyJWTPaginated list of past sign-ins for the current User: timestamp, IP, user agent, device id, success/failure. Surface this in account settings so users can spot suspicious activity.
Security settings
/profile/security/settingsJWTAggregated read of every security knob for the User — 2FA status, trusted devices count, recent login summary, password-changed-at. Use this to render a one-page security dashboard.
Org-wide policy
ConfigAdmins can enforce 2FA org-wide via Org Management settings. When the policy is on:
- Every User must enable 2FA before their first session expires.
- Sign-in flows respect a grace period (configurable, default 7 days).
- Customers are unaffected — the policy is per-User-collection.
For Customer-facing 2FA (storefronts, member portals), use the same endpoints — the underlying service handles both principal types. Most Customer-facing apps choose passwordless (magic link) instead, with 2FA reserved for high-trust user accounts.