{"record":{"id":"71d8a6d414eb05b9","repo":"fullstackhero/dotnet-starter-kit","slug":"user-is-not-authenticated","errorCode":null,"errorMessage":"User is not authenticated.","messagePattern":"User is not authenticated\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"warning","filePath":"src/Modules/Identity/Modules.Identity/Features/v1/Users/ChangePassword/ChangePasswordCommandHandler.cs","lineNumber":25,"sourceCode":"\npublic sealed class ChangePasswordCommandHandler : ICommandHandler<ChangePasswordCommand, string>\n{\n    private readonly IUserService _userService;\n    private readonly ICurrentUser _currentUser;\n\n    public ChangePasswordCommandHandler(IUserService userService, ICurrentUser currentUser)\n    {\n        _userService = userService;\n        _currentUser = currentUser;\n    }\n\n    public async ValueTask<string> Handle(ChangePasswordCommand command, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(command);\n\n        if (!_currentUser.IsAuthenticated())\n        {\n            throw new InvalidOperationException(\"User is not authenticated.\");\n        }\n\n        var userId = _currentUser.GetUserId().ToString();\n\n        await _userService.ChangePasswordAsync(command.Password, command.NewPassword, command.ConfirmNewPassword, userId, cancellationToken).ConfigureAwait(false);\n\n        return \"password reset email sent\";\n    }\n}","sourceCodeStart":7,"sourceCodeEnd":34,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Features/v1/Users/ChangePassword/ChangePasswordCommandHandler.cs#L7-L34","documentation":"ChangePasswordCommandHandler throws InvalidOperationException('User is not authenticated.') when ICurrentUser.IsAuthenticated() is false. Unlike the 2FA handlers it uses the BCL exception type instead of the module's UnauthorizedException, so it surfaces as a 500 unless mapped.","triggerScenarios":"Calling change-password without a valid bearer token or with an expired JWT; invoking the handler directly in tests without an authenticated ICurrentUser; a route accidentally exposed without authorization.","commonSituations":"Token expired mid-session; frontend dropped the Authorization header; auth middleware misordered or omitted; test harness lacking an authenticated principal.","solutions":["Sign in again and retry with a fresh Authorization: Bearer header","Confirm the endpoint requires authorization and middleware order is UseAuthentication → UseAuthorization","In tests, stub ICurrentUser with IsAuthenticated() = true","Consider aligning with the module's UnauthorizedException so callers get a proper 401 instead of a 500"],"exampleFix":"// before\nif (!_currentUser.IsAuthenticated())\n{\n    throw new InvalidOperationException(\"User is not authenticated.\");\n}\n// after\nif (!_currentUser.IsAuthenticated())\n{\n    throw new UnauthorizedException();\n}","handlingStrategy":"try-catch","validationCode":"function canChangePassword() {\n  return Boolean(accessToken) && !isTokenExpired(accessToken);\n}\nif (!canChangePassword()) await reauthenticate();","typeGuard":null,"tryCatchPattern":"try {\n  await api.changePassword(payload);\n} catch (e) {\n  if (e.status === 500 && /not authenticated/i.test(e.message)\n      || e.status === 401) {\n    await reauthenticate();\n    return retry();\n  }\n  throw e;\n}","preventionTips":["Refresh the token before sensitive account operations","Centralize auth header injection in the API client","Server-side: prefer UnauthorizedException over InvalidOperationException so callers get a 401, not a 500"],"tags":["authentication","jwt","identity","password"],"backgroundTag":"authentication-required","analyzedSha":"3f2959e683e9f83f13e55e1678c9119f63c7e8e5","analyzedAt":"2026-09-15T22:20:53.684Z","contentChangedAt":"2026-09-15T22:20:53.684Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}