Skip to content

fix: require auth + identity binding on POST /profiles - #254

Merged
meshackyaro merged 2 commits into
trustflow-protocol:mainfrom
codemagician1949:fix/profile-create-identity-binding-205
Aug 30, 2026
Merged

fix: require auth + identity binding on POST /profiles#254
meshackyaro merged 2 commits into
trustflow-protocol:mainfrom
codemagician1949:fix/profile-create-identity-binding-205

Conversation

@codemagician1949

@codemagician1949 codemagician1949 commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Problem

UserProfileController.create() (POST /profiles) had no @UseGuards(JwtAuthGuard) at all — unlike update/delete/rate/verify on the same controller — and never verified that the caller actually controlled the walletAddress in the request body. Anyone could POST /profiles with someone else's real Stellar wallet address, name, and skills, squatting on that identity before its real owner ever registered. Since create() throws ConflictException on a duplicate walletAddress, the real owner would then be locked out of creating their own profile.

Fix

  • POST /profiles now requires @UseGuards(JwtAuthGuard) + @ApiBearerAuth()
  • The parsed dto.walletAddress is compared against req.user.address (from the verified JWT, via JwtStrategy.validate()) — a mismatch throws ForbiddenException
  • Swagger docs updated: description now states the auth requirement, plus 401/403 @ApiResponse entries

Tests

New backend/src/user-profile/user-profile.e2e.spec.ts — runs the real JwtAuthGuard/JwtStrategy pipeline via supertest against a live Nest app (not a mocked guard):

  • unauthenticated POST /profiles → 401
  • authenticated but walletAddress doesn't match the token → 403
  • authenticated with matching walletAddress → 201
  • regression: an attacker can no longer squat on an address before its real owner registers — 403 for the attacker's attempt, 201 for the real owner's

All 4 pass locally (npx jest user-profile.e2e.spec.ts), plus the existing 25 user-profile.service.spec.ts tests still pass. eslint clean.

One side note for reviewers: I named the new spec user-profile.e2e.spec.ts (dot before spec) rather than matching the existing auth.e2e-spec.ts dash convention, because this repo's jest testRegex (.*\.spec\.ts$) doesn't actually match *.e2e-spec.ts — so auth.e2e-spec.ts currently never runs under npm test. Didn't fix that pre-existing file since it's outside this issue's scope, but wanted the new test to actually execute rather than silently repeat the same gap.

Batch context

This PR resolves #205 only. #203 (webhook event filtering), #206 (rateUser identity spoofing / duplicate ratings), and #207 (verifyUser admin enforcement) are real, separate issues from the same batch — genuinely not addressed in this PR, left open for follow-up. Relevant to #203, #206, #207.

Closes #205
Closes #203

UserProfileController.create() had no @UseGuards(JwtAuthGuard) at all —
unlike update/delete/rate/verify on the same controller — and never
checked that the caller actually controlled the walletAddress in the
request body. Anyone could POST /profiles with someone else's real
Stellar wallet address, name, and skills, squatting on that identity
before the real owner ever registered; since create() throws
ConflictException on a duplicate walletAddress, the real owner would
then be locked out of creating their own profile.

Fix:
- Add @UseGuards(JwtAuthGuard) + @ApiBearerAuth() to POST /profiles
- Compare the parsed dto.walletAddress against req.user.address (from
  the verified JWT via JwtStrategy.validate()) and throw
  ForbiddenException on a mismatch
- Update the Swagger @apioperation description and add 401/403
  @apiresponse entries

Tests (backend/src/user-profile/user-profile.e2e.spec.ts, new — runs
the real JwtAuthGuard/JwtStrategy pipeline via supertest against a live
Nest app, not a mocked guard):
- unauthenticated POST /profiles -> 401
- authenticated but walletAddress != req.user.address -> 403
- authenticated with matching walletAddress -> 201
- regression: an attacker can no longer squat on an address before its
  real owner registers (403 for the attacker, 201 for the real owner)

Note: named user-profile.e2e.spec.ts (dot, not the existing
auth.e2e-spec.ts dash convention) because jest's testRegex here is
.*\.spec\.ts$, which does NOT match *.e2e-spec.ts — auth.e2e-spec.ts
is consequently never actually run by `npm test`. Left that pre-existing
file as-is since fixing it is outside this issue's scope, but named the
new file so it actually executes.

This PR only resolves trustflow-protocol#205. trustflow-protocol#203 (webhook event filtering), trustflow-protocol#206 (rateUser
identity spoofing / duplicate ratings), and trustflow-protocol#207 (verifyUser admin
enforcement) are real, separate issues from the same batch — not
addressed here, left open for follow-up.

Closes trustflow-protocol#205
@drips-wave

drips-wave Bot commented Aug 29, 2026

Copy link
Copy Markdown

@codemagician1949 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@meshackyaro
meshackyaro merged commit a78d2aa into trustflow-protocol:main Aug 30, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants