Auth0.NET
Authentication Library
Auth0.NET
Overview
Auth0.NET is Auth0's official .NET SDK for server applications, established as the standard choice for authentication implementation in ASP.NET Core, .NET Framework, and Blazor as of 2025. The latest v9 series provides comprehensive support for OAuth 2.0, OpenID Connect, and SAML 2.0, offering multi-tenant capabilities, rich authentication provider integrations (Azure AD, Google, Facebook, etc.), and enterprise Identity Provider (IdP) connectivity. With ASP.NET Core Identity integration, JWT validation and claims management, Role-Based Access Control (RBAC), multi-factor authentication (MFA), audit logging, and security policy enforcement, Auth0.NET enables comprehensive implementation of enterprise-level web application and API authentication and authorization.
Details
Auth0.NET v9 is a high-performance authentication SDK leveraging the latest .NET 8/9 features. The architecture consists of four main components: authentication client, Management API, user management, and role/permission management, enabling scalable server-side authentication. Deep integration with ASP.NET Core authentication middleware, custom claims transformation, conditional access, API protection, and Blazor Server/WebAssembly support optimize it for modern .NET application development. The Management API enables programmatic user management, organizational hierarchy, rule/hook execution, and tenant management.
Key Features
- ASP.NET Core Integration: Deep integration with authentication middleware and DI support
- Management API: Programmatic user and tenant management capabilities
- Multi-tenant: Hierarchical tenant management for enterprise organizations
- SAML/OIDC/OAuth: Comprehensive enterprise Identity Provider connectivity
- API Protection: JWT validation and scope-based access control
- Blazor Support: Authentication implementation for Server/WebAssembly
Advantages and Disadvantages
Advantages
- Highest compatibility within the .NET ecosystem as Auth0's official SDK
- Enhanced development efficiency through complete integration with ASP.NET Core authentication pipeline
- Advanced user and tenant management capabilities through Management API
- Comprehensive Identity Provider support for enterprise SAML/OIDC integrations
- High performance and security leveraging latest .NET 8/9 features
- Excellent compatibility with Microsoft ecosystem
Disadvantages
- Limited to .NET platform, unsuitable for cross-platform development
- Vendor lock-in risk due to Auth0 service dependency
- Increasing costs based on user count and API calls in commercial usage
- Learning cost for advanced customization in complex enterprise requirements
- Constraints in on-premise environments and external service dependency
- Difficulty identifying Auth0 service-side issues during debugging
Reference Pages
Usage Examples
Package Installation and Project Setup
# Install NuGet packages
dotnet add package Auth0.NET
dotnet add package Auth0.AspNetCore.Authentication
dotnet add package Auth0.ManagementApi
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect
# Create project (MVC application)
dotnet new mvc -n Auth0WebApp
cd Auth0WebApp
ASP.NET Core Authentication Configuration (Program.cs)
// Program.cs - Auth0 ASP.NET Core Integration
using Auth0.AspNetCore.Authentication;
using Auth0.ManagementApi;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
var builder = WebApplication.CreateBuilder(args);
// Get Auth0 configuration
var auth0Config = builder.Configuration.GetSection("Auth0");
var domain = auth0Config["Domain"];
var clientId = auth0Config["ClientId"];
var clientSecret = auth0Config["ClientSecret"];
var audience = auth0Config["Audience"];
// Add Auth0 authentication services
builder.Services
.AddAuth0WebAppAuthentication(options =>
{
options.Domain = domain;
options.ClientId = clientId;
options.ClientSecret = clientSecret;
options.Scope = "openid profile email";
// Custom claims transformation
options.OpenIdConnectEvents = new OpenIdConnectEvents
{
OnTokenValidated = async context =>
{
var claimsIdentity = context.Principal?.Identity as ClaimsIdentity;
if (claimsIdentity != null)
{
// Add custom claims
var userIdClaim = claimsIdentity.FindFirst("sub");
if (userIdClaim != null)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userIdClaim.Value));
}
// Get and set role information
var rolesClaim = claimsIdentity.FindFirst("https://schemas.auth0.com/roles");
if (rolesClaim != null)
{
var roles = System.Text.Json.JsonSerializer.Deserialize<string[]>(rolesClaim.Value);
foreach (var role in roles)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role));
}
}
}
}
};
});
// JWT Bearer authentication (for APIs)
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = $"https://{domain}/";
options.Audience = audience;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.Zero
};
});
// Configure authorization policies
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdminRole", policy =>
policy.RequireRole("Admin"));
options.AddPolicy("RequireManagerRole", policy =>
policy.RequireRole("Manager", "Admin"));
options.AddPolicy("RequireApiAccess", policy =>
policy.RequireAuthenticatedUser()
.RequireClaim("scope", "read:api"));
});
// Auth0 Management API client
builder.Services.AddScoped<IManagementApiClient>(provider =>
{
var httpClient = new HttpClient();
var managementApiClient = new ManagementApiClient(
token: GetManagementApiToken(), // Implementation required
baseUri: new Uri($"https://{domain}/api/v2/"),
httpClient: httpClient
);
return managementApiClient;
});
// Additional services
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IRoleService, RoleService>();
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Configure middleware pipeline
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
// Authentication and authorization middleware
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
// Management API token retrieval helper
static string GetManagementApiToken()
{
// Implementation: Get token via Machine-to-Machine authentication
// Simplified for this example
return "your-management-api-token";
}
appsettings.json Configuration
{
"Auth0": {
"Domain": "your-tenant.auth0.com",
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret",
"Audience": "https://your-api-identifier",
"ManagementApiClientId": "your-management-api-client-id",
"ManagementApiClientSecret": "your-management-api-client-secret"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
Authentication Controller Implementation
// Controllers/AuthController.cs - Authentication Controller
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
[Route("[controller]")]
public class AuthController : Controller
{
private readonly ILogger<AuthController> _logger;
public AuthController(ILogger<AuthController> logger)
{
_logger = logger;
}
[HttpGet("login")]
public async Task<IActionResult> Login(string returnUrl = "/")
{
var authenticationProperties = new AuthenticationProperties
{
RedirectUri = returnUrl
};
await HttpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, authenticationProperties);
return new EmptyResult();
}
[HttpGet("logout")]
[Authorize]
public async Task<IActionResult> Logout()
{
var authenticationProperties = new AuthenticationProperties
{
RedirectUri = Url.Action("Index", "Home")
};
await HttpContext.SignOutAsync(Auth0Constants.AuthenticationScheme, authenticationProperties);
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return new EmptyResult();
}
[HttpGet("profile")]
[Authorize]
public IActionResult Profile()
{
var userClaims = User.Claims.Select(c => new {
Type = c.Type,
Value = c.Value
}).ToList();
var profileModel = new UserProfileModel
{
UserId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value,
Name = User.FindFirst("name")?.Value,
Email = User.FindFirst(ClaimTypes.Email)?.Value,
Picture = User.FindFirst("picture")?.Value,
Roles = User.Claims
.Where(c => c.Type == ClaimTypes.Role)
.Select(c => c.Value)
.ToList(),
Claims = userClaims
};
return View(profileModel);
}
[HttpGet("access-denied")]
public IActionResult AccessDenied()
{
return View();
}
}
// Models/UserProfileModel.cs
public class UserProfileModel
{
public string? UserId { get; set; }
public string? Name { get; set; }
public string? Email { get; set; }
public string? Picture { get; set; }
public List<string> Roles { get; set; } = new();
public List<object> Claims { get; set; } = new();
}
User Management Service Using Management API
// Services/IUserService.cs - User Service Interface
using Auth0.ManagementApi.Models;
public interface IUserService
{
Task<User> GetUserByIdAsync(string userId);
Task<IPagedList<User>> GetUsersAsync(int page = 0, int perPage = 50);
Task<User> CreateUserAsync(UserCreateRequest request);
Task<User> UpdateUserAsync(string userId, UserUpdateRequest request);
Task DeleteUserAsync(string userId);
Task<IList<Role>> GetUserRolesAsync(string userId);
Task AssignRolesToUserAsync(string userId, AssignRolesRequest request);
Task RemoveRolesFromUserAsync(string userId, AssignRolesRequest request);
}
// Services/UserService.cs - User Service Implementation
using Auth0.ManagementApi;
using Auth0.ManagementApi.Models;
public class UserService : IUserService
{
private readonly IManagementApiClient _managementApiClient;
private readonly ILogger<UserService> _logger;
public UserService(IManagementApiClient managementApiClient, ILogger<UserService> logger)
{
_managementApiClient = managementApiClient;
_logger = logger;
}
public async Task<User> GetUserByIdAsync(string userId)
{
try
{
_logger.LogInformation("Retrieving user with ID: {UserId}", userId);
return await _managementApiClient.Users.GetAsync(userId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving user {UserId}", userId);
throw;
}
}
public async Task<IPagedList<User>> GetUsersAsync(int page = 0, int perPage = 50)
{
try
{
var getUsersRequest = new GetUsersRequest
{
Page = page,
PerPage = perPage,
IncludeTotals = true,
Sort = "created_at:-1"
};
_logger.LogInformation("Retrieving users page {Page}, per page {PerPage}", page, perPage);
return await _managementApiClient.Users.GetAllAsync(getUsersRequest);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving users");
throw;
}
}
public async Task<User> CreateUserAsync(UserCreateRequest request)
{
try
{
_logger.LogInformation("Creating new user with email: {Email}", request.Email);
return await _managementApiClient.Users.CreateAsync(request);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating user with email {Email}", request.Email);
throw;
}
}
public async Task<User> UpdateUserAsync(string userId, UserUpdateRequest request)
{
try
{
_logger.LogInformation("Updating user {UserId}", userId);
return await _managementApiClient.Users.UpdateAsync(userId, request);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error updating user {UserId}", userId);
throw;
}
}
public async Task DeleteUserAsync(string userId)
{
try
{
_logger.LogInformation("Deleting user {UserId}", userId);
await _managementApiClient.Users.DeleteAsync(userId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error deleting user {UserId}", userId);
throw;
}
}
public async Task<IList<Role>> GetUserRolesAsync(string userId)
{
try
{
_logger.LogInformation("Retrieving roles for user {UserId}", userId);
return await _managementApiClient.Users.GetRolesAsync(userId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving roles for user {UserId}", userId);
throw;
}
}
public async Task AssignRolesToUserAsync(string userId, AssignRolesRequest request)
{
try
{
_logger.LogInformation("Assigning roles to user {UserId}", userId);
await _managementApiClient.Users.AssignRolesAsync(userId, request);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error assigning roles to user {UserId}", userId);
throw;
}
}
public async Task RemoveRolesFromUserAsync(string userId, AssignRolesRequest request)
{
try
{
_logger.LogInformation("Removing roles from user {UserId}", userId);
await _managementApiClient.Users.RemoveRolesAsync(userId, request);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error removing roles from user {UserId}", userId);
throw;
}
}
}
JWT Authentication in API Controllers
// Controllers/ApiController.cs - API Authentication Controller
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
[ApiController]
[Route("api/[controller]")]
[Authorize] // Requires JWT Bearer authentication
public class UsersController : ControllerBase
{
private readonly IUserService _userService;
private readonly ILogger<UsersController> _logger;
public UsersController(IUserService userService, ILogger<UsersController> logger)
{
_userService = userService;
_logger = logger;
}
[HttpGet]
[Authorize(Policy = "RequireAdminRole")]
public async Task<ActionResult<IEnumerable<object>>> GetUsers(
[FromQuery] int page = 0,
[FromQuery] int perPage = 50)
{
try
{
var users = await _userService.GetUsersAsync(page, perPage);
var userList = users.Select(u => new
{
UserId = u.UserId,
Email = u.Email,
Name = u.Name,
Picture = u.Picture,
CreatedAt = u.CreatedAt,
LastLogin = u.LastLogin,
EmailVerified = u.EmailVerified
});
return Ok(new
{
Users = userList,
Total = users.Paging.Total,
Page = page,
PerPage = perPage
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving users");
return StatusCode(500, new { Error = "Internal server error" });
}
}
[HttpGet("{userId}")]
[Authorize(Policy = "RequireManagerRole")]
public async Task<ActionResult<object>> GetUser(string userId)
{
try
{
// Check if current user is accessing their own information or has admin privileges
var currentUserId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var isAdmin = User.IsInRole("Admin");
if (currentUserId != userId && !isAdmin)
{
return Forbid("You can only access your own user information");
}
var user = await _userService.GetUserByIdAsync(userId);
var userRoles = await _userService.GetUserRolesAsync(userId);
return Ok(new
{
UserId = user.UserId,
Email = user.Email,
Name = user.Name,
Picture = user.Picture,
CreatedAt = user.CreatedAt,
LastLogin = user.LastLogin,
EmailVerified = user.EmailVerified,
Roles = userRoles.Select(r => new { r.Id, r.Name, r.Description })
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving user {UserId}", userId);
return StatusCode(500, new { Error = "Internal server error" });
}
}
[HttpPost]
[Authorize(Policy = "RequireAdminRole")]
public async Task<ActionResult<object>> CreateUser([FromBody] CreateUserRequest request)
{
try
{
var userCreateRequest = new UserCreateRequest
{
Email = request.Email,
Name = request.Name,
Password = request.Password,
Connection = "Username-Password-Authentication",
EmailVerified = false
};
var createdUser = await _userService.CreateUserAsync(userCreateRequest);
return CreatedAtAction(
nameof(GetUser),
new { userId = createdUser.UserId },
new
{
UserId = createdUser.UserId,
Email = createdUser.Email,
Name = createdUser.Name,
CreatedAt = createdUser.CreatedAt
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error creating user");
return StatusCode(500, new { Error = "Internal server error" });
}
}
[HttpPut("{userId}/roles")]
[Authorize(Policy = "RequireAdminRole")]
public async Task<IActionResult> UpdateUserRoles(string userId, [FromBody] UpdateUserRolesRequest request)
{
try
{
var assignRolesRequest = new AssignRolesRequest
{
Roles = request.RoleIds
};
// Remove existing roles
var currentRoles = await _userService.GetUserRolesAsync(userId);
if (currentRoles.Any())
{
var removeRolesRequest = new AssignRolesRequest
{
Roles = currentRoles.Select(r => r.Id).ToArray()
};
await _userService.RemoveRolesFromUserAsync(userId, removeRolesRequest);
}
// Assign new roles
await _userService.AssignRolesToUserAsync(userId, assignRolesRequest);
return NoContent();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error updating user roles for {UserId}", userId);
return StatusCode(500, new { Error = "Internal server error" });
}
}
}
// Request DTOs
public class CreateUserRequest
{
public string Email { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}
public class UpdateUserRolesRequest
{
public string[] RoleIds { get; set; } = Array.Empty<string>();
}
Blazor Server Authentication Integration
// Components/LoginDisplay.razor - Blazor Authentication Display Component
@using Microsoft.AspNetCore.Components.Authorization
@using System.Security.Claims
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject NavigationManager Navigation
<AuthorizeView>
<Authorized>
<div class="user-info">
<img src="@GetUserPicture(context.User)" alt="Profile" class="profile-picture" />
<span>Hello, @GetUserName(context.User)!</span>
<a href="/Auth/Logout" class="btn btn-outline-secondary">Logout</a>
</div>
</Authorized>
<NotAuthorized>
<a href="/Auth/Login" class="btn btn-primary">Login</a>
</NotAuthorized>
</AuthorizeView>
@code {
private string GetUserName(ClaimsPrincipal user)
{
return user.FindFirst("name")?.Value ?? user.FindFirst(ClaimTypes.Name)?.Value ?? "User";
}
private string GetUserPicture(ClaimsPrincipal user)
{
return user.FindFirst("picture")?.Value ?? "/images/default-avatar.png";
}
}
// Components/Pages/UserManagement.razor - User Management Page
@page "/admin/users"
@using Auth0.ManagementApi.Models
@attribute [Authorize(Policy = "RequireAdminRole")]
@inject IUserService UserService
@inject IJSRuntime JSRuntime
<PageTitle>User Management</PageTitle>
<h3>User Management</h3>
@if (loading)
{
<div class="text-center">
<div class="spinner-border" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
}
else if (error != null)
{
<div class="alert alert-danger">
Error: @error
</div>
}
else
{
<div class="row mb-3">
<div class="col">
<button class="btn btn-primary" @onclick="LoadUsers">Refresh</button>
<button class="btn btn-success" @onclick="ShowCreateUserModal">Create New User</button>
</div>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>Picture</th>
<th>Name</th>
<th>Email</th>
<th>Created</th>
<th>Last Login</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach (var user in users)
{
<tr>
<td>
<img src="@(user.Picture ?? "/images/default-avatar.png")"
alt="Profile" class="rounded-circle" width="40" height="40" />
</td>
<td>@user.Name</td>
<td>@user.Email</td>
<td>@user.CreatedAt?.ToString("yyyy/MM/dd")</td>
<td>@user.LastLogin?.ToString("yyyy/MM/dd HH:mm")</td>
<td>
<button class="btn btn-sm btn-outline-primary"
@onclick="() => EditUser(user.UserId)">Edit</button>
<button class="btn btn-sm btn-outline-danger"
@onclick="() => DeleteUser(user.UserId)">Delete</button>
</td>
</tr>
}
</tbody>
</table>
@if (users.Count == 0)
{
<div class="text-center text-muted">
No users found.
</div>
}
}
@code {
private List<User> users = new();
private bool loading = true;
private string? error;
protected override async Task OnInitializedAsync()
{
await LoadUsers();
}
private async Task LoadUsers()
{
loading = true;
error = null;
try
{
var pagedUsers = await UserService.GetUsersAsync(0, 100);
users = pagedUsers.ToList();
}
catch (Exception ex)
{
error = ex.Message;
}
finally
{
loading = false;
}
}
private async Task EditUser(string userId)
{
// Implementation for showing edit modal
await JSRuntime.InvokeVoidAsync("alert", $"Edit functionality (User ID: {userId})");
}
private async Task DeleteUser(string userId)
{
var confirmed = await JSRuntime.InvokeAsync<bool>("confirm", "Are you sure you want to delete this user?");
if (confirmed)
{
try
{
await UserService.DeleteUserAsync(userId);
await LoadUsers(); // Reload the list
}
catch (Exception ex)
{
error = ex.Message;
}
}
}
private async Task ShowCreateUserModal()
{
// Implementation for showing create user modal
await JSRuntime.InvokeVoidAsync("alert", "Create new user functionality");
}
}
<style>
.profile-picture {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
.user-info {
display: flex;
align-items: center;
gap: 10px;
}
</style>