fullstackhero/dotnet-starter-kit · error · UnauthorizedException

Unauthorized

Error message

Unauthorized

What it means

EndImpersonationCommandHandler.Handle first checks _currentUser.IsAuthenticated() and throws UnauthorizedException when the caller has no valid principal. Ending impersonation requires an authenticated user because the handler must read the actor claims (act_sub, act_tenant) from the current token to mint the original actor's new token.

Solutions

  1. Re-authenticate to obtain a fresh token (if possible via the original login flow) before calling EndImpersonation.
  2. Ensure the Authorization: Bearer header is actually sent on the EndImpersonation request.
  3. If tokens expire quickly, end impersonation proactively before expiry or handle 401 by falling back to a fresh login.
  4. Check JWT validation settings (issuer/audience/signing key) if valid tokens are being rejected.

Example fix

// before
await api.PostAsync("/impersonation/end"); // 401: token expired

// after
if (auth.isTokenExpired()) {
    await auth.loginAgain(); // cannot silently end impersonation without a valid session
    return;
}
await api.post("/impersonation/end");
Defensive patterns

Strategy: try-catch

Validate before calling

if (!auth.isAuthenticated() || auth.isTokenExpired()) {
    await auth.refreshOrLogin();
    return; // only call EndImpersonation with a live session
}

Type guard

bool canEndImpersonation(AuthState s) => s is { IsAuthenticated: true, AccessToken.Length: > 0 };

Try / catch

try
{
    await api.post("/impersonation/end");
}
catch (UnauthorizedException)
{
    await auth.login(); // session gone; recover via fresh login
}

Prevention

When it happens

Trigger: Calling the EndImpersonation endpoint with no JWT, an expired token, or an otherwise invalid/unrecognized authentication scheme so ICurrentUser.IsAuthenticated() returns false.

Common situations: The impersonation/access token expired while impersonating; the client never attached the Authorization header; the token was issued before a key/signing-credential rotation and now fails validation.

Understand the failure class

Related errors


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

Appendix: source

Thrown at src/Modules/Identity/Modules.Identity/Features/v1/Impersonation/EndImpersonation/EndImpersonationCommandHandler.cs:52

    {
        _identityService = identityService;
        _tokenService = tokenService;
        _securityAudit = securityAudit;
        _currentUser = currentUser;
        _requestContext = requestContext;
        _grantService = grantService;
        _logger = logger;
    }

    public async ValueTask<TokenResponse> Handle(
        EndImpersonationCommand request,
        CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(request);

        if (!_currentUser.IsAuthenticated())
        {
            throw new UnauthorizedException();
        }

        var claims = _currentUser.GetUserClaims()?.ToList()
            ?? throw new UnauthorizedException();

        var actorUserId = claims.FirstOrDefault(c => c.Type == ClaimConstants.ActorSubject)?.Value;
        var actorTenantId = claims.FirstOrDefault(c => c.Type == ClaimConstants.ActorTenant)?.Value;
        var jti = claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Jti)?.Value;

        if (string.IsNullOrWhiteSpace(actorUserId) || string.IsNullOrWhiteSpace(actorTenantId))
        {
            // Signed in but no act_sub claim (End called on a non-impersonation token): client error,
            // must be 4xx not CustomException's default 500.
            throw new CustomException(
                "current session is not an impersonation session",
                errors: null,
                System.Net.HttpStatusCode.BadRequest);
        }

View on GitHub (pinned to 3f2959e683)