{"record":{"id":"635b397c082aa0b4","repo":"fullstackhero/dotnet-starter-kit","slug":"user-userid-not-found-verifyenrolltwofactorcommandhandler","errorCode":null,"errorMessage":"User {userId} not found.","messagePattern":"User (.+?) not found\\.","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"warning","filePath":"src/Modules/Identity/Modules.Identity/Features/v1/TwoFactor/VerifyEnroll/VerifyEnrollTwoFactorCommandHandler.cs","lineNumber":34,"sourceCode":"    public VerifyEnrollTwoFactorCommandHandler(UserManager<FshUser> userManager, ICurrentUser currentUser)\n    {\n        _userManager = userManager;\n        _currentUser = currentUser;\n    }\n\n    public async ValueTask<bool> Handle(\n        VerifyEnrollTwoFactorCommand command, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(command);\n\n        if (!_currentUser.IsAuthenticated())\n        {\n            throw new UnauthorizedException();\n        }\n\n        var userId = _currentUser.GetUserId().ToString();\n        var user = await _userManager.FindByIdAsync(userId)\n            ?? throw new NotFoundException($\"User {userId} not found.\");\n\n        var sanitized = command.Code.Replace(\" \", string.Empty, StringComparison.Ordinal);\n        var valid = await _userManager.VerifyTwoFactorTokenAsync(\n            user,\n            _userManager.Options.Tokens.AuthenticatorTokenProvider,\n            sanitized);\n\n        if (!valid)\n        {\n            throw new CustomException(\n                \"The authenticator code is invalid.\",\n                errors: null,\n                System.Net.HttpStatusCode.BadRequest);\n        }\n\n        await _userManager.SetTwoFactorEnabledAsync(user, true);\n        return true;\n    }","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Features/v1/TwoFactor/VerifyEnroll/VerifyEnrollTwoFactorCommandHandler.cs#L16-L52","documentation":"Thrown during two-factor enrollment verification when UserManager.FindByIdAsync for the current authenticated user's ID returns null. This guard fires when the principal's userId no longer maps to a real FshUser (e.g. deleted between token issue and verification), after an earlier UnauthorizedException check already filtered anonymous callers.","triggerScenarios":"Verifying enrollment with a token issued for a deleted user; a token whose subject claim targets a different environment's database; synthetic test ids.","commonSituations":"Account deleted after enrollment started; environment/connection-string mismatch reusing old tokens; DB re-seed without re-login.","solutions":["Log in again to get a token bound to an existing user","Confirm the user id claim exists in the identity database the API uses","Check that the API targets the expected environment/database","Treat token-for-missing-user as a session-invalid case in client handling"],"exampleFix":"// before\nawait verifyEnroll(code); // old token, user deleted\n// after\nif (isSessionInvalidError(err)) await reauthenticate();\nawait verifyEnroll(code);","handlingStrategy":"try-catch","validationCode":"const sub = parseJwt(accessToken)?.sub;\nif (!sub) await reauthenticate();","typeGuard":null,"tryCatchPattern":"try {\n  await api.verifyEnrollTwoFactor({ code });\n} catch (e) {\n  if (e.status === 404) { clearSession(); await reauthenticate(); return; }\n  throw e;\n}","preventionTips":["Don't reuse tokens across environments or after DB re-seeds","Invalidate tokens when accounts are deleted","Treat 404-on-current-user as a re-auth signal"],"tags":["identity","user-not-found","jwt","two-factor"],"backgroundTag":"user-not-found","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"}