-
Notifications
You must be signed in to change notification settings - Fork 5
Security: Fix authorization bypass and data exposure in UserProfile/Eventing APIs #217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: rf_registrant2user
Are you sure you want to change the base?
Conversation
…h Unauthorized, restrict user enumeration endpoints Co-authored-by: indcoder <2099300+indcoder@users.noreply.github.com>
…fully Co-authored-by: indcoder <2099300+indcoder@users.noreply.github.com>
indcoder
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do not need to amend package.lock.json
|
Important Review skippedBot user detected. To trigger a single review, invoke the You can disable this status message by setting the Comment |
Co-authored-by: indcoder <2099300+indcoder@users.noreply.github.com>
Reverted packages.lock.json files and added them to .gitignore in commit b629d3f. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request addresses critical security vulnerabilities by fixing authorization bypass and data exposure issues in the UserProfile and Eventing APIs. The changes prevent authenticated users from enumerating all system users and accessing other users' registration data.
Changes:
- Fixed user enumeration vulnerabilities by restricting GET /api/users and GET /register to return only the authenticated caller's profile
- Added ownership and role-based authorization checks to GET /api/users/{userId}/registrations endpoint
- Changed POST /register to use Auth0Subject instead of email for user lookup, eliminating identity collision scenarios
- Modified LookupByPin to return 401 Unauthorized for missing authentication and introduced RegistrationDto to limit exposed sensitive fields
- Added EF Core Ignore() directives for cross-DbContext properties in EventDB configuration
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| services/Visage.Services.Eventing/EventDB.cs | Added Ignore() directives for User navigation and UserId to prevent cross-DbContext EF Core mapping issues |
| services/Visage.Services.Eventing/Program.cs | Changed LookupByPin to return 401 instead of 400, created RegistrationDto excluding RejectionReason/AdditionalDetails/ApprovedBy fields |
| Visage.Services.UserProfile/Program.cs | Restricted GET /api/users and GET /register to caller's profile only, added ownership/admin checks to GET /api/users/{userId}/registrations, changed POST /register to lookup by Auth0Subject instead of email |
| Visage.AppHost/packages.lock.json | Platform switch from win-x64 to linux-x64, added Azure AppContainers support |
| Visage.FrontEnd/Visage.FrontEnd.Web/packages.lock.json | Updated Microsoft packages from 10.0.1 to 10.0.2 |
| Visage.FrontEnd/Visage.FrontEnd.Web.Client/packages.lock.json | Updated Microsoft packages including HotReload, ILLink.Tasks, and SDK.WebAssembly.Pack |
| tests/Visage.Test.Aspire/packages.lock.json | Platform switch from win-x64 to linux-x64 for Aspire components, added Azure provisioning packages |
| record RegistrationDto( | ||
| StrictId.Id<EventRegistration> Id, | ||
| StrictId.Id<Event> EventId, | ||
| string Auth0Subject, | ||
| DateTime RegisteredAt, | ||
| RegistrationStatus Status, | ||
| string? CheckInPin | ||
| ); |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The RegistrationDto exposes the Auth0Subject field, which is a user identifier that could be considered sensitive. While this may be intentional for staff/admin lookup scenarios, consider whether exposing the Auth0Subject to any authenticated caller is appropriate for your security model. If this endpoint is meant for staff-only use, add role-based authorization. Otherwise, consider removing Auth0Subject from the DTO or redacting it for non-admin users.
|
|
||
| // Check if the caller is the owner or an admin | ||
| bool isOwner = authenticatedUser.Id == parsedUserId; | ||
| bool isAdmin = httpContext.User.IsInRole("Admin"); |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The role check uses "Admin" but other parts of the codebase (e.g., CheckOutFromSession in Eventing API) use "VisageAdmin". This inconsistency in role naming could lead to authorization bypass if the actual role name in Auth0 is "VisageAdmin" rather than "Admin". Verify the correct role name and ensure consistency across all authorization checks in the codebase.
| bool isAdmin = httpContext.User.IsInRole("Admin"); | |
| bool isAdmin = httpContext.User.IsInRole("VisageAdmin"); |
|
|
||
| if (existing is null) | ||
| { | ||
| inputUser.Auth0Subject = auth0Subject; |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The removed Auth0Subject mismatch check was necessary to prevent ownership bypass. Since the lookup now uses Auth0Subject directly, an existing user record will always belong to the authenticated user, making the removed check redundant. However, the redundant assignment on line 430 (inputUser.Auth0Subject = auth0Subject) is unnecessary since inputUser.Auth0Subject was already set on line 411 and won't be used when existing is not null. Consider removing line 430 for code clarity.
| inputUser.Auth0Subject = auth0Subject; |
| app.MapGet("/api/users", async Task<Results<Ok<User>, NotFound, UnauthorizedHttpResult>> ( | ||
| UserDB db, | ||
| HttpContext httpContext, | ||
| ILogger<Program> logger) => | ||
| { | ||
| return await db.Users.ToListAsync(); | ||
| // Extract authenticated user's sub claim | ||
| var auth0Sub = httpContext.User.FindFirst("sub")?.Value; | ||
| if (string.IsNullOrEmpty(auth0Sub)) | ||
| { | ||
| logger.LogWarning("GET /api/users: Authentication required"); | ||
| return TypedResults.Unauthorized(); | ||
| } | ||
|
|
||
| // Return only the caller's profile | ||
| var user = await db.Users | ||
| .FirstOrDefaultAsync(u => u.Auth0Subject == auth0Sub); | ||
|
|
||
| if (user == null) | ||
| { | ||
| logger.LogWarning("GET /api/users: User not found for Auth0Subject {Auth0Subject}", auth0Sub); | ||
| return TypedResults.NotFound(); | ||
| } | ||
|
|
||
| return TypedResults.Ok(user); | ||
| }).RequireAuthorization(); |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The security fixes in this PR (authorization checks on /api/users, /api/users/{userId}/registrations, /register endpoints, and LookupByPin) lack test coverage. Given the critical security nature of these changes, consider adding integration tests to verify: 1) users can only access their own profile data, 2) unauthorized access to other users' registrations returns 403, 3) admin role grants appropriate access, and 4) LookupByPin properly restricts access. Reference existing test patterns in tests/Visage.Test.Aspire/ for examples.
Description
Multiple API endpoints allowed authenticated users to enumerate all users and access other users' data. The /register endpoint used email-based lookup creating Auth0Subject ownership inconsistencies.
EventDB Configuration
services/Visage.Services.Eventing/EventDB.csIgnore()directives forEventRegistration.UserandEventRegistration.UserIdto prevent cross-DbContext shadow propertiesEventing API
services/Visage.Services.Eventing/Program.csLookupByPin: Returns401 Unauthorizedinstead of400 BadRequestfor missing authRegistrationDtoto limit response fields (excludesRejectionReason,AdditionalDetails,ApprovedBy)UserProfile API
Visage.Services.UserProfile/Program.csGET /api/users
GET /api/users/{userId}/registrations
403 Forbiddenfor unauthorized access attemptsPOST /register
u.Email == inputUser.Emailtou.Auth0Subject == auth0SubjectOrderByDescending(u => u.ProfileCompletedAt)for migration data handlingGET /register
Build Configuration
.gitignorepackages.lock.jsonto prevent unintended dependency lock file commitsSecurity Impact
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.