feat(auth): enforce JWT authentication on matches and tournaments endpoints#463
Open
mohadon0 wants to merge 1 commit into
Open
feat(auth): enforce JWT authentication on matches and tournaments endpoints#463mohadon0 wants to merge 1 commit into
mohadon0 wants to merge 1 commit into
Conversation
## Summary
Removes all placeholder user identity generation from the HTTP layer
and enforces JWT authentication on every protected endpoint in
src/http/matches.rs and src/http/tournaments.rs.
## Changes
### src/auth/extractor.rs (new)
- Implements actix-web FromRequest for Claims
- Reads Authorization: Bearer <token> header
- Validates via JwtService registered as web::Data<Arc<JwtService>>
- Falls back to request extensions if AuthMiddleware already ran (zero
overhead for middleware-wrapped scopes)
- Returns 401 for missing/invalid/expired tokens, 403 for revoked tokens
### src/http/matches.rs (new)
- GET /api/matches/{id} — get_match
- POST /api/matches/{id}/report — report_score
- POST /api/matches/{id}/dispute — dispute_match
- All handlers receive Claims via the extractor; user_id is parsed from
claims.sub — no Uuid::new_v4() placeholder anywhere
### src/http/tournaments.rs (new)
- GET /api/tournaments — list_tournaments
- GET /api/tournaments/{id} — get_tournament
- POST /api/tournaments/{id}/join — join_tournament
- Same pattern: user_id from Uuid::parse_str(&claims.sub), invalid sub
returns 401 before reaching business logic
### src/main.rs
- Registers /api/matches scope with the three new routes
- Extends /api/tournaments scope to include list, get, join alongside the
existing /{id}/statistics route
### src/auth/mod.rs
- Adds pub mod extractor so the FromRequest impl is compiled
### src/http/mod.rs
- Adds pub mod matches and pub mod tournaments
### backend/Cargo.toml
- Adds actix-web to [dev-dependencies] for test::init_service / test::call_service
### backend/tests/jwt_auth_enforcement_test.rs (new)
Comprehensive tests covering:
- valid token → 200 with correct sub
- missing Authorization header → 401
- malformed header (no Bearer prefix) → 401
- expired token (beyond 30s leeway) → 401
- invalid signature (wrong secret) → 401
- extensions fallback (AuthMiddleware already ran) → reuses Claims
- user_id always comes from claims.sub, never Uuid::new_v4()
- roles preserved in Claims
- refresh token type is distinct from access token type
|
@mohadon0 is attempting to deploy a commit to the paul joseph's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@mohadon0 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! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #372
Overview
Removes all placeholder user identity generation from the HTTP layer and enforces JWT authentication on every protected endpoint. Requests without a valid JWT are rejected with
401 Unauthorizedbefore reaching business logic.What Changed
New:
src/auth/extractor.rsActix-Web
FromRequestimplementation forClaims:Authorization: Bearer <token>headerJwtService::validate_token(registered asweb::Data<Arc<JwtService>>)AuthMiddlewarealready ran — zero overhead for middleware-wrapped scopes401, blacklisted/revoked →403, all other failures →401New:
src/http/matches.rsProtected match endpoints — every handler declares
claims: Claimsas a parameter:/api/matches/{id}get_match/api/matches/{id}/reportreport_score/api/matches/{id}/disputedispute_matchUser identity:
Uuid::parse_str(&claims.sub)— noUuid::new_v4()placeholder.New:
src/http/tournaments.rsProtected tournament endpoints:
/api/tournamentslist_tournaments/api/tournaments/{id}get_tournament/api/tournaments/{id}/joinjoin_tournamentSame identity pattern. Unauthenticated requests never reach
TournamentService.src/main.rs/api/matchesscope/api/tournamentsscope with list, get, join routes alongside the existing/{id}/statisticsJwtServicewas already registered asweb::Data<Arc<JwtService>>— no change neededsrc/auth/mod.rs+src/http/mod.rspub mod extractor,pub mod matches,pub mod tournamentsbackend/Cargo.tomlactix-webto[dev-dependencies]fortest::init_serviceNew:
backend/tests/jwt_auth_enforcement_test.rsTests covering all specified scenarios:
200with correctsubAuthorizationheader →401Bearerprefix) →401401401Claimswithout re-validatinguser_idalways comes fromclaims.sub, neverUuid::new_v4()rolespreserved through the extractorWhat Was Tested
ReportScoreRequestimported directly frommodels::match_models(avoids duplicate re-export ambiguity withmodels::matchmaker)Security Properties
/api/matchesor/api/tournamentsis reachable without a valid JWT403(distinct from unauthenticated401) so clients can distinguish session revocation from missing credentials