{"record":{"id":"86e3cac1d8f24779","repo":"fullstackhero/dotnet-starter-kit","slug":"error-resetting-password","errorCode":null,"errorMessage":"error resetting password","messagePattern":"error resetting password","errorType":"exception","errorClass":"CustomException","httpStatus":400,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Services/UserPasswordService.cs","lineNumber":78,"sourceCode":"    }\n\n    public async Task ResetPasswordAsync(string email, string password, string token, CancellationToken cancellationToken)\n    {\n        EnsureValidTenant();\n\n        var user = await userManager.FindByEmailAsync(email);\n        if (user == null)\n        {\n            throw new NotFoundException(\"user not found\");\n        }\n\n        token = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(token));\n        var result = await userManager.ResetPasswordAsync(user, token, password);\n\n        if (!result.Succeeded)\n        {\n            var errors = result.Errors.Select(e => e.Description).ToList();\n            throw new CustomException(\"error resetting password\", errors);\n        }\n\n        // Raise domain event for password reset\n        var tenantId = multiTenantContextAccessor?.MultiTenantContext?.TenantInfo?.Id;\n        user.RecordPasswordChanged(wasReset: true, tenantId);\n        await db.SaveChangesAsync(cancellationToken);\n    }\n\n    public async Task ChangePasswordAsync(string password, string newPassword, string confirmNewPassword, string userId, CancellationToken cancellationToken = default)\n    {\n        var user = await userManager.FindByIdAsync(userId);\n\n        _ = user ?? throw new NotFoundException(\"user not found\");\n\n        var result = await userManager.ChangePasswordAsync(user, password, newPassword);\n\n        if (!result.Succeeded)\n        {","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Services/UserPasswordService.cs#L60-L96","documentation":"ResetPasswordAsync throws CustomException('error resetting password') when userManager.ResetPasswordAsync fails (result.Succeeded == false). The Identity result errors (e.g. invalid/expired token, weak password) are attached to the exception as a list of descriptions.","triggerScenarios":"Submitting a reset token that is invalid, already used, or expired; new password violating the configured Identity password policy (length, complexity, reuse); token mangled by incorrect Base64Url encoding/decoding.","commonSituations":"Reset link older than the token lifetime; user requested multiple resets and used the older token; client double-encoded/decoded the Base64Url token; new password rejected by policy (e.g. too similar to old or missing special char).","solutions":["Inspect the errors collection attached to the exception — it names the exact Identity failure","Request a fresh password-reset token and retry promptly before it expires","Ensure the token is sent exactly as generated (Base64Url encoded, no trimming/HTML unescaping)","Choose a password satisfying the configured Identity password options"],"exampleFix":"// before: swallowing the detail\ncatch (CustomException) { return Results.BadRequest(\"reset failed\"); }\n// after\ncatch (CustomException ex) { return Results.BadRequest(new { ex.Message, errors = ex.Errors }); }","handlingStrategy":"try-catch","validationCode":"var policyErrors = newPassword is null || newPassword.Length < 8\n    ? new List<string> { \"Password must be at least 8 characters.\" }\n    : new List<string>();\nif (policyErrors.Count > 0) return Results.BadRequest(policyErrors);","typeGuard":null,"tryCatchPattern":"try\n{\n    await passwordService.ResetPasswordAsync(email, token, newPassword, ct);\n}\ncatch (CustomException ex)\n{\n    return Results.BadRequest(new { message = ex.Message, identityErrors = ex.Errors });\n}","preventionTips":["Use reset tokens promptly — they expire quickly","Never re-encode or trim the Base64Url token between generation and submission","Enforce password policy client-side to mirror Identity options","Avoid issuing multiple reset tokens for one attempt; always use the latest"],"tags":["identity","password-reset","token","validation"],"backgroundTag":"schema-validation-failed","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"}