fullstackhero/dotnet-starter-kit · error · UnauthorizedAccessException

Cannot revoke session for another user

Error message

Cannot revoke session for another user

What it means

RevokeSessionAsync throws UnauthorizedAccessException when the target session's UserId does not match the currently authenticated user. Only the session owner may revoke their own session; revoking others' sessions requires an admin path.

Solutions

  1. Only revoke sessions listed for the current user (fetch the user's sessions first, then revoke from that list)
  2. Ensure the auth token belongs to the same user as the session
  3. Use the admin revocation flow with proper permissions for other users' sessions

Example fix

// before
await sessionService.RevokeSessionAsync(sessionIdFromOtherUser, ct);
// after
var mySessions = await sessionService.GetUserSessionsAsync(currentUser.GetUserId().ToString(), ct);
if (mySessions.Any(s => s.Id == sessionId)) await sessionService.RevokeSessionAsync(sessionId, ct);
Defensive patterns

Strategy: validation

Validate before calling

var session = await db.UserSessions.FindAsync(new object[] { sessionId }, ct);
var currentUserId = currentUser.GetUserId().ToString();
if (session is null || !string.Equals(session.UserId, currentUserId, StringComparison.OrdinalIgnoreCase))
    return Results.Forbid();

Try / catch

try
{
    await sessionService.RevokeSessionAsync(sessionId, ct);
}
catch (UnauthorizedAccessException)
{
    return Results.Forbid();
}

Prevention

When it happens

Trigger: Calling RevokeSessionAsync(sessionId, ...) with a sessionId that belongs to another user (or with a token for a different account than the session owner).

Common situations: Client keeps a device/session list cached across account switches; user copies a session id from an admin tool; session row's UserId differs due to cross-tenant data copy.

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


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/45b3d97db6f23e79. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Services/SessionService.cs:199

        Guid sessionId,
        string revokedBy,
        string? reason = null,
        CancellationToken cancellationToken = default)
    {
        EnsureValidTenant();

        var session = await _db.UserSessions
            .FirstOrDefaultAsync(s => s.Id == sessionId && !s.IsRevoked, cancellationToken);

        if (session is null)
        {
            return false;
        }

        var currentUserId = _currentUser.GetUserId().ToString();
        if (!string.Equals(session.UserId, currentUserId, StringComparison.OrdinalIgnoreCase))
        {
            throw new UnauthorizedAccessException("Cannot revoke session for another user");
        }

        var tenantId = _multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id;
        session.Revoke(revokedBy, reason ?? "User requested", tenantId);

        await _db.SaveChangesAsync(cancellationToken);

        if (_logger.IsEnabled(LogLevel.Information))
        {
            _logger.LogInformation("Session {SessionId} revoked by {RevokedBy}", sessionId, revokedBy);
        }

        return true;
    }

    public async Task<int> RevokeAllSessionsAsync(
        string userId,
        string revokedBy,

View on GitHub (pinned to 3f2959e683)