# Backend fixes required for Wallet Login + Favorites production readiness Date: 2026-06-04 Environment tested: `https://arkie-library-stag.com/apnew/api` ## Summary Frontend has been updated to the new backend contract: - Wallet login: `POST /api/auth/wallet/login` with `{ address }` - Wallet session check: `GET /api/auth/wallet/me` with `Authorization: Bearer ` - Favorites list/status: `GET /api/favorites` and `GET /api/favorites?ids=...` - Favorite mutation: `POST /api/posts/{id}/favorite` with `{ add: true|false }` Staging confirms the new login endpoint works, but favorite mutation currently accepts an invalid Bearer token. This must be fixed before production trust. --- ## Priority 0 — Fix favorite mutation authentication ### Current staging behavior The following request currently returns `200 OK` even with an invalid token: ```bash curl -i -X POST \ "https://arkie-library-stag.com/apnew/api/posts/8f4a571c-3477-4b05-91be-d85907048de5/favorite" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer invalid-token" \ --data '{"add":true}' ``` Observed response: ```http HTTP 200 {"ok":true} ``` ### Required behavior `POST /api/posts/{id}/favorite` must require a valid wallet JWT. Invalid, missing, expired, malformed, or unverifiable tokens must return: ```http HTTP 401 Unauthorized ``` Recommended response body: ```json { "error": "unauthorized" } ``` ### Acceptance tests #### Missing token ```bash curl -i -X POST \ "https://arkie-library-stag.com/apnew/api/posts/{postId}/favorite" \ -H "Content-Type: application/json" \ --data '{"add":true}' ``` Expected: ```http HTTP 401 ``` #### Invalid token ```bash curl -i -X POST \ "https://arkie-library-stag.com/apnew/api/posts/{postId}/favorite" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer invalid-token" \ --data '{"add":true}' ``` Expected: ```http HTTP 401 ``` #### Valid token ```bash TOKEN=$(curl -s -X POST \ "https://arkie-library-stag.com/apnew/api/auth/wallet/login" \ -H "Content-Type: application/json" \ --data '{"address":"0x0000000000000000000000000000000000000001"}' \ | jq -r .token) curl -i -X POST \ "https://arkie-library-stag.com/apnew/api/posts/{postId}/favorite" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ --data '{"add":true}' ``` Expected: ```http HTTP 200 ``` Response should include at least: ```json { "ok": true, "favorited": true } ``` Then cancel: ```bash curl -i -X POST \ "https://arkie-library-stag.com/apnew/api/posts/{postId}/favorite" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ --data '{"add":false}' ``` Expected: ```json { "ok": true, "favorited": false } ``` --- ## Priority 1 — Confirm wallet login security model ### Current contract ```http POST /api/auth/wallet/login Content-Type: application/json { "address": "0x..." } ``` Response: ```json { "token": "", "wallet": "0x..." } ``` This is what the frontend now uses. ### Production risk This flow does not prove wallet ownership. Any client can submit any wallet address and receive a token for that address. If wallet identity is only used for low-risk favorites, this may be acceptable as an MVP. If wallet identity will be used for user identity, permissions, membership, rewards, asset ownership, admin behavior, or anything security-sensitive, backend should require signature verification. ### Recommended secure production flow If stronger security is required, backend should use nonce + signature: 1. `POST /api/auth/wallet/nonce` with `{ address }` 2. Backend returns a one-time message / nonce. 3. Frontend asks wallet to sign the message. 4. `POST /api/auth/wallet/verify` with `{ address, message, signature }` 5. Backend verifies recovered address equals requested address. 6. Backend issues JWT. If backend decides to keep the simplified `{ address }` login, please explicitly confirm that this is an accepted production risk. --- ## Priority 2 — Normalize favorites response contract Frontend currently supports the staging response shape, but the response should be made explicit. ### Favorites list ```http GET /api/favorites?lang=&limit=&page=&sort=&category=&q= Authorization: Bearer ``` Current staging response observed: ```json { "items": [ { "id": "...", "postType": "image", "categoryId": 11, "categorySlug": "official-assets", "language": "zh", "title": "..." } ] } ``` Recommended production response: ```json { "items": [], "page": 1, "limit": 24, "total": 0 } ``` `page`, `limit`, and `total` are needed for correct pagination. ### Favorite status by ids ```http GET /api/favorites?ids=id1,id2,id3 Authorization: Bearer ``` Current staging response observed: ```json { "items": [] } ``` This works, but for frontend performance and clarity, recommended response is: ```json { "ids": ["id1", "id3"] } ``` Meaning: only IDs that are already favorited by the current wallet user. Frontend currently accepts both: - `{ ids: string[] }` - `{ items: Resource[] }` But backend should document and standardize one shape. --- ## Priority 3 — Required status codes Please standardize these responses: | Case | Expected status | | --- | --- | | Missing Bearer token on protected endpoint | `401` | | Invalid/expired Bearer token | `401` | | Valid token but post ID does not exist | `404` | | Invalid JSON body | `400` | | Invalid `add` value | `400` | | Successful favorite add/remove | `200` | Protected endpoints: - `GET /api/auth/wallet/me` - `GET /api/favorites` - `GET /api/favorites?ids=...` - `POST /api/posts/{id}/favorite` --- ## Frontend compatibility notes The frontend currently calls these staging paths through the same-origin prefix: ```txt /apnew/api/auth/wallet/login /apnew/api/auth/wallet/me /apnew/api/favorites /apnew/api/favorites?ids=... /apnew/api/posts/{id}/favorite ``` In frontend source this is written as `/api/...`; staging build uses `VITE_API_PREFIX=/apnew`. Please keep backend routes under `/api/...` behind the proxy. --- ## Final production checklist Backend should confirm all of the following before production release: - [ ] `POST /api/posts/{id}/favorite` rejects missing token with `401`. - [ ] `POST /api/posts/{id}/favorite` rejects invalid token with `401`. - [ ] `POST /api/posts/{id}/favorite` only changes favorites for the wallet from the validated JWT. - [ ] `GET /api/favorites` requires a valid Bearer token. - [ ] `GET /api/favorites?ids=...` requires a valid Bearer token, unless explicitly declared public/legacy. - [ ] `GET /api/auth/wallet/me` validates token and returns the wallet address from the token. - [ ] Backend explicitly confirms whether simplified `{ address }` login is acceptable for production, or switches to nonce/signature verification.