fullstackhero/dotnet-starter-kit · error · UnauthorizedAccessException
Cannot view sessions for another user
Error message
Cannot view sessions for another user
What it means
GetUserSessionsAsync throws UnauthorizedAccessException when the userId parameter does not match the authenticated current user (case-insensitive comparison). Users may only list their own active sessions; admin flows must use GetUserSessionsForAdminAsync instead.
Solutions
- Pass the current user's id (from the auth token / CurrentUser.GetUserId()) instead of a client-supplied id
- For viewing other users' sessions, use the admin query (GetUserSessionsForAdminAsync) with the appropriate permission
- Check which account the JWT belongs to — the token may be for a different user than expected
Example fix
// before await sessionService.GetUserSessionsAsync(userIdFromRoute, ct); // after var currentUserId = currentUser.GetUserId().ToString(); await sessionService.GetUserSessionsAsync(currentUserId, ct);
Defensive patterns
Strategy: validation
Validate before calling
var currentUserId = currentUser.GetUserId().ToString();
if (!string.Equals(requestedUserId, currentUserId, StringComparison.OrdinalIgnoreCase))
return Results.Forbid(); Try / catch
try
{
return await sessionService.GetUserSessionsAsync(userId, ct);
}
catch (UnauthorizedAccessException)
{
return Results.Forbid();
} Prevention
- Always derive the target userId from the auth token, never from client input
- Use the dedicated admin query for viewing other users' sessions
- Ensure the frontend reads the user id from the session state of the currently authenticated account
When it happens
Trigger: Calling GetUserSessionsAsync(someOtherUserId) while authenticated as a different user — e.g. passing a userId taken from a route/body controlled by the client rather than the current principal.
Common situations: Client fetches sessions from a cached/stale userId (e.g. after re-login as a different account); frontend mistakenly uses an admin-scope endpoint with a normal user token; ID casing or Guid formatting confusion.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- Cannot revoke session for another user
- Cannot revoke sessions for another user
- Administrators cannot remove their own admin role.
- invalid tenant
- Only administrators can change user status.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/6d6d5bc0271256c1.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Identity/Modules.Identity/Services/SessionService.cs:91
if (_logger.IsEnabled(LogLevel.Information))
{
_logger.LogInformation("Created session {SessionId} for user {UserId}", session.Id, userId);
}
return MapToDto(session, isCurrentSession: true);
}
public async Task<List<UserSessionDto>> GetUserSessionsAsync(
string userId,
CancellationToken cancellationToken = default)
{
EnsureValidTenant();
var currentUserId = _currentUser.GetUserId().ToString();
if (!string.Equals(userId, currentUserId, StringComparison.OrdinalIgnoreCase))
{
throw new UnauthorizedAccessException("Cannot view sessions for another user");
}
var now = _timeProvider.GetUtcNow().UtcDateTime;
var sessions = await _db.UserSessions
.AsNoTracking()
.Where(s => s.UserId == userId && !s.IsRevoked && s.ExpiresAt > now)
.OrderByDescending(s => s.LastActivityAt)
.ToListAsync(cancellationToken);
return sessions.Select(s => MapToDto(s, isCurrentSession: false)).ToList();
}
public async Task<List<UserSessionDto>> GetUserSessionsForAdminAsync(
string userId,
CancellationToken cancellationToken = default)
{
EnsureValidTenant();
View on GitHub (pinned to 3f2959e683)