Skip to content

[#79] Rating API with signature verification + token gate#84

Merged
realproject7 merged 2 commits intomainfrom
task/79-rating-api
Mar 15, 2026
Merged

[#79] Rating API with signature verification + token gate#84
realproject7 merged 2 commits intomainfrom
task/79-rating-api

Conversation

@realproject7
Copy link
Copy Markdown
Owner

Summary

  • POST /api/ratings: Recovers wallet address via recoverMessageAddress() (viem), looks up storyline token, checks balanceOf() on-chain (rejects with 403 if 0 balance), upserts rating via service role client
  • GET /api/ratings?storylineId=N: Returns ratings array + computed average + count via anon client (RLS public read)
  • Upserts on (storyline_id, rater_address) — one rating per user per storyline

Test plan

  • npm run lint passes
  • npm run typecheck passes
  • POST with valid signature + token balance → 200 success
  • POST with valid signature + 0 balance → 403 rejection
  • POST with invalid signature → 400 error
  • GET returns ratings array with correct average
  • Re-rating (same user) updates existing row

Fixes #79

🤖 Generated with Claude Code

- POST /api/ratings: recovers address via recoverMessageAddress(),
  checks balanceOf() on storyline token, upserts via service role
- GET /api/ratings?storylineId=N: returns ratings array + average
- Rejects if rater holds 0 tokens (403)

Fixes #79

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: REQUEST CHANGES

Summary

The API shape matches most of issue #79, but the POST endpoint currently treats any valid wallet signature as authorization for any submitted rating payload. That leaves the token gate tied only to wallet ownership, not to the specific rating request being written.

Findings

  • [high] POST /api/ratings recovers an address from the supplied message and signature, then trusts the separately submitted storylineId, rating, and comment fields without verifying that those values were part of the signed message. An attacker can replay any previously captured signature from a token holder over an unrelated message and submit arbitrary ratings/comments for that wallet as long as the holder still owns tokens. This is a real auth bypass, not just a missing nonce. The server needs to verify a canonical message that includes the rating payload being authorized.
    • File: src/app/api/ratings/route.ts:67
    • Suggestion: define a canonical signed message format that embeds at least storylineId, rating, and optionally comment (plus a nonce/timestamp), then either validate that exact message string server-side before calling recoverMessageAddress, or switch to verifyMessage() against the expected signer and expected message.

Decision

Requesting changes because the current signature flow does not bind the signature to the rating being persisted, so a valid signature can authorize unintended writes.

- Signed message must match "Rate storyline {id} with rating {n}" to
  prevent replay attacks across storylines/ratings
- Added Number.isInteger() check for rating value

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@project7-interns project7-interns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE

Summary

The follow-up binds the signed message to the submitted rating action and adds the missing integer validation. The API now matches the security intent of issue #79.

Findings

  • No blocking findings.

Decision

Approving because the POST flow now reconstructs the expected message server-side, enforces exact equality before signature recovery, and preserves the token-gate/write-path requirements.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P5-R2] Rating API with signature verification + token gate

2 participants