Every ASP.NET Core app that needs sign-in has to answer a question before writing a single line of authentication code: who owns the identities, and who issues the tokens? The answer might be your own database through ASP.NET Core Identity, a managed platform such as Microsoft Entra ID, a self-hosted OAuth/OIDC server such as Duende IdentityServer or OpenIddict, or a commercial identity-as-a-service product such as Auth0 or Okta. This guide compares those options on the dimensions that actually drive the decision β€” hosting burden, licensing, protocol support and multi-tenant fit β€” and covers what migrating between them involves.

What Is an Identity Stack, and Why the Choice Matters#

"Identity stack" covers everything that turns a username, a passkey or a social login into a ClaimsPrincipal your app can authorize against: the user store, the credential checks, token issuance, and often multi-factor and account-recovery flows. Get the schemes right (see authentication in ASP.NET Core) and the policies right (see authorization in ASP.NET Core), and the stack underneath still matters, because it decides how much you build versus operate versus pay for, and how hard it is to add a mobile app, a partner API or a second tenant later. Changing stacks after launch is expensive β€” it touches every client, every stored credential and often every session β€” so this is a decision worth making deliberately up front.

How the Options Differ: Library, Managed Service or Self-Hosted Server#

Every option in this guide falls into one of three categories:

  • A library that runs inside your app. ASP.NET Core Identity is this: it owns a Users table in your database and issues its own cookies and, optionally, proprietary bearer tokens. There is no separate service to deploy.
  • A managed, multi-tenant identity platform. Microsoft Entra ID, Entra External ID, Auth0 and Okta run as SaaS. You register an application with them and never operate the identity provider yourself.
  • A self-hosted OAuth/OIDC server framework. Duende IdentityServer, OpenIddict and Keycloak are frameworks (or, for Keycloak, a ready-made server) that you deploy, and which then issue standards-based tokens to every other app in your organization, including apps that aren't ASP.NET Core at all.

The first category is the least operational overhead and the least reach beyond your own app. The third is the most control and the most operational responsibility. Most teams land on the second for workforce and customer sign-in, and reach for the third only when they need to run their own token issuer, for example to support many first- and third-party clients against one set of accounts.

Getting Started: A Minimal ASP.NET Core Identity API#

For an app that only needs its own users, ASP.NET Core Identity's MapIdentityApi<TUser> gives you registration, login, email confirmation, two-factor and password-reset endpoints without writing them yourself:

C#
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));

builder.Services.AddAuthorization();
builder.Services.AddIdentityApiEndpoints<IdentityUser>()
    .AddEntityFrameworkStores<AppDbContext>();

var app = builder.Build();

app.MapIdentityApi<IdentityUser>();
app.MapGet("/me", (ClaimsPrincipal user) => user.Identity?.Name).RequireAuthorization();

app.Run();

This is the fastest path to working authentication, and it is also the boundary of what Identity is designed for: it issues cookies or its own proprietary bearer tokens, not standards-based JWTs or OAuth 2.0 grants for other applications to consume. The rest of this guide is about what to reach for once you outgrow that boundary.

ASP.NET Core Identity: Own Your User Store#

ASP.NET Core Identity is the right default when your app is the only client, you're comfortable owning the user database, and you don't need to issue tokens that other services will independently validate. UserManager<TUser> and SignInManager<TUser> handle password hashing, lockout, two-factor authentication and external login provisioning; RoleManager<TRole> backs role-based checks (see roles in ASP.NET Core authorization). As of .NET 10, Identity also has built-in support for passkeys (WebAuthn/FIDO2 credentials), configurable through IdentityPasskeyOptions and enabled by default in the Blazor Web App project template:

C#
builder.Services.Configure<IdentityPasskeyOptions>(options =>
{
    options.ServerDomain = "app.contoso.com"; // Pin the Relying Party ID explicitly
    options.UserVerificationRequirement = "required"; // Require biometric or PIN verification
});

The trade-off is reach: Identity is not a general-purpose OAuth/OIDC server. If a mobile app, a partner or a second internal service needs to obtain and independently validate its own token, look at Duende IdentityServer or OpenIddict instead of trying to stretch Identity to cover that case.

Microsoft Entra ID and Entra External ID: Managed Identity Platform#

Microsoft Entra ID is Microsoft's workforce identity platform β€” the right choice when your users already have organizational accounts, and you want single sign-on, conditional access and Microsoft Graph integration without running any identity infrastructure yourself. Microsoft Entra External ID is the customer-identity (CIAM) counterpart, and Microsoft now recommends it over Azure AD B2C for new customer-facing ASP.NET Core apps. Both are driven from ASP.NET Core through the Microsoft.Identity.Web package:

C#
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("EntraId"));

builder.Services.AddControllersWithViews()
    .AddMicrosoftIdentityUI(); // Adds ready-made sign-in/sign-out UI

Entra ID and Entra External ID cost nothing to operate beyond your Microsoft 365 or Azure subscription for reasonable usage tiers, scale to enterprise workforce sizes without you managing a server, and integrate natively with the rest of the Microsoft ecosystem. The trade-off is less control over the exact sign-in experience and token contents compared to a self-hosted server, and a dependency on Microsoft's platform for an outage or an incident.

Duende IdentityServer: A Full OAuth/OIDC Server You Host#

Duende IdentityServer is a standards-compliant OpenID Connect and OAuth 2.0 framework you run as your own service, issuing tokens to any number of first- and third-party clients against whatever user store you plug in, including ASP.NET Core Identity itself:

C#
builder.Services.AddIdentityServer()
    .AddInMemoryClients(builder.Configuration.GetSection("IdentityServer:Clients"))
    .AddInMemoryApiScopes(builder.Configuration.GetSection("IdentityServer:ApiScopes"))
    .AddAspNetIdentity<IdentityUser>(); // Bridges to your existing Identity user store

Duende IdentityServer is source-available, not free for production: development and testing require no license, but production use requires a paid license, alongside a free Community Edition for qualifying smaller organizations. The current major version targets net10.0. Weigh the license cost against what you'd spend building and maintaining the equivalent OAuth/OIDC server yourself β€” for an organization issuing tokens to many internal and external clients, that comparison usually favors Duende.

OpenIddict: The Open-Source OAuth/OIDC Server Framework#

OpenIddict covers similar ground to Duende IdentityServer β€” client, server and token-validation building blocks for OAuth 2.0 and OpenID Connect β€” but ships under the Apache-2.0 license with no commercial licensing requirement. It targets .NET 8, 9 and 10, as well as .NET Framework, and integrates directly with your app's own Entity Framework Core DbContext rather than requiring a separate data store:

C#
builder.Services.AddDbContext<AppDbContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("Default"));
    options.UseOpenIddict(); // Registers OpenIddict's entity sets in your existing context
});

builder.Services.AddOpenIddict()
    .AddCore(options => options.UseEntityFrameworkCore().UseDbContext<AppDbContext>())
    .AddServer(options =>
    {
        options.SetTokenEndpointUris("connect/token");
        options.AllowClientCredentialsFlow();
        options.AddDevelopmentSigningCertificate(); // Replace with a real certificate in production
        options.UseAspNetCore().EnableTokenEndpointPassthrough();
    });

OpenIddict is the natural choice when licensing cost rules out Duende IdentityServer but you still need a real, standards-compliant token server rather than Identity's proprietary tokens. The trade-off is that you own the security review, certificate rotation and operational runbook yourself; there's no vendor to call.

Keycloak: Self-Hosted, Open-Source and Java-Based#

Keycloak is a ready-to-run, open-source identity and access management server, not a .NET library β€” you deploy it as its own service (a container is the common path) and point your ASP.NET Core apps at it with the same standard handlers you'd use for any OIDC provider, since Keycloak speaks standard OpenID Connect and OAuth 2.0:

