{"record":{"id":"05bf640c1fccb20f","repo":"fullstackhero/dotnet-starter-kit","slug":"invalid-credentials","errorCode":null,"errorMessage":"Invalid credentials.","messagePattern":"Invalid credentials\\.","errorType":"exception","errorClass":"UnauthorizedAccessException","httpStatus":401,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Features/v1/Tokens/TokenGeneration/GenerateTokenCommandHandler.cs","lineNumber":73,"sourceCode":"        var ip = _requestContext.IpAddress ?? \"unknown\";\n        var ua = _requestContext.UserAgent ?? \"unknown\";\n        var clientId = _requestContext.ClientId;\n\n        // Validate credentials (includes 2FA verification when the user has it enabled)\n        var identityResult = await _identityService\n            .ValidateCredentialsAsync(request.Email, request.Password, request.TwoFactorCode, cancellationToken);\n\n        if (identityResult is null)\n        {\n            // 1) Audit failed login BEFORE throwing\n            await _securityAudit.LoginFailedAsync(\n                subjectIdOrName: request.Email,\n                clientId: clientId!,\n                reason: \"InvalidCredentials\",\n                ip: ip,\n                ct: cancellationToken);\n\n            throw new UnauthorizedAccessException(\"Invalid credentials.\");\n        }\n\n        // Unpack subject + claims\n        var (subject, claims) = identityResult.Value;\n\n        // 2) Audit successful login\n        await _securityAudit.LoginSucceededAsync(\n            userId: subject,\n            userName: claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value ?? request.Email,\n            clientId: clientId!,\n            ip: ip,\n            userAgent: ua,\n            ct: cancellationToken);\n\n        // Issue token\n        var token = await _tokenService.IssueAsync(subject, claims, /*extra*/ null, cancellationToken);\n\n        // Persist refresh token (hashed) for this user","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Features/v1/Tokens/TokenGeneration/GenerateTokenCommandHandler.cs#L55-L91","documentation":"Thrown by the token generation handler after ValidateCredentialsAsync (which also covers 2FA verification when enabled) returns a null identity result. It means the email/password pair (or supplied two-factor code) failed authentication; the handler first records a failed-login security audit entry bound to the request's IP/user-agent/clientId, then throws.","triggerScenarios":"Wrong password, nonexistent email, account locked/disabled, or the password hash doesn't verify against the stored Identity user.","commonSituations":"Users typing wrong password; seed data reset so the account no longer exists; pointing the app at an empty/different database; Identity lockout after repeated failures.","solutions":["Verify the email/password are correct and the account is not locked out","Re-run the DbMigrator --seed if the user record is missing","Check you're connecting to the intended database/environment"],"exampleFix":"// before\ndotnet run --project src/Host/FSH.Starter.Api\n// after\ndotnet run --project src/Host/FSH.Starter.DbMigrator -- apply --seed\ndotnet run --project src/Host/FSH.Starter.Api","handlingStrategy":"validation","validationCode":"if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))\n    throw new ValidationException(\"Email and password are required before calling generate-token\");","typeGuard":null,"tryCatchPattern":"try { return await api.post('/tokens', { email, password }); }\ncatch (UnauthorizedAccessException) { showError('Invalid email or password'); return null; } // never reveal which one was wrong","preventionTips":["Show a generic 'invalid credentials' message; never reveal whether the email exists","Handle lockout: after repeated failures direct the user to password reset","Ensure seed users exist (DbMigrator --seed) in fresh environments","Confirm API/database environment variables point at the intended database"],"tags":["auth","login","credentials","unauthorized"],"backgroundTag":"invalid-credentials","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"}