# Auth To Profile Flow Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Complete the PC registration, login, profile, unauthorized, logout, and endpoint-address flow with one token owner and deterministic redirects. **Architecture:** `config.js` remains the sole runtime owner of the API base address. `utils/ApiClient.js` owns token validation, persistence, and cleanup; `profile-pages.js` decides whether a profile request is allowed and redirects unauthenticated users to the homepage; `profile-common.js` delegates confirmed logout to the API client. The integration tracker records the current address and verified behavior only after verification commands complete. **Tech Stack:** Browser JavaScript (UMD and jQuery), Axios, Node.js `assert` tests, static HTML, Markdown. ## Global Constraints - The current API base address is `http://182.61.18.23:8080` for development and production, and `config.js` is its only runtime owner. - Runtime-related tests use the same address; only documents explicitly marked historical may retain the former test domain. - Successful registration clears `genealogy_auth_token` and redirects to `login.html`; it never persists a registration response token. - Only successful password or SMS login with a nonempty response token persists `genealogy_auth_token` and redirects to `profile.html`. - `profile.html` and `profile-data.html` redirect to `index.html` before a profile request when no token exists, and after HTTP or business `401` responses. - Confirmed logout requests `/genealogy/pc/auth/logout`, clears the token regardless of the request outcome, and redirects to `index.html`. - Do not add global Axios redirects, duplicate token storage in pages, alter API paths, or change password-reset, phone-change, account-deactivation, or profile pages that do not load the profile API. --- ### Task 1: Synchronize the configured API address test **Files:** - Modify: `tests/config.test.js:11-28` - Modify: `tests/utils.test.js:31` **Interfaces:** - Consumes: `configFactory(...).getConfig()` and `getApiBaseUrl()` from `config.js`. - Produces: a test that asserts the current `config.js` address for both environments. - [ ] **Step 1: Confirm the existing address-contract test fails** The current test contains these stale assertions: ```js assert.deepStrictEqual(developmentConfig.getConfig(), { environment: 'development', apiBaseUrl: 'http://test-genealogy-api.ddxcjp.cn' }); assert.strictEqual(productionConfig.getApiBaseUrl(), 'http://test-genealogy-api.ddxcjp.cn'); ``` Run: `node tests/config.test.js` Expected: failure because `config.js` already returns `http://182.61.18.23:8080`. - [ ] **Step 2: Update the stale test expectations** Replace the two expected URL values with: ```js assert.deepStrictEqual(developmentConfig.getConfig(), { environment: 'development', apiBaseUrl: 'http://182.61.18.23:8080' }); assert.strictEqual(productionConfig.getApiBaseUrl(), 'http://182.61.18.23:8080'); ``` Replace the mocked requester address in `tests/utils.test.js` with: ```js baseUrl: 'http://182.61.18.23:8080', ``` - [ ] **Step 3: Run the focused configuration test** Run: `node tests/config.test.js` Expected: `config tests passed`. Run: `node tests/utils.test.js` Expected: `utils tests passed`. - [ ] **Step 4: Commit the address-test synchronization** ```powershell git add -- tests/config.test.js tests/utils.test.js git commit -m "test: sync configured api address" ``` ### Task 2: Enforce login-token and logout-cleanup contracts **Files:** - Modify: `utils/ApiClient.js:52-60, 167-191, 270-274` - Modify: `tests/api-client.test.js:83-177` **Interfaces:** - Consumes: `getTokenFromResponse(data)`, `setToken(token)`, `clearToken()`, and public `client.login`, `client.loginBySms`, and `client.logout` methods. - Produces: `client.login(body)` and `client.loginBySms(body)` reject with `ApiError('登录响应缺少 token')` when their response has no token; `client.logout()` clears the configured token key before resolving or rejecting. - [ ] **Step 1: Write failing API-client assertions** Append this after the registration assertions and before `client.sendSmsCode(...)` in `tests/api-client.test.js`: ```js const missingTokenStore = createStore(); const missingTokenClient = GenealogyApi.createClient({ baseUrl: 'http://182.61.18.23:8080', clientId: 'client-x', tenantId: 'tenant-x', tokenKey: 'token-key', tokenStore: missingTokenStore, axiosInstance: { request: async () => ({ status: 200, data: { code: 200, data: {} } }) } }); await assert.rejects( missingTokenClient.login({ phone: '13800000000', password: 'md5' }), (error) => error.message === '登录响应缺少 token' ); assert.strictEqual(missingTokenStore.getItem('token-key'), null); store.setItem('token-key', 'logout-token'); await client.logout(); assert.strictEqual(calls[9].url, '/genealogy/pc/auth/logout'); assert.strictEqual(store.getItem('token-key'), null); const failedLogoutStore = createStore({ 'token-key': 'logout-token' }); const failedLogoutClient = GenealogyApi.createClient({ baseUrl: 'http://182.61.18.23:8080', clientId: 'client-x', tenantId: 'tenant-x', tokenKey: 'token-key', tokenStore: failedLogoutStore, axiosInstance: { request: async () => { throw new Error('logout failed'); } } }); await assert.rejects(failedLogoutClient.logout(), /logout failed/); assert.strictEqual(failedLogoutStore.getItem('token-key'), null); ``` Move the existing SMS-code assertions from `calls[9]` to `calls[10]` and change the final call-count assertion from `10` to `11`. Replace the two existing `baseUrl: 'https://test-genealogy-api.ddxcjp.cn'` fixtures in this file with: ```js baseUrl: 'http://182.61.18.23:8080', ``` - [ ] **Step 2: Run the focused API-client test and confirm failure** Run: `node tests/api-client.test.js` Expected: failure at the missing-token rejection because the current login method resolves without writing a token, before reaching the logout assertions. - [ ] **Step 3: Add the minimal token validation and logout cleanup** Add this helper after `getTokenFromResponse` in `utils/ApiClient.js`: ```js function requireLoginToken(data) { var token = getTokenFromResponse(data); if (!token) throw new ApiError('登录响应缺少 token'); return token; } ``` Replace the token writes in `login()` and `loginBySms()` with: ```js setToken(requireLoginToken(data)); ``` Replace `logout` with: ```js logout: async function () { try { return await request('DELETE', '/genealogy/pc/auth/logout'); } finally { clearToken(); } }, ``` - [ ] **Step 4: Run the API-client test and confirm all token paths pass** Run: `node tests/api-client.test.js` Expected: `api-client tests passed`. - [ ] **Step 5: Commit the API-client contract** ```powershell git add -- utils/ApiClient.js tests/api-client.test.js git commit -m "fix: complete auth token lifecycle" ``` ### Task 3: Guard profile access and connect confirmed logout **Files:** - Modify: `public/js/profile-pages.js:18-22, 165-198` - Modify: `public/js/profile-common.js:9-82` - Modify: `profile.html:80, 355` - Modify: `tests/profile-pages.test.js:1-65` - Create: `tests/profile-logout-structure.test.js` **Interfaces:** - Consumes: public `api.getToken()`, `api.clearToken()`, `api.currentProfile()`, `api.updateProfile()`, and `api.logout()` methods. - Produces: `ProfilePages.shouldRedirectToHome(api, error)` for token and unauthorized classification; profile reads and writes redirect to `index.html` when that helper returns true; the existing logout controls call `api.logout()` before navigating to `index.html`. - [ ] **Step 1: Write failing profile and logout tests** Add these assertions before the final `console.log` in `tests/profile-pages.test.js`: ```js assert.strictEqual(ProfilePages.shouldRedirectToHome({ getToken: () => '' }), true); assert.strictEqual(ProfilePages.shouldRedirectToHome({ getToken: () => 'token-x' }), false); assert.strictEqual(ProfilePages.shouldRedirectToHome({ getToken: () => 'token-x' }, { status: 401 }), true); assert.strictEqual(ProfilePages.shouldRedirectToHome({ getToken: () => 'token-x' }, { code: 401 }), true); assert.strictEqual(ProfilePages.shouldRedirectToHome({ getToken: () => 'token-x' }, { status: 500 }), false); ``` Create `tests/profile-logout-structure.test.js` with: ```js const assert = require('assert'); const fs = require('fs'); const path = require('path'); const profileHtml = fs.readFileSync(path.join(__dirname, '..', 'profile.html'), 'utf8'); const profileCommon = fs.readFileSync(path.join(__dirname, '..', 'public', 'js', 'profile-common.js'), 'utf8'); assert.match(profileHtml, /data-logout-confirm/); assert.match(profileHtml, //); assert.match(profileCommon, /function performLogout\(targetUrl\)/); assert.match(profileCommon, /api\.logout\(\)/); assert.match(profileCommon, /targetUrl \|\| 'index\.html'/); console.log('profile logout structure tests passed'); ``` - [ ] **Step 2: Run the new focused tests and confirm failure** Run: `node tests/profile-pages.test.js` Expected: failure because `shouldRedirectToHome` is not exported. Run: `node tests/profile-logout-structure.test.js` Expected: failure because the logout confirmation markup and `performLogout` function do not exist. - [ ] **Step 3: Add profile token guard and unauthorized redirect** Add these helpers after `getApi()` in `public/js/profile-pages.js`: ```js function shouldRedirectToHome(api, error) { var status = error && (error.status || error.code); return !api || !api.getToken || !api.getToken() || Number(status) === 401; } function redirectToHome() { if (!root.location) return; if (typeof root.location.replace === 'function') { root.location.replace('index.html'); return; } root.location.href = 'index.html'; } function redirectUnauthorized(api, error) { if (!shouldRedirectToHome(api, error)) return false; if (api && api.clearToken) api.clearToken(); redirectToHome(); return true; } ``` Replace the existing combined early-return condition in `loadProfile()` with these two branches: ```js if (!api || !documentRef || !documentRef.querySelector('[data-profile-page]')) return; if (redirectUnauthorized(api)) return; ``` Update its `catch` block to start with: ```js if (redirectUnauthorized(api, error)) return; ``` Update `submitProfileForm()` to place `if (redirectUnauthorized(api)) return;` after its existing null guard, and to start its `catch` block with `if (redirectUnauthorized(api, error)) return;` before setting an error status or message. Export the classifier with the existing public methods: ```js shouldRedirectToHome: shouldRedirectToHome, ``` - [ ] **Step 4: Connect both logout confirmations to the API client** Add this function before `bindLogout()` in `public/js/profile-common.js`: ```js function performLogout(targetUrl) { var api = window.GenealogyApi && window.GenealogyApi.defaultClient; var redirect = function () { window.location.href = targetUrl || 'index.html'; }; if (!api || !api.logout) { redirect(); return; } api.logout().catch(function () {}).then(redirect); } ``` In `bindLogout()`, change the default target URL to `index.html`, replace the Layui confirmation callback body with `performLogout(targetUrl);`, and add this event handler before the existing `[data-logout-close]` handler: ```js $(document).on('click', '[data-logout-confirm]', function (event) { event.preventDefault(); closeLegacyLogout(); performLogout($(this).attr('href') || 'index.html'); }); ``` In `profile.html`, change the legacy confirmation anchor to: ```html 确认退出 ``` - [ ] **Step 5: Run the focused profile tests and syntax checks** Run: `node tests/profile-pages.test.js` Expected: `profile-pages tests passed`. Run: `node tests/profile-logout-structure.test.js` Expected: `profile logout structure tests passed`. Run: `node --check public/js/profile-pages.js` Expected: exit code `0`. Run: `node --check public/js/profile-common.js` Expected: exit code `0`. - [ ] **Step 6: Commit the profile and logout flow** ```powershell git add -- public/js/profile-pages.js public/js/profile-common.js profile.html tests/profile-pages.test.js tests/profile-logout-structure.test.js git commit -m "fix: guard profile auth and logout" ``` ### Task 4: Record the current flow and run end-to-end regression checks **Files:** - Modify: `docs/pc-api-page-integration-tracker-2026-07-09.md:1-9, 32, 70, 163` - Modify: `docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md:3-5` **Interfaces:** - Consumes: the verified results of Tasks 1 through 3. - Produces: a tracker whose current address and authentication-flow status match the code and tests, and a spec marked approved for implementation. - [ ] **Step 1: Run all focused flow tests** Run: ```powershell node tests/config.test.js node tests/api-client.test.js node tests/auth-pages.test.js node tests/profile-pages.test.js node tests/profile-logout-structure.test.js ``` Expected: each command prints its success message and exits with code `0`. - [ ] **Step 2: Run syntax and complete regression checks** Run: ```powershell node --check utils/ApiClient.js node --check public/js/profile-pages.js node --check public/js/profile-common.js Get-ChildItem -Path tests -Filter *.test.js | ForEach-Object { & node $_.FullName; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } } ``` Expected: all syntax checks exit `0`; every test script prints its success message; PowerShell exits `0` on the first test failure instead of masking it. - [ ] **Step 3: Update the tracker from verified evidence** At the top of `docs/pc-api-page-integration-tracker-2026-07-09.md`, replace the active base-address line with: ```markdown 接口基础地址:`http://182.61.18.23:8080` ``` Add this section immediately after the introductory historical-record lines: ```markdown ## Current Endpoint Contract - Runtime owner: `config.js`. - Current development and production address: `http://182.61.18.23:8080`. - Address change record: on 2026-07-11 the active address changed from the historical test domain to this IP address and port. Historical references to `test-genealogy-api.ddxcjp.cn` do not describe the current runtime configuration. ``` Replace the YAML-analysis item that says the project currently uses the test domain with: ```markdown 2. `servers` 仍是本地地址示例;当前运行时基础地址以 `config.js` 为准,为 `http://182.61.18.23:8080`。 ``` Replace the PC-010 row with: ```markdown | PC-010 | 已复核(当前地址已同步) | `config.js`、`tests/config.test.js`、`utils/ApiClient.js` | API 基础地址 | 当前开发和生产地址均为 `http://182.61.18.23:8080`;地址唯一运行时来源为 `config.js`,`tests/config.test.js` 已同步断言。 | ``` After Steps 1 and 2 pass, run `Get-Date -Format "yyyy-MM-dd HH:mm:ss K"` and insert its output as the `完成时间` line immediately after the `状态` line in this record. Append the rest of this record after the existing PC entries: ```markdown ## PC-025 注册、登录到个人资料闭环 - 状态:已完成 - 规划文件:`docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md` - 页面范围:`register.html`、`login.html`、`profile.html`、`profile-data.html` - 接口范围:`/genealogy/pc/auth/register`、`/genealogy/pc/auth/login`、`/genealogy/pc/auth/login/sms`、`/genealogy/pc/auth/profile`、`/genealogy/pc/auth/logout` - 地址契约:运行时唯一来源为 `config.js`;当前开发和生产地址均为 `http://182.61.18.23:8080`。 - 流程结果:注册成功清除 token 并跳转 `login.html`;密码和短信登录必须取得 token 后跳转 `profile.html`;个人资料无 token 或收到 HTTP/业务 `401` 时清 token 并跳转 `index.html`;确认退出无论接口结果都清 token 并跳转 `index.html`。 - 修改文件:`tests/config.test.js`、`utils/ApiClient.js`、`tests/api-client.test.js`、`public/js/profile-pages.js`、`public/js/profile-common.js`、`profile.html`、`tests/profile-pages.test.js`、`tests/profile-logout-structure.test.js`。 - 验证结果:`node tests/config.test.js`、`node tests/api-client.test.js`、`node tests/auth-pages.test.js`、`node tests/profile-pages.test.js`、`node tests/profile-logout-structure.test.js`、三个 `node --check` 命令和全量 Node 测试均通过。 ``` Change the spec status body to: ```markdown Approved for implementation. ``` - [ ] **Step 4: Check final whitespace and commit the record** Run: `git diff --check` Expected: exit code `0`; pre-existing CRLF conversion warnings are not whitespace errors. ```powershell git add -- docs/pc-api-page-integration-tracker-2026-07-09.md docs/superpowers/specs/2026-07-11-auth-to-profile-flow-design.md git commit -m "docs: record auth profile flow" ```