{"record":{"id":"169603701c060bac","repo":"fullstackhero/dotnet-starter-kit","slug":"failed-to-create-user-from-external-principal","errorCode":null,"errorMessage":"Failed to create user from external principal.","messagePattern":"Failed to create user from external principal\\.","errorType":"exception","errorClass":"CustomException","httpStatus":400,"severity":"error","filePath":"src/Modules/Identity/Modules.Identity/Services/UserRegistrationService.cs","lineNumber":193,"sourceCode":"\n        userName = await EnsureUniqueUserNameAsync(userName);\n\n        var user = new FshUser\n        {\n            Email = email,\n            UserName = userName,\n            FirstName = firstName,\n            LastName = lastName,\n            EmailConfirmed = true,\n            PhoneNumberConfirmed = false,\n            IsActive = true\n        };\n\n        var result = await userManager.CreateAsync(user);\n        if (!result.Succeeded)\n        {\n            var errors = result.Errors.Select(e => e.Description).ToList();\n            throw new CustomException(\n                \"Failed to create user from external principal.\",\n                errors,\n                HttpStatusCode.BadRequest);\n        }\n\n        return user;\n    }\n\n    private static (string firstName, string lastName, string userName) ExtractUserInfoFromPrincipal(\n        ClaimsPrincipal principal, string email)\n    {\n        var firstName = principal.FindFirstValue(ClaimTypes.GivenName)\n            ?? principal.FindFirstValue(\"given_name\")\n            ?? string.Empty;\n\n        var lastName = principal.FindFirstValue(ClaimTypes.Surname)\n            ?? principal.FindFirstValue(\"family_name\")\n            ?? string.Empty;","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/fullstackhero/dotnet-starter-kit/blob/3f2959e683e9f83f13e55e1678c9119f63c7e8e5/src/Modules/Identity/Modules.Identity/Services/UserRegistrationService.cs#L175-L211","documentation":"Thrown as a CustomException (400) when userManager.CreateAsync fails while provisioning a local user from an external-auth principal. The message is generic on purpose; the specific Identity error descriptions (duplicate email/username, password policy, validator failures) are attached in the errors list. It means the external identity is valid but local account creation was rejected by ASP.NET Identity.","triggerScenarios":"Signing in with an external provider whose email already belongs to an existing local account (duplicate username/email); custom user validators failing; normalized username collisions.","commonSituations":"User first registered with password, later tries Google sign-in with the same email; two providers issuing the same email; username generated from email exceeding length limits; custom IUserValidator misconfigured.","solutions":["Inspect the errors array on the exception for the exact Identity reason","If duplicate email, route the user to the external-login linking flow (sign in with password, then link provider) instead of auto-creating","Adjust generated userName/email normalization to avoid collisions (e.g. append provider id)","Review custom IUserValidator/IEmailValidator registrations for overly strict rules"],"exampleFix":"// before\nvar user = await userRegistrationService.GetOrCreateFromPrincipalAsync(principal, provider);\n// after\ntry\n{\n    var user = await userRegistrationService.GetOrCreateFromPrincipalAsync(principal, provider);\n}\ncatch (CustomException ex) when (ex.Errors?.Any(e => e.Contains(\"already taken\")) == true)\n{\n    return Results.Conflict(\"An account with this email exists. Sign in with your password to link the provider.\");\n}","handlingStrategy":"try-catch","validationCode":"var email = principal.FindFirstValue(ClaimTypes.Email); var exists = await userManager.Users.AnyAsync(u => u.NormalizedEmail == email!.ToUpperInvariant());","typeGuard":"bool CanCreate(string email) => !string.IsNullOrWhiteSpace(email) && email.Contains('@');","tryCatchPattern":"catch (CustomException ex) when (ex.Errors?.Any(e => e.Contains(\"already\")) == true) { return Results.Conflict(\"Account exists — sign in and link the provider instead.\"); }","preventionTips":["Expose ex.Errors to clients instead of the generic message","Implement provider linking for existing local accounts","Generate unique usernames (email prefix + provider id) to avoid collisions","Test each external provider against a pre-existing same-email local account"],"tags":["identity","external-auth","duplicate-user","aspnet-identity"],"backgroundTag":"api-error-response","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"}