{"record":{"id":"bf19a7b653349ae3","repo":"fullstackhero/dotnet-starter-kit","slug":"user-userid-not-found-enrolltwofactorcommandhandler","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/Enroll/EnrollTwoFactorCommandHandler.cs","lineNumber":38,"sourceCode":"    public EnrollTwoFactorCommandHandler(UserManager<FshUser> userManager, ICurrentUser currentUser)\n    {\n        _userManager = userManager;\n        _currentUser = currentUser;\n    }\n\n    public async ValueTask<TwoFactorEnrollmentResponse> Handle(\n        EnrollTwoFactorCommand 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        // Always reset so calling enroll twice rotates the secret — prevents stale codes\n        // from a prior incomplete enrollment from silently succeeding.\n        await _userManager.ResetAuthenticatorKeyAsync(user);\n        var sharedKey = await _userManager.GetAuthenticatorKeyAsync(user)\n            ?? throw new CustomException(\"Failed to generate authenticator key.\");\n\n        var email = user.Email ?? user.UserName ?? user.Id;\n        var authenticatorUri = string.Format(\n            System.Globalization.CultureInfo.InvariantCulture,\n            \"otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6\",\n            UrlEncoder.Default.Encode(IssuerName),\n            UrlEncoder.Default.Encode(email),\n            sharedKey);\n\n        return new TwoFactorEnrollmentResponse(sharedKey, authenticatorUri);\n    }\n}","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Features/v1/TwoFactor/Enroll/EnrollTwoFactorCommandHandler.cs#L20-L56","documentation":"EnrollTwoFactorCommandHandler throws NotFoundException when the user id from the current principal cannot be resolved by UserManager.FindByIdAsync. The token is authenticated but references a user record that is absent from the identity store.","triggerScenarios":"Enrolling with a token issued for a since-deleted user; a token whose nameidentifier claim belongs to another database/environment; test fixtures with invented user ids.","commonSituations":"Stale token after account deletion; switching connection strings/environments while reusing cached tokens; database re-seeded without re-issuing tokens.","solutions":["Re-authenticate so the token maps to an existing user","Verify the user id claim exists inAspNetUsers in the database the API actually uses","Confirm environment/config points at the intended DB (no dev/prod mixup)","Return a clearer session-invalid message instead of a raw not-found if soft-deleted users should be treated as logged out"],"exampleFix":"// before\nvar user = await _userManager.FindByIdAsync(userId)\n    ?? throw new NotFoundException($\"User {userId} not found.\");\n// after\nvar user = await _userManager.FindByIdAsync(userId);\nif (user is null)\n{\n    throw new UnauthorizedException(\"Session is no longer valid. Please sign in again.\");\n}","handlingStrategy":"try-catch","validationCode":"const sub = parseJwt(accessToken)?.sub;\nif (!sub) await reauthenticate();","typeGuard":null,"tryCatchPattern":"try {\n  await api.enrollTwoFactor();\n} catch (e) {\n  if (e.status === 404) { clearSession(); await reauthenticate(); return; }\n  throw e;\n}","preventionTips":["Invalidate tokens on account deletion","Re-login after DB/environment changes","Map token-for-missing-user to a session-expired UX"],"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"}