C#
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = "https://keycloak.contoso.com/realms/contoso";
        options.Audience = "orders-api";
    });

There is no first-party .NET SDK; you configure AddJwtBearer or AddOpenIdConnect against the realm's issuer URL exactly as you would for any other OIDC server, and manage users, clients and realms through Keycloak's own admin console or REST admin API. Keycloak is a strong fit for organizations that want a self-hosted, vendor-neutral identity server and already run the infrastructure (and, typically, the Java expertise) to operate it.

Auth0 and Okta: Identity as a SaaS Product#

Auth0 and Okta are commercial, multi-tenant identity-as-a-service platforms: no server to run, a hosted login page and admin console, and an ASP.NET Core integration built on top of standard OpenID Connect. Both publish an official NuGet package:

C#
// Auth0
builder.Services.AddAuth0WebAppAuthentication(options =>
{
    options.Domain = builder.Configuration["Auth0:Domain"]!;
    options.ClientId = builder.Configuration["Auth0:ClientId"]!;
});

// Okta
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OktaDefaults.MvcAuthenticationScheme;
})
.AddCookie()
.AddOktaMvc(new OktaMvcOptions
{
    OktaDomain = builder.Configuration["Okta:Domain"],
    ClientId = builder.Configuration["Okta:ClientId"],
    ClientSecret = builder.Configuration["Okta:ClientSecret"],
});

These packages come from Auth0.AspNetCore.Authentication and Okta.AspNetCore respectively. Both products are billed by monthly active users past a free tier, and both are a fast way to get enterprise features β€” social login, adaptive MFA, breached-password detection β€” without building them. The trade-off is the same as any SaaS dependency: your sign-in flow is only as available as the vendor, and per-user pricing can grow faster than infrastructure costs as you scale.

Decision Matrix: Choosing an Identity Stack#

StackHostingCost modelToken standardBest for
ASP.NET Core IdentityIn-process, your databaseFree (open source)Cookies; proprietary bearer tokensSingle-app, first-party-only sign-in
Entra ID / Entra External IDManaged by MicrosoftIncluded in subscription tiers, then per-userOIDC / OAuth 2.0Workforce SSO; CIAM on the Microsoft stack
Duende IdentityServerSelf-hostedPaid license for production; free dev/test and a Community EditionOIDC / OAuth 2.0Many first- and third-party clients, enterprise support
OpenIddictSelf-hostedFree (Apache-2.0)OIDC / OAuth 2.0Standards-compliant server without license cost
KeycloakSelf-hosted (own infra)Free (open source)OIDC / OAuth 2.0Vendor-neutral, self-hosted, non-Microsoft stacks
Auth0 / OktaManaged SaaSPer-user, after a free tierOIDC / OAuth 2.0Fast time-to-market, cross-cloud, enterprise SaaS features

Migration Considerations#

Moving between these stacks is rarely a drop-in swap, because each one owns credentials, session state or both:

  • Identity to a token server (Duende or OpenIddict). AddAspNetIdentity<TUser>() in Duende IdentityServer, and the equivalent Identity integration in OpenIddict, both let the new server sit on top of your existing Identity user store, so you don't have to migrate password hashes immediately β€” only add a token-issuing layer in front.
  • Password hash compatibility. PasswordHasher<TUser>.VerifyHashedPassword returns PasswordVerificationResult.SuccessRehashNeeded when a stored hash used an older algorithm version; handle that return value by rehashing on next successful login instead of forcing a mass password reset.
  • Azure AD B2C to Entra External ID. Microsoft's official guidance is to migrate existing B2C applications to Entra External ID rather than build new CIAM projects on B2C; the migration path covers moving both app registrations and user accounts, including password migration strategies.
  • Self-hosted to managed, or the reverse. Plan for a dual-running window: keep both issuers' signing keys valid and both Authority values accepted by downstream APIs (see multiple JWT bearer schemes) until every client has moved, rather than cutting over all at once.
  • Session invalidation. Moving identity providers usually means every existing session or refresh token becomes invalid; communicate a forced re-authentication window to users instead of letting them hit unexplained failures.

