feat(m3): native auth — login+TOTP, token storage, refresh, access-level menu
All checks were successful
PR Checks / android-build (pull_request) Successful in 9m41s
All checks were successful
PR Checks / android-build (pull_request) Successful in 9m41s
Implements M3 (docs/android/PLAN.md §4): the functional Kotlin auth pass.
- Native username/password (+ single-request TOTP) login over the existing
POST /auth/mobile/login; a 401 { totpRequired } reveals the code field, 429
surfaces a backoff message (§4.1).
- Token pair in EncryptedSharedPreferences (TokenStore behind SessionManager,
the single source of truth for the in-memory bearer + observable Session);
base URL stays in plain DataStore (§4.3).
- OkHttp AuthInterceptor (bearer) + TokenAuthenticator: one-shot, mutex-
serialized refresh-on-401 that replays the request, on its own bare client so
it can never recurse; single-use rotation; dead refresh signs out, transient
network keeps the session.
- Logout (POST /auth/mobile/logout, this session or all devices) tears down
locally even on failure.
- GET /auth/me re-validates the role on every resume; a surviving 401 signs out
(role stays advisory — backend is authority).
- Declarative access-level menu (visibleEntries: public/signed-in/player) with a
Sign in / Sign out toggle + a My Account screen.
- Custom-Tab hand-offs (androidx.browser) to the website for register / forgot-
password / SSO — no native screens (§4.2).
- Settings → Server switch now also clears the stored session (§3).
Biometric app-lock is deferred to M6 (tokens already encrypted at rest; it is
opt-in UX, not a v1 requirement — decided at M3).
JVM unit tests (18): auth-DTO decode (incl. totpRequired vs a plain credential
401), the SessionManager lifecycle over a fake store, and the menu access filter
+ role mapping. No backend/API change — a pure consumer of the existing mobile
bearer + /auth/me surface.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NgyHnrNa8WwG3doxvxjuCr
This commit is contained in:
@@ -41,8 +41,38 @@
|
||||
<string name="menu_shard">Shard</string>
|
||||
<string name="menu_about">About</string>
|
||||
<string name="menu_contact">Contact</string>
|
||||
<string name="menu_account">My account</string>
|
||||
<string name="menu_sign_in">Sign in</string>
|
||||
<string name="menu_sign_out">Sign out</string>
|
||||
<string name="menu_change_server">Change server</string>
|
||||
|
||||
<!-- ── Auth: login (§4.1) ──────────────────────────────────────────── -->
|
||||
<string name="login_title">Sign in</string>
|
||||
<string name="login_subtitle">Sign in with your shard account.</string>
|
||||
<string name="login_username">Username</string>
|
||||
<string name="login_password">Password</string>
|
||||
<string name="login_totp_code">Authentication code</string>
|
||||
<string name="login_totp_hint">Enter the 6-digit code from your authenticator app.</string>
|
||||
<string name="login_button">Sign in</string>
|
||||
<string name="login_register">Create an account</string>
|
||||
<string name="login_forgot">Forgot your password?</string>
|
||||
<string name="login_sso">Sign in with Google or Discord (on the website)</string>
|
||||
<string name="login_error_credentials">Incorrect username or password.</string>
|
||||
<string name="login_error_code">That code didn\'t match. Try the current code.</string>
|
||||
<string name="login_error_rate_limited">Too many attempts. Please try again shortly.</string>
|
||||
<string name="login_error_server">Something went wrong. Please try again.</string>
|
||||
<string name="login_error_network">Can\'t reach the site. Check your connection and try again.</string>
|
||||
|
||||
<!-- ── Auth: account (§5, §6.3) ────────────────────────────────────── -->
|
||||
<string name="account_title">My account</string>
|
||||
<string name="account_sign_out">Sign out</string>
|
||||
<string name="account_sign_out_all">Sign out on all devices</string>
|
||||
<string name="role_player">Player</string>
|
||||
<string name="role_moderator">Moderator</string>
|
||||
<string name="role_editor">Editor</string>
|
||||
<string name="role_admin">Administrator</string>
|
||||
<string name="role_unknown">Signed in</string>
|
||||
|
||||
<!-- ── Home / status (§6.1) ────────────────────────────────────────── -->
|
||||
<string name="home_status_live">Online</string>
|
||||
<string name="home_status_maintenance">Under maintenance</string>
|
||||
|
||||
Reference in New Issue
Block a user