diff --git a/docs/superpowers/specs/2026-05-08-mini-program-auth-restore-design.md b/docs/superpowers/specs/2026-05-08-mini-program-auth-restore-design.md new file mode 100644 index 0000000..c669c23 --- /dev/null +++ b/docs/superpowers/specs/2026-05-08-mini-program-auth-restore-design.md @@ -0,0 +1,114 @@ +# Mini Program Auth Restore And Diagnostic Logs Design + +## Problem + +After the mini program is built and uploaded, it can enter the onboarding/system initialization flow without a real login. The current startup logic treats a local `access_token` as proof of authentication. If the backend API is unavailable, the token is expired, or `fetchUserProfile()` fails, the splash page still routes to onboarding because `hasProfile` is false. + +This makes production failures hard to diagnose because the startup path has little structured logging for environment, API target, token validation, refresh, and routing decisions. + +## Goals + +- Never enter onboarding or the main app unless the current session is accepted by the backend. +- If `access_token` validation fails, try `refresh_token` once before sending the user to login. +- Clear local tokens only when refresh/auth recovery fails or the backend explicitly rejects authentication. +- Add production-safe console logs that show the current environment, backend API/WS configuration, auth stage, request path, response status, and route decision. +- Keep the change scoped to mini program startup auth, request handling, and diagnostics. + +## Non-Goals + +- No UI redesign. +- No backend contract changes unless an existing endpoint path is proven wrong during implementation. +- No new routing framework or broad page refactor. + +## Current Findings + +- `mini-program/src/pages/splash/index.vue` checks `uni.getStorageSync('access_token')` directly. If any token exists, it calls `fetchUserProfile()`, then routes to main when `hasProfile` is true or onboarding when false. +- `mini-program/src/stores/app.js` has the same pattern in `initialize()`: local token means `isLoggedIn = true`. +- `fetchUserProfile()` catches errors and returns `null`, so the caller cannot distinguish "no profile" from "API failed" or "unauthorized". +- `mini-program/src/services/auth.js` already exposes `validateToken()` and `refreshToken()`. +- `mini-program/src/services/request.js` logs only when `DEBUG` is true. Production sets `VITE_DEBUG=false`, so key diagnostics disappear online. + +## Recommended Approach + +Introduce one unified session recovery flow in the app store, used by splash and protected pages: + +1. Read `access_token` and `refresh_token`. +2. If no `access_token` exists, reset auth state and return `unauthenticated`. +3. Validate `access_token` with `/auth/validateToken`. +4. If validation succeeds, fetch the current profile and return `authenticated`. +5. If validation fails and a `refresh_token` exists, call `/auth/refreshToken`. +6. If refresh succeeds, save new tokens, fetch the current profile, and return `authenticated`. +7. If refresh fails, clear tokens, reset auth state, and return `unauthenticated`. +8. If the network/API fails in a way that is not an auth rejection, return `error` so splash can route to login with a visible toast or keep the user from entering onboarding. + +The splash page becomes the only startup router. It should route based on the recovery result: + +- `unauthenticated` -> `/pages/login/index` +- `authenticated` with profile -> `/pages/main/index` +- `authenticated` without profile -> `/pages/onboarding/index` +- `error` -> `/pages/login/index` with diagnostic toast for now + +## Logging Design + +Add a small diagnostics helper or local logging functions that print structured logs with stable tags: + +- `[ENV]` at startup: normalized env, raw env, API base URL, WS URL, debug flag, platform. +- `[AUTH] restore:start`: whether access/refresh token exists, using masked token presence only. +- `[AUTH] validate:success|fail` +- `[AUTH] refresh:start|success|fail` +- `[AUTH] profile:success|empty|fail` +- `[AUTH] route`: final route and reason. +- `[API] request`: method, path, full URL, env. +- `[API] response`: path, HTTP status, backend code, success. +- `[API] fail`: path, error message. + +Production logging must not print raw token values, phone numbers, SMS codes, Authorization headers, or full sensitive payloads. + +Because the current production `VITE_DEBUG=false` hides logs, auth and environment logs should always print during startup. General request logs can remain tied to `DEBUG`, except auth-related request failures should always print a concise error. + +## Implementation Units + +- `mini-program/src/config/env.js` + - Expose a stable `getConfig()` result for logs. + - Keep existing API/WS selection behavior. + +- `mini-program/src/services/request.js` + - Improve request/response/failure logs. + - On HTTP 401, clear tokens and use `uni.reLaunch({ url: '/pages/login/index' })`. + - Reject with enough status/code metadata for auth recovery to decide what happened. + +- `mini-program/src/services/auth.js` + - Reuse `validateToken()` and `refreshToken()`. + - Ensure refresh stores returned tokens. + +- `mini-program/src/stores/app.js` + - Add `restoreSession()` as the single startup auth API. + - Make `initialize()` call `restoreSession()` or deprecate direct token trust. + - Make `fetchUserProfile()` optionally surface errors for auth flow. + +- `mini-program/src/pages/splash/index.vue` + - Replace direct token branching with `store.restoreSession()`. + - Route only from the returned status. + +- `mini-program/src/pages/main/index.vue` + - Avoid fetching protected business data when session recovery fails. + +## Error Handling + +- Invalid/expired access token: attempt refresh once. +- Missing refresh token after validation failure: clear auth and route to login. +- Refresh 401 or backend rejection: clear auth and route to login. +- Profile API 401: clear auth and route to login. +- Network/API unreachable during startup: do not enter onboarding/main. Route to login with logs showing API target and failure. + +## Verification + +- Build `mini-program` for WeChat with production mode. +- Confirm startup console prints environment and backend URL. +- Test with no tokens: splash routes to login. +- Test with valid tokens and existing profile: routes to main. +- Test with valid tokens and no profile: routes to onboarding. +- Test with expired access token and valid refresh token: refreshes, then routes correctly. +- Test with expired access and refresh token: clears tokens and routes to login. +- Test with backend/API unreachable: does not enter onboarding; logs request failure and route reason. +