Best Practices#

  • Start with the category, not the product. Decide library vs. managed platform vs. self-hosted server first; the specific vendor choice is much easier once that's settled.
  • Don't build a token server you don't need. If your app is the only client, Identity's cookies or proprietary tokens are simpler and cheaper to operate than standing up Duende or OpenIddict.
  • Budget for license cost as an operating expense, not a one-time decision, when evaluating Duende IdentityServer against OpenIddict or a managed platform.
  • Keep the user store separate from the token issuer where possible. Both Duende IdentityServer and OpenIddict can sit on top of ASP.NET Core Identity's store, which keeps a future switch between them cheaper.
  • Verify passkey and 2FA support against your actual target framework, since features such as ASP.NET Core Identity passkeys are version-gated (.NET 10 and later).

Common Pitfalls#

  • Treating Identity as an OAuth server. Its bearer tokens are proprietary and meant for simple, first-party scenarios; don't try to hand them to a separate downstream API expecting a standard JWT.
  • Assuming Auth0 or Okta pricing scales linearly with infrastructure cost. Per-user SaaS pricing can outpace what the equivalent self-hosted option would cost at scale; model this before committing.
  • Deploying Duende IdentityServer to production without a license. The package works in development without one, which has led teams to discover the licensing requirement only when they ship.
  • Starting new customer-identity projects on Azure AD B2C. Microsoft's current guidance directs new CIAM projects to Entra External ID instead.
  • Underestimating operational ownership of a self-hosted server. Keycloak and OpenIddict both require you to handle certificate rotation, availability and security patching that a managed platform absorbs for you.

Frequently Asked Questions#

Is ASP.NET Core Identity enough, or do I need a real OAuth server?#

Identity is enough when your application is the only client that will ever validate its tokens. The moment a separate service, mobile app or partner needs to independently validate a token issued for your users, you need an actual OAuth/OIDC server such as Duende IdentityServer or OpenIddict, or a managed platform such as Entra ID.

Is Duende IdentityServer free to use?#

It's free for development and testing, and there's a free Community Edition for qualifying smaller organizations, but production use otherwise requires a paid commercial license. OpenIddict, which covers similar OAuth/OIDC server scenarios, is free and open source under the Apache-2.0 license.

Should new projects still use Azure AD B2C?#

No. Microsoft now directs new customer-facing ASP.NET Core projects to Microsoft Entra External ID, and provides a documented migration path for existing Azure AD B2C applications. Existing B2C tenants continue to work, but new CIAM projects shouldn't start there.

Does ASP.NET Core Identity support passkeys?#

Yes, as of .NET 10. Identity's passkey support covers registering a passkey on an existing account, passwordless account creation, and passwordless sign-in, configured through IdentityPasskeyOptions. It's built into the Blazor Web App project template; earlier target frameworks don't have it.

How do I choose between Keycloak and a managed platform like Entra ID or Auth0?#

Choose Keycloak when you need a self-hosted, vendor-neutral server, typically for regulatory, cost-at-scale or multi-cloud reasons, and you have the operational capacity to run it. Choose a managed platform when minimizing operational burden matters more than owning the infrastructure, or when you're already standardized on that vendor's broader ecosystem.

Summary#

  • Identity stacks split into three categories: an in-process library (ASP.NET Core Identity), a managed platform (Entra ID, Entra External ID, Auth0, Okta), and a self-hosted OAuth/OIDC server (Duende IdentityServer, OpenIddict, Keycloak).
  • Reach for Identity alone only when your app is the sole client of its own tokens.
  • Duende IdentityServer requires a paid license for production; OpenIddict is a free, open-source alternative with similar standards coverage; Keycloak is a self-hosted, non-.NET option.
  • Microsoft now steers new customer-identity projects toward Entra External ID instead of Azure AD B2C.
  • Plan migrations around dual-running windows and password-hash compatibility rather than a single cutover.

Further Reading#