docs(auth): design the mobile SSO authorization bridge (M9)
Record the plan before coding: native Android "Sign in with Google/Discord"
via a Mobile SSO Authorization Bridge that extends the existing /auth/sso/*
redirect flow and terminates in the existing mobile bearer tokens.
- BACKEND_DESIGN.md: mobile_auth_sessions / mobile_auth_codes schema, the
/auth/mobile/sso/{start,exchange} contract, the two PKCE layers, state/CSRF,
exact-match redirect-URI allowlist, TOTP parity, and the documented
revocation-latency window.
- android/PLAN.md: promote §4.2's "possible later enhancement" to milestone M9
(backend-first, mirroring M7); status note.
- android/APP_LINKS.md: new architecture note on the per-shard assetlinks.json
/ pairing multi-tenancy question (App Links deferred; custom scheme only now).
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Android App — Plan
|
||||
|
||||
Status: **M0–M7 landed; M7 (push notifications) both parts done — Part 1 backend (website#78) and Part 2 app (Android-app#15) plus a small `push.ntfyUrl` settings addition (website#79). Remaining: set the shard's `NTFY_*` deploy config so push lights up, and cut the v1 tag.** This document is the
|
||||
Status: **M0–M7 landed; M7 (push notifications) both parts done — Part 1 backend (website#78) and Part 2 app (Android-app#15) plus a small `push.ntfyUrl` settings addition (website#79). Remaining: set the shard's `NTFY_*` deploy config so push lights up, and cut the v1 tag. M9 (native SSO login) is now underway backend-first — the Mobile SSO Authorization Bridge is being built in `website/` + `docs/` ahead of the app-side client (§4.2, §9 M9); custom-scheme callback only for now, App Links deferred (see [`APP_LINKS.md`](./APP_LINKS.md)).** This document is the
|
||||
design contract for the `RunicGateway/Android-app` repo. It was written before implementation so the
|
||||
API changes it depends on could be landed in `website/` and `docs/` first. The authoritative API
|
||||
reference is the committed OpenAPI spec at `website/server/swagger/swagger-output.json` (regenerated
|
||||
@@ -467,14 +467,19 @@ completes them in a Custom Tab, then returns and signs in natively (§4.1):
|
||||
new username + password. (No mobile register/invite endpoints needed.)
|
||||
- **Forgot / reset password** — the app links to the website's reset page (the flow being built in §8
|
||||
before app work). The user resets there, then signs into the app. (No mobile reset endpoint needed.)
|
||||
- **SSO (Google / Discord / OIDC)** — SSO stays the website's browser redirect flow (`/auth/sso/*`),
|
||||
**link-only** (no auto-provisioning). For v1 the app does **not** do one-tap in-app SSO; instead an
|
||||
SSO user links their identity and sets a password on the website (the existing "set initial password"
|
||||
path for SSO-provisioned accounts), then uses password login in the app. `GET /auth/sso/providers`
|
||||
can still be shown so the login screen can direct users to "sign in with … on the website."
|
||||
- *Possible later enhancement (out of v1):* true in-app SSO via a Custom-Tab flow that hands a
|
||||
one-time code back to an app link, exchanged for mobile tokens — a small new backend endpoint. Only
|
||||
build it if password-for-SSO-users proves too clunky.
|
||||
- **SSO (Google / Discord / OIDC)** — **v1** shipped this as a website browser hand-off: an SSO user
|
||||
links their identity and sets a password on the website, then uses password login in the app.
|
||||
`GET /auth/providers` is shown so the login screen can direct users to "sign in with … on the website."
|
||||
- **Native in-app SSO — now being built (M9), post-v1 additive.** The "possible later enhancement"
|
||||
noted here is now the **Mobile SSO Authorization Bridge**: a Custom-Tab flow that hands a one-time
|
||||
code back to the app's fixed callback (`runicgateway://auth/callback`), exchanged for the *existing*
|
||||
mobile bearer tokens. It **extends** the existing `/auth/sso/*` redirect flow rather than adding a
|
||||
parallel auth path — same PKCE-vs-IdP, same link-only + opt-in-provisioning policy, same TOTP gate,
|
||||
same token shape as `/auth/mobile/login`. The bridge adds a **second** PKCE layer (app ↔ website)
|
||||
and an app-generated `state` (CSRF, verified by the app before exchange). Backend + docs land first
|
||||
(this document's canonical API ref is `../website/BACKEND_DESIGN.md` → "Mobile SSO Authorization
|
||||
Bridge"); the native app client is M9. Custom-scheme callback only for now — App Links are deferred
|
||||
(see [`APP_LINKS.md`](./APP_LINKS.md)).
|
||||
|
||||
### 4.3 Session model (all paths)
|
||||
- **Refresh:** `POST /auth/mobile/refresh` `{ refreshToken }` → new pair. **Single-use / rotated:** store
|
||||
@@ -704,6 +709,23 @@ push, and Play (M6–M8) follow the designed app.
|
||||
its `NTFY_*` deploy config (§13).
|
||||
9. **M8 — Google Play**: Play Console listing, signing/upload key, and (optionally) an FCM build flavor
|
||||
— after the direct-APK release is stable.
|
||||
10. **M9 — Native SSO login** (post-v1, additive; independent of M8): in-app "Sign in with Google /
|
||||
Discord" via the **Mobile SSO Authorization Bridge** (§4.2). **Backend-first**, mirroring M7's
|
||||
split:
|
||||
- **Part 1 — backend + docs (in progress):** `mobile_auth_sessions` + `mobile_auth_codes` bridge
|
||||
tables; `GET /auth/mobile/sso/start` (seeds a bridge session, reuses the existing SSO redirect
|
||||
tagged `mode:'mobile'`); a mobile branch in the SSO callback + TOTP-completion that mints a
|
||||
single-use, hashed, PKCE-bound authorization code and redirects to the fixed app callback instead
|
||||
of setting a cookie; `POST /auth/mobile/sso/exchange` (code + PKCE verifier → the existing mobile
|
||||
bearer token pair); an exact-match redirect-URI allowlist; boot-time + opportunistic cleanup of
|
||||
the bridge tables. Reuses `GET /auth/providers` for discovery and `POST /auth/mobile/{refresh,
|
||||
logout}` unchanged. See `../website/BACKEND_DESIGN.md`.
|
||||
- **Part 2 — app client:** register the `runicgateway://auth/callback` intent-filter; generate
|
||||
`code_verifier`/`code_challenge` + `state`; open the Custom Tab at `/auth/mobile/sso/start`;
|
||||
verify `state` on the callback; `POST …/exchange`; store the returned pair in the existing
|
||||
`TokenStore` (M3). No new token-storage or refresh code — it feeds the M3 session machinery.
|
||||
- **Deferred:** App Links / per-shard `assetlinks.json` / pairing — custom scheme only for now
|
||||
([`APP_LINKS.md`](./APP_LINKS.md)).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user