Files
Arkie-Library-Frontend/docs/backend-wallet-favorites-production-fixes.md
2026-06-04 17:06:29 +08:00

6.8 KiB

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 <token>
  • 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:

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 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 401 Unauthorized

Recommended response body:

{
  "error": "unauthorized"
}

Acceptance tests

Missing token

curl -i -X POST \
  "https://arkie-library-stag.com/apnew/api/posts/{postId}/favorite" \
  -H "Content-Type: application/json" \
  --data '{"add":true}'

Expected:

HTTP 401

Invalid token

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 401

Valid token

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 200

Response should include at least:

{
  "ok": true,
  "favorited": true
}

Then cancel:

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:

{
  "ok": true,
  "favorited": false
}

Priority 1 — Confirm wallet login security model

Current contract

POST /api/auth/wallet/login
Content-Type: application/json

{
  "address": "0x..."
}

Response:

{
  "token": "<jwt>",
  "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.

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

GET /api/favorites?lang=&limit=&page=&sort=&category=&q=
Authorization: Bearer <token>

Current staging response observed:

{
  "items": [
    {
      "id": "...",
      "postType": "image",
      "categoryId": 11,
      "categorySlug": "official-assets",
      "language": "zh",
      "title": "..."
    }
  ]
}

Recommended production response:

{
  "items": [],
  "page": 1,
  "limit": 24,
  "total": 0
}

page, limit, and total are needed for correct pagination.

Favorite status by ids

GET /api/favorites?ids=id1,id2,id3
Authorization: Bearer <token>

Current staging response observed:

{
  "items": []
}

This works, but for frontend performance and clarity, recommended response is:

{
  "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:

/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.