Identity Providers Comparison
Key Points
- Hosted (cloud) IdPs — Microsoft Entra ID (formerly Azure AD), Auth0, Okta, AWS Cognito, Google Identity Platform. Pay per user/MAU.
- Self-hosted IdPs — Duende IdentityServer (commercial since v6), OpenIddict (free, MIT), Keycloak (free, Java).
- Senior choice in 2026 .NET ecosystem: Entra ID (if Microsoft shop), Auth0/Okta (best UX/integrations, paid), Duende (regulated/on-prem .NET), OpenIddict (free .NET on-prem), Keycloak (cross-stack).
- ASP.NET Core Identity is not an IdP — it's a user store. Pair with Duende/OpenIddict if you need OAuth2/OIDC server.
- The real choice driver: data residency, compliance, multi-tenant story, B2B/B2C, MFA needs, social logins, cost, vendor risk.
Concepts (deep dive)
Microsoft Entra ID
Microsoft's cloud IdP (renamed from Azure AD in 2023).
| Feature | Notes |
|---|---|
| Workforce | Default for enterprises in M365 |
| External Identities | B2B (guest users), B2C (customer-facing app) |
| Conditional Access | Risk-based policies, MFA enforcement |
| Verified ID | Decentralized credentials |
Microsoft.Identity.Web | First-class .NET integration |
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
Strengths: deep Microsoft integration, Conditional Access, enterprise SSO. Weaknesses: B2C is creaky and expensive; complex tenancy model.
Auth0 (Okta)
Cloud IdP focused on developer experience.
builder.Services
.AddAuthentication(o => { o.DefaultScheme = "Cookies"; o.DefaultChallengeScheme = "Auth0"; })
.AddCookie()
.AddOpenIdConnect("Auth0", o =>
{
o.Authority = $"https://{domain}/";
o.ClientId = clientId;
o.ClientSecret = secret;
o.ResponseType = "code";
o.UsePkce = true;
});
Strengths: best DX, social logins out of the box, robust SDKs, Actions/Rules for custom claim logic. Weaknesses: pricing scales fast (per MAU); Okta acquisition added enterprise focus.
Okta (Workforce)
Auth0's parent. Workforce IdP, dominant in US enterprises. Similar OIDC integration.
AWS Cognito
AWS-native IdP. Decent if you're all-in on AWS. Cheaper than Auth0 at scale. Less polish in customizations.
Duende IdentityServer
The successor to IdentityServer4. Self-hosted, .NET-native, OAuth2/OIDC server.
builder.Services.AddIdentityServer()
.AddInMemoryClients(Config.Clients)
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddAspNetIdentity<AppUser>()
.AddDeveloperSigningCredential();
Important: Commercial license required for production since v6 (paid for >1M tokens/year, or for commercial use). Free for non-commercial / dev / very small.
Strengths: full control, on-prem, .NET-native, mature, customizable. Weaknesses: licensing cost; you operate it.
OpenIddict
Free, MIT-licensed alternative to Duende.
builder.Services.AddOpenIddict()
.AddCore(o => o.UseEntityFrameworkCore().UseDbContext<AppDb>())
.AddServer(o =>
{
o.SetTokenEndpointUris("/connect/token");
o.SetAuthorizationEndpointUris("/connect/authorize");
o.AllowAuthorizationCodeFlow().RequireProofKeyForCodeExchange();
o.AllowClientCredentialsFlow();
o.RegisterScopes("api");
o.AddDevelopmentEncryptionCertificate().AddDevelopmentSigningCertificate();
})
.AddValidation(o => { o.UseLocalServer(); o.UseAspNetCore(); });
Strengths: free, well-maintained, similar API surface to Duende. Weaknesses: smaller community than Duende; less commercial support.
Keycloak
Free, Apache 2.0, Red Hat-maintained. Java-based. Cross-stack — works for non-.NET clients too.
Strengths: feature-rich (admin UI, realms, federation, MFA, social login). Weaknesses: Java operational cost; not .NET-native.
ASP.NET Core Identity
NOT an IdP. It's a user store + sign-in helpers + 2FA. To act as an OIDC server, pair with Duende or OpenIddict.
builder.Services.AddIdentityCore<AppUser>()
.AddEntityFrameworkStores<AppDb>();
// + Duende or OpenIddict for OIDC endpoints
Or: use Identity for self-hosted user-facing login pages; emit cookies. No external clients.
Decision matrix
| Need | Recommendation |
|---|---|
| Microsoft shop, M365 | Entra ID |
| Best DX, social logins | Auth0 |
| US enterprise SSO | Okta |
| AWS-native | Cognito |
| Self-hosted, regulated, .NET | Duende (paid) |
| Self-hosted, free, .NET | OpenIddict |
| Self-hosted, cross-stack, free | Keycloak |
| User store + login UI only | ASP.NET Core Identity |
B2B vs B2C
- B2B (workforce): Entra workforce, Okta, Duende. SSO, Conditional Access, federation.
- B2C (customers): Entra External ID (formerly B2C), Auth0 Universal Login, Cognito. Sign-up flows, social logins, custom branding.
Multi-tenant SaaS considerations
- Per-tenant IdP (each customer brings their own IdP via OIDC/SAML federation) — the most flexible enterprise pattern.
- Single IdP, multi-tenant — your IdP holds users tagged by tenant. Simpler.
- Auth0 Organizations and Entra External ID multi-tenant apps both support this.
Migration risks
Switching IdPs is hard:
- User identifiers (
subclaim) differ — re-link user records. - Federated identities (Google/MS personal) need re-consent.
- Refresh tokens / sessions invalidated.
- Custom claim logic (Auth0 Actions, Entra claims mappings) doesn't translate.
Plan for "dual issuer" period during migration — accept tokens from both old and new IdPs.
MFA & passwordless
Modern IdPs all support: - TOTP (Google Authenticator, Authy) - WebAuthn / FIDO2 / passkeys - SMS (deprecated for high-security) - Push notifications
For new apps, push for passkeys — phishing-resistant, no secret to steal.
Code: correct vs wrong
❌ Wrong: rolling your own OAuth server
Don't. Use Duende / OpenIddict / hosted IdP.
✅ Correct: configure trusted IdP
❌ Wrong: ASP.NET Core Identity for SSO across multiple apps
✅ Correct: front Identity with Duende/OpenIddict
builder.Services.AddIdentityCore<AppUser>().AddEntityFrameworkStores<AppDb>();
builder.Services.AddOpenIddict().AddCore(...).AddServer(...);
Design patterns for this topic
Pattern 1 — "Hosted IdP by default; self-hosted only when required"
- Intent: lower operational burden.
Pattern 2 — "ASP.NET Identity + Duende/OpenIddict"
- Intent: self-hosted with full OIDC server.
Pattern 3 — "Federation for B2B"
- Intent: customer's IdP federates into yours; no user duplication.
Pattern 4 — "Multi-tenant via per-tenant authority"
- Intent: tenant ID in the route → authority resolves dynamically.
Pattern 5 — "Dual issuer during migration"
- Intent: accept tokens from old and new IdP for transition window.
Pros & cons / trade-offs
| IdP | Pros | Cons |
|---|---|---|
| Entra ID | Microsoft integration; CA | Complex tenancy |
| Auth0 | Best DX | Pricing |
| Okta | Enterprise SSO | Enterprise pricing |
| Cognito | AWS-native | Less customizable |
| Duende | .NET-native | License cost |
| OpenIddict | Free; .NET | Smaller community |
| Keycloak | Free; cross-stack | Java ops |
| Identity (alone) | Self-host user store | Not an IdP |
When to use / when to avoid
- Use hosted IdP unless compliance/data-residency forces self-hosting.
- Use Duende/OpenIddict when self-hosting on .NET.
- Avoid rolling your own OAuth — full of footguns.
- Avoid Identity-only if you need SSO across apps.
Interview Q&A
Q1. Is ASP.NET Core Identity an IdP? No — it's a user store and sign-in helper. Pair with Duende or OpenIddict to act as an OAuth2/OIDC server.
Q2. When choose Duende over Auth0? On-prem requirement, regulated data, full customization, or massive scale where Auth0's per-MAU pricing dominates Duende's flat fee.
Q3. What's Entra External ID? Microsoft's customer-identity offering (replaces B2C). Sign-up, social logins, custom branding for customer-facing apps.
Q4. Why prefer hosted IdP for most apps? Security maturity, MFA, attack-detection, compliance certifications. Hard to match self-hosted.
Q5. What's federation? Trusting an external IdP (customer's, partner's). Your IdP issues its own token but trusts the external one for identity assertion.
Q6. How do you migrate IdPs? Dual-issue period; accept both old and new tokens; re-link user records by email or external ID; force re-consent at cutover.
Q7. When use Keycloak in a .NET shop? Cross-stack environments (mix of Java/Node/.NET) where one IdP serves all. Or if licensing rules out Duende and you don't want OpenIddict.
Q8. What's a confidential vs public client? Confidential = can hold a secret (server-side web app, daemon). Public = can't (SPA, mobile). PKCE makes public clients viable.
Q9. Why are passkeys important? WebAuthn/FIDO2 phishing-resistant credentials bound to origin. Replace passwords entirely. Senior teams should be planning rollouts.
Q10. Multi-tenant: shared vs per-tenant authority? Shared: one IdP, tenant claim per user. Per-tenant: dynamic authority (https://idp/{tenant}/v2.0). Per-tenant scales to enterprise federation.
Gotchas / common mistakes
- ⚠️ Mixing two self-hosted IdPs — duplicate users; no SSO.
- ⚠️ Hosting IdP on the same domain as the app — CSRF/SameSite weirdness.
- ⚠️ Logging tokens in console output — accidentally leaking credentials.
- ⚠️ Default Auth0 / Cognito quotas — production traffic blows past them.
- ⚠️ Hard-coding
client_secretin repo — use Key Vault / managed identity. - ⚠️ Not planning IdP migration — coupling assumptions to vendor.