{"record":{"id":"6d6d5bc0271256c1","repo":"fullstackhero/dotnet-starter-kit","slug":"cannot-view-sessions-for-another-user","errorCode":null,"errorMessage":"Cannot view sessions for another user","messagePattern":"Cannot view sessions for another user","errorType":"exception","errorClass":"UnauthorizedAccessException","httpStatus":403,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Services/SessionService.cs","lineNumber":91,"sourceCode":"\n        if (_logger.IsEnabled(LogLevel.Information))\n        {\n            _logger.LogInformation(\"Created session {SessionId} for user {UserId}\", session.Id, userId);\n        }\n\n        return MapToDto(session, isCurrentSession: true);\n    }\n\n    public async Task<List<UserSessionDto>> GetUserSessionsAsync(\n        string userId,\n        CancellationToken cancellationToken = default)\n    {\n        EnsureValidTenant();\n\n        var currentUserId = _currentUser.GetUserId().ToString();\n        if (!string.Equals(userId, currentUserId, StringComparison.OrdinalIgnoreCase))\n        {\n            throw new UnauthorizedAccessException(\"Cannot view sessions for another user\");\n        }\n\n        var now = _timeProvider.GetUtcNow().UtcDateTime;\n        var sessions = await _db.UserSessions\n            .AsNoTracking()\n            .Where(s => s.UserId == userId && !s.IsRevoked && s.ExpiresAt > now)\n            .OrderByDescending(s => s.LastActivityAt)\n            .ToListAsync(cancellationToken);\n\n        return sessions.Select(s => MapToDto(s, isCurrentSession: false)).ToList();\n    }\n\n    public async Task<List<UserSessionDto>> GetUserSessionsForAdminAsync(\n        string userId,\n        CancellationToken cancellationToken = default)\n    {\n        EnsureValidTenant();\n","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Services/SessionService.cs#L73-L109","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait sessionService.GetUserSessionsAsync(userIdFromRoute, ct);\n// after\nvar currentUserId = currentUser.GetUserId().ToString();\nawait sessionService.GetUserSessionsAsync(currentUserId, ct);","handlingStrategy":"validation","validationCode":"var currentUserId = currentUser.GetUserId().ToString();\nif (!string.Equals(requestedUserId, currentUserId, StringComparison.OrdinalIgnoreCase))\n    return Results.Forbid();","typeGuard":null,"tryCatchPattern":"try\n{\n    return await sessionService.GetUserSessionsAsync(userId, ct);\n}\ncatch (UnauthorizedAccessException)\n{\n    return Results.Forbid();\n}","preventionTips":["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"],"tags":["authorization","sessions","identity"],"backgroundTag":"permission-denied","analyzedSha":"3f2959e683e9f83f13e55e1678c9119f63c7e8e5","analyzedAt":"2026-09-15T22:20:53.684Z","contentChangedAt":"2026-09-15T22:20:53.684Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}