Merge pull request 'docs(android): record trusted-devices app implementation in PLAN §4.1.1' (#33) from docs/trusted-devices-mfa-app into main
Reviewed-on: #33 Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
This commit is contained in:
@@ -544,25 +544,37 @@ Uses the existing **mobile bearer** surface, no backend changes:
|
|||||||
- `POST /auth/mobile/logout` `{ refreshToken?, all? }` (requires bearer) — revoke this session or all
|
- `POST /auth/mobile/logout` `{ refreshToken?, all? }` (requires bearer) — revoke this session or all
|
||||||
sessions. Called on user logout and on "sign out everywhere."
|
sessions. Called on user logout and on "sign out everywhere."
|
||||||
|
|
||||||
#### 4.1.1 Trusted devices & recovery codes — backend ready, app work sequenced after backend
|
#### 4.1.1 Trusted devices & recovery codes — **implemented** (app PR `feature/trusted-devices-mfa`)
|
||||||
The backend trusted-device + recovery-code feature (canonical ref: `../website/TRUSTED_DEVICES_MFA.md`)
|
The backend trusted-device + recovery-code feature (canonical ref: `../website/TRUSTED_DEVICES_MFA.md`)
|
||||||
is additive on the mobile surface; the app consumes it as follows (implemented **after** the backend
|
is additive on the mobile surface. As built:
|
||||||
lands, alongside the account screens):
|
|
||||||
- **Login extras:** `POST /auth/mobile/login` accepts `recoveryCode` (a single-use alternative to
|
- **Login extras:** `POST /auth/mobile/login` accepts `recoveryCode` (a single-use alternative to
|
||||||
`code`), `trustDevice: true`, and an `X-Trust-Token` header. On the `401 { totpRequired }` screen,
|
`code`), `trustDevice: true`, `device_name`, and an `X-Trust-Token` header. On the `401 { totpRequired }`
|
||||||
offer "use a recovery code instead" and a **"Trust this device"** checkbox. When `trustDevice` is set
|
screen the app offers a "use a recovery code instead" toggle and a **"Trust this device"** checkbox.
|
||||||
and accepted, the response carries `trustToken` → **store it in EncryptedSharedPreferences** (same
|
When `trustDevice` is accepted, the response carries `trustToken` → stored in a **dedicated
|
||||||
store as the bearer tokens, never plain prefs/logs) and send it as `X-Trust-Token` on future logins
|
EncryptedSharedPreferences file** (`runic_trust`, AES-256-GCM; never plain prefs/logs) **scoped to the
|
||||||
to skip the TOTP prompt. A `{ trustLimitReached, devices }` response means show the device list and
|
username** it was minted for, and replayed as `X-Trust-Token` on that account's future logins to skip
|
||||||
prompt the user to revoke one (`DELETE /auth/me/trusted-devices/:id`) then retry.
|
the TOTP prompt.
|
||||||
- **Self-service (account screens):** `GET /auth/me/trusted-devices`, `DELETE …/:id`, `DELETE
|
- **Login-time cap:** a `{ trustLimitReached, devices }` response means login **succeeded** but the
|
||||||
…/trusted-devices` (untrust all); `POST /auth/me/trusted-devices` to trust the current device from an
|
device was not remembered (session is already issued on native, unlike the web cookie step). Rather
|
||||||
authenticated session (returns `{ trustToken }` for native). Recovery codes: enabling TOTP returns
|
than a blocking login-time modal, this is surfaced + resolved on the **Trusted Devices** account
|
||||||
the one-time `recoveryCodes` (show once, offer copy/share); `GET …/recovery-codes/status` for the
|
screen, whose `POST /auth/me/trusted-devices` gives the exact revoke-one-then-retry flow. (Deliberate
|
||||||
remaining count; `POST …/recovery-codes/generate` (password step-up) to regenerate.
|
deviation from the earlier "prompt at login" sketch — the mobile login is past the point a modal would
|
||||||
- **Invalidation:** on logout / dead-refresh sign-out / Settings→Server switch, **clear the stored
|
gate.)
|
||||||
`trustToken`** along with the bearer tokens (a password change/reset or TOTP disable already revokes
|
- **Self-service (account screens):** a **Security** section on the account screen links to two
|
||||||
it server-side).
|
dedicated screens. **Trusted Devices** — `GET /auth/me/trusted-devices` (list), `DELETE …/:id`
|
||||||
|
(revoke one), `DELETE …/trusted-devices` (untrust all), and `POST /auth/me/trusted-devices` to trust
|
||||||
|
the current device (stores the returned `{ trustToken }`). **Recovery Codes** — enabling TOTP returns
|
||||||
|
the one-time `recoveryCodes` (shown once on the account screen with copy/share); `GET
|
||||||
|
…/recovery-codes/status` for the remaining count; `POST …/recovery-codes/generate` (password step-up)
|
||||||
|
to regenerate, shown once.
|
||||||
|
- **Invalidation — trust token deliberately survives logout.** The trust token is the native analogue
|
||||||
|
of the web `rg_trust` cookie, which per the canonical design *survives logout so the next login skips
|
||||||
|
2FA*. On mobile the token is only ever consulted at a **fresh** login — i.e. exactly after a logout or
|
||||||
|
a dead-refresh sign-out — so clearing it there would make the feature a no-op. It is therefore kept in
|
||||||
|
its own store, **untouched by session teardown** (`SessionManager.onSignedOut`), and cleared only on a
|
||||||
|
**Settings → Server switch** (bound to the old host), an **untrust-all**, or server-side revocation
|
||||||
|
(password change/reset, TOTP disable — which makes any surviving token inert; the next login just
|
||||||
|
prompts for the code). This supersedes the earlier handoff note that said to clear it on logout.
|
||||||
|
|
||||||
### 4.2 Website-handled flows: registration, invite, forgot-password, SSO
|
### 4.2 Website-handled flows: registration, invite, forgot-password, SSO
|
||||||
These are **not** rebuilt in the app. The app links out to the website's own pages/API and the user
|
These are **not** rebuilt in the app. The app links out to the website's own pages/API and the user
|
||||||
|
|||||||
Reference in New Issue
Block a user