This document provides practical, copy-pastable code examples for common scenarios when using the Auth0 ASP.NET Core API Authentication SDK.
- Getting Started
- Configuration
- DPoP (Demonstration of Proof-of-Possession)
- Multiple Custom Domains
- Authorization
- Advanced Scenarios
- Integration Examples
Basic setup for Auth0 JWT authentication in a minimal API.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
// Add Auth0 JWT authentication
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"]
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/", () => "Hello, World!");
app.Run();Protect endpoints using RequireAuthorization() in minimal APIs.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"]
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
// Public endpoint - no authentication required
app.MapGet("/api/public", () => Results.Ok(new { message = "This is a public endpoint" }));
// Protected endpoint - requires authentication
app.MapGet("/api/protected", () => Results.Ok(new { message = "You are authenticated!" }))
.RequireAuthorization();
// Protected endpoint with user information
app.MapGet("/api/user", (HttpContext context) =>
{
var userId = context.User.FindFirst("sub")?.Value;
var email = context.User.FindFirst("email")?.Value;
return Results.Ok(new { userId, email });
})
.RequireAuthorization();
app.Run();Protect endpoints using [Authorize] attribute in controllers.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"]
};
});
builder.Services.AddAuthorization();
builder.Services.AddControllers();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
[ApiController]
[Route("api/[controller]")]
public class DataController : ControllerBase
{
// Public endpoint
[HttpGet("public")]
public IActionResult GetPublic()
{
return Ok(new { message = "This is a public endpoint" });
}
// Protected endpoint
[Authorize]
[HttpGet("protected")]
public IActionResult GetProtected()
{
var userId = User.FindFirst("sub")?.Value;
return Ok(new { message = "You are authenticated!", userId });
}
// Protected POST endpoint
[Authorize]
[HttpPost]
public IActionResult CreateData([FromBody] DataModel data)
{
return CreatedAtAction(nameof(GetProtected), new { id = data.Id }, data);
}
}
public record DataModel(int Id, string Name);Customize JWT token validation with specific parameters.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"],
RequireHttpsMetadata = true,
SaveToken = true,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(5),
NameClaimType = "name",
RoleClaimType = "https://schemas.auth0.com/roles"
}
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/api/protected", () => Results.Ok(new { message = "Token validated with custom parameters" }))
.RequireAuthorization();
app.Run();DPoP is a security mechanism that binds access tokens to cryptographic keys, preventing token theft and replay attacks. This SDK provides seamless DPoP integration with flexible enforcement modes.
Enable DPoP with a single method call - accepts both DPoP and Bearer tokens.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"]
};
}).WithDPoP(); // ✨ Enable DPoP with default settings (Allowed mode)
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
// This endpoint works with both:
// 1. DPoP tokens (Authorization: DPoP <token> + DPoP: <proof>)
// 2. Bearer tokens (Authorization: Bearer <token>)
app.MapGet("/api/data", () => Results.Ok(new { message = "Supports both DPoP and Bearer tokens" }))
.RequireAuthorization();
app.Run();What this does:
- Enables DPoP validation in Allowed mode (default)
- Accepts DPoP-bound tokens with proof validation
- Still accepts regular Bearer tokens for backward compatibility
- Uses default time validation settings (300s iat offset, 30s leeway)
Use Allowed mode to gradually adopt DPoP without breaking existing clients using Bearer tokens.
using Auth0.AspNetCore.Authentication.Api;
using Auth0.AspNetCore.Authentication.Api.DPoP;
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"]
};
}).WithDPoP(dpopOptions =>
{
// Explicitly set to Allowed mode (this is the default)
dpopOptions.Mode = DPoPModes.Allowed;
// Customize time validation
dpopOptions.IatOffset = 300; // Allow DPoP proof tokens up to 5 minutes old
dpopOptions.Leeway = 30; // 30 seconds clock skew tolerance
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/api/legacy", () =>
Results.Ok(new { message = "Works with Bearer tokens from legacy clients" }))
.RequireAuthorization();
app.MapGet("/api/modern", () =>
Results.Ok(new { message = "Works with DPoP tokens from modern clients" }))
.RequireAuthorization();
app.Run();Use this when:
- You're migrating from Bearer tokens to DPoP
- You have mixed clients (some support DPoP, some don't)
- You want to test DPoP without forcing all clients to upgrade
- You need a gradual rollout strategy
Security note: Allowed mode provides backward compatibility but doesn't enforce the full security benefits of DPoP for Bearer tokens.
Use Required mode when you want maximum security - only DPoP tokens are accepted.
using Auth0.AspNetCore.Authentication.Api;
using Auth0.AspNetCore.Authentication.Api.DPoP;
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"]
};
}).WithDPoP(dpopOptions =>
{
// Only accept DPoP tokens, reject Bearer tokens
dpopOptions.Mode = DPoPModes.Required;
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
// This endpoint ONLY accepts DPoP tokens
// Bearer tokens will be rejected with 401 Unauthorized
app.MapGet("/api/high-security", () =>
Results.Ok(new { message = "DPoP token verified successfully" }))
.RequireAuthorization();
app.Run();Multiple Custom Domains (MCD) lets you accept tokens from multiple Auth0 custom domains while keeping a single SDK instance. This is useful when one application serves multiple custom domains, each mapped to a different Auth0 custom domain.
MCD is intended for the custom domains of a single Auth0 tenant. It is not a supported way to connect multiple Auth0 tenants to one application.
Configure a static list of allowed Auth0 custom domains when they are known at application startup.
using Auth0.AspNetCore.Authentication.Api;
using Auth0.AspNetCore.Authentication.Api.CustomDomains;
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"]
};
})
.WithCustomDomains(options =>
{
// Static list of allowed Auth0 custom domains
options.Domains = new[]
{
"brand-1.custom-domain.com",
"brand-2.custom-domain.com",
"brand-3.custom-domain.com"
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
// This endpoint accepts tokens from any of the configured domains
app.MapGet("/api/data", () =>
Results.Ok(new { message = "Authenticated from any allowed domain" }))
.RequireAuthorization();
app.Run();What this does:
- Accepts JWT tokens issued by any domain in the
Domainslist - Automatically validates token issuer against the allowed list before any network calls
- Rejects tokens from unauthorized domains with 401 Unauthorized
- Uses in-memory cache for OIDC configurations (100 entries, 10-minute sliding expiration by default)
Use this when:
- You have a fixed set of Auth0 custom domains known at application startup
- You need simple, straightforward multi-domain configuration
Resolve allowed domains dynamically at runtime based on request context, database lookups, or external APIs.
using Auth0.AspNetCore.Authentication.Api;
using Auth0.AspNetCore.Authentication.Api.CustomDomains;
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
// Register a tenant service for domain resolution
builder.Services.AddSingleton<ITenantService, TenantService>();
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"]
};
})
.WithCustomDomains(options =>
{
// Dynamic domain resolution using request context
options.DomainsResolver = async (httpContext, cancellationToken) =>
{
// Example: resolve from a request header
var tenantId = httpContext.Request.Headers["X-Tenant-Id"].FirstOrDefault();
// Example: resolve from a database or service
var tenantService = httpContext.RequestServices.GetRequiredService<ITenantService>();
var domains = await tenantService.GetAllowedDomainsAsync(tenantId, cancellationToken);
return domains;
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/api/tenant-data", () =>
Results.Ok(new { message = "Domain resolved dynamically" }))
.RequireAuthorization();
app.Run();What this does:
- Resolves allowed domains dynamically for each request
- Provides full
HttpContextaccess for custom resolution logic - Supports database queries, external API calls, or complex business logic
Use this when:
- Domains are not known at startup (e.g., customer onboarding creates new tenants)
- You need request-specific domain resolution (headers, query params, claims)
- Tenant configuration is stored in a database or external service
- Building a white-label SaaS with customer-specific Auth0 custom domains
Important: DomainsResolver and Domains are mutually exclusive — configure only one.
You can control how OpenID Connect configuration managers are cached per domain with ConfigurationManagerCache.
By default, the SDK uses an in-memory cache with:
maxSize: 100entries- No expiration (entries remain until evicted by size pressure)
The cache is keyed by the OIDC metadata endpoint URL (e.g., https://brand-1.custom-domain.com/.well-known/openid-configuration). Each distinct domain occupies one cache entry.
using Auth0.AspNetCore.Authentication.Api;
using Auth0.AspNetCore.Authentication.Api.CustomDomains;
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"]
};
})
.WithCustomDomains(options =>
{
options.Domains = new[]
{
"brand-1.custom-domain.com",
"brand-2.custom-domain.com"
};
// Customize cache settings
options.ConfigurationManagerCache = new MemoryConfigurationManagerCache(
maxSize: 50, // Maximum number of cached configurations
slidingExpiration: TimeSpan.FromHours(1) // Evict entries not accessed within 1 hour
);
// Customize OIDC refresh intervals
options.AutomaticRefreshInterval = TimeSpan.FromHours(24);
options.RefreshInterval = TimeSpan.FromMinutes(10);
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/api/data", () =>
Results.Ok(new { message = "Using custom cache configuration" }))
.RequireAuthorization();
app.Run();Disables caching entirely — a new configuration manager is created on every request (not recommended for production):
.WithCustomDomains(options =>
{
options.Domains = new[] { "brand-1.custom-domain.com" };
// Disable caching entirely (fetch OIDC config on every request)
options.ConfigurationManagerCache = new NullConfigurationManagerCache();
});Implement IConfigurationManagerCache for custom caching strategies (e.g., a distributed cache):
using Auth0.AspNetCore.Authentication.Api.CustomDomains;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
public class MyCustomConfigurationManagerCache : IConfigurationManagerCache
{
public IConfigurationManager<OpenIdConnectConfiguration> GetOrCreate(
string metadataAddress,
Func<string, IConfigurationManager<OpenIdConnectConfiguration>> factory)
{
// Return a cached instance or call factory(metadataAddress) to create one
throw new NotImplementedException();
}
public void Clear() { /* Evict all entries */ }
public void Dispose() { /* Clean up resources */ }
}
// Usage
.WithCustomDomains(options =>
{
options.Domains = new[] { "brand-1.custom-domain.com" };
options.ConfigurationManagerCache = new MyCustomConfigurationManagerCache();
});When configuring the DomainsResolver, you are responsible for ensuring that all resolved domains are trusted. Mis-configuring the domain resolver is a critical security risk that can lead to authentication bypass on the relying party (RP) or expose the application to Server-Side Request Forgery (SSRF).
Single tenant limitation:
The DomainsResolver is intended solely for multiple custom domains belonging to the same Auth0 tenant. It is not a supported mechanism for connecting multiple Auth0 tenants to a single application.
Secure proxy requirement:
When using MCD, your application must be deployed behind a secure edge or reverse proxy (e.g., Cloudflare, Nginx, or AWS ALB). The proxy must be configured to sanitize and overwrite Host and X-Forwarded-Host headers before they reach your application.
Without a trusted proxy layer to validate these headers, an attacker can manipulate the domain resolution process. This can result in authentication bypass or exposure to Server-Side Request Forgery (SSRF).
Validate scopes from Auth0 access tokens using authorization policies.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"]
};
});
// Define scope-based authorization policies
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("read:data", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c =>
c.Type == "scope" &&
c.Value.Split(' ').Contains("read:data"))));
options.AddPolicy("write:data", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c =>
c.Type == "scope" &&
c.Value.Split(' ').Contains("write:data"))));
options.AddPolicy("delete:data", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c =>
c.Type == "scope" &&
c.Value.Split(' ').Contains("delete:data"))));
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
// Requires 'read:data' scope
[Authorize(Policy = "read:data")]
[HttpGet]
public IActionResult GetProducts()
{
var products = new[] { "Product 1", "Product 2", "Product 3" };
return Ok(products);
}
// Requires 'write:data' scope
[Authorize(Policy = "write:data")]
[HttpPost]
public IActionResult CreateProduct([FromBody] ProductModel product)
{
return CreatedAtAction(nameof(GetProducts), new { id = product.Id }, product);
}
// Requires 'delete:data' scope
[Authorize(Policy = "delete:data")]
[HttpDelete("{id}")]
public IActionResult DeleteProduct(int id)
{
return NoContent();
}
}
public record ProductModel(int Id, string Name);Validate Auth0 permissions using custom authorization policies.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"]
};
});
// Define permission-based authorization policies
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("CanReadUsers", policy =>
policy.RequireClaim("permissions", "read:users"));
options.AddPolicy("CanCreateUsers", policy =>
policy.RequireClaim("permissions", "create:users"));
options.AddPolicy("CanDeleteUsers", policy =>
policy.RequireClaim("permissions", "delete:users"));
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
[Authorize(Policy = "CanReadUsers")]
[HttpGet]
public IActionResult GetUsers()
{
var users = new[] { "User 1", "User 2", "User 3" };
return Ok(users);
}
[Authorize(Policy = "CanCreateUsers")]
[HttpPost]
public IActionResult CreateUser([FromBody] UserModel user)
{
return CreatedAtAction(nameof(GetUsers), new { id = user.Id }, user);
}
[Authorize(Policy = "CanDeleteUsers")]
[HttpDelete("{id}")]
public IActionResult DeleteUser(string id)
{
return NoContent();
}
}
public record UserModel(string Id, string Name, string Email);Create a reusable authorization handler for scope validation.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"]
};
});
// Register custom authorization handler
builder.Services.AddSingleton<IAuthorizationHandler, HasScopeHandler>();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("read:products", policy =>
policy.Requirements.Add(new HasScopeRequirement("read:products")));
options.AddPolicy("write:products", policy =>
policy.Requirements.Add(new HasScopeRequirement("write:products")));
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
// Custom authorization requirement
public class HasScopeRequirement : IAuthorizationRequirement
{
public string Scope { get; }
public HasScopeRequirement(string scope)
{
Scope = scope ?? throw new ArgumentNullException(nameof(scope));
}
}
// Custom authorization handler
public class HasScopeHandler : AuthorizationHandler<HasScopeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
HasScopeRequirement requirement)
{
var scopeClaim = context.User.FindFirst(c => c.Type == "scope");
if (scopeClaim != null)
{
var scopes = scopeClaim.Value.Split(' ');
if (scopes.Contains(requirement.Scope))
{
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}
[ApiController]
[Route("api/[controller]")]
public class InventoryController : ControllerBase
{
[Authorize(Policy = "read:products")]
[HttpGet]
public IActionResult GetInventory()
{
return Ok(new[] { "Item 1", "Item 2" });
}
[Authorize(Policy = "write:products")]
[HttpPost]
public IActionResult AddInventory([FromBody] InventoryItem item)
{
return Created($"/api/inventory/{item.Id}", item);
}
}
public record InventoryItem(int Id, string Name, int Quantity);Validate Auth0 roles using authorization policies.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"],
TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = "https://schemas.auth0.com/roles"
}
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
options.AddPolicy("ManagerOrAdmin", policy => policy.RequireRole("Manager", "Admin"));
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
[ApiController]
[Route("api/[controller]")]
public class AdminController : ControllerBase
{
[Authorize(Policy = "AdminOnly")]
[HttpGet("settings")]
public IActionResult GetSettings()
{
return Ok(new { setting = "Admin settings" });
}
[Authorize(Policy = "ManagerOrAdmin")]
[HttpGet("reports")]
public IActionResult GetReports()
{
var userRoles = User.FindAll("https://schemas.auth0.com/roles")
.Select(c => c.Value);
return Ok(new { message = "Reports data", roles = userRoles });
}
}Access and use user claims from Auth0 tokens.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"]
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
// Access user claims in minimal API
app.MapGet("/api/profile", (HttpContext context) =>
{
var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value
?? context.User.FindFirst("sub")?.Value;
return Results.Ok(new { userId });
})
.RequireAuthorization();
// Access all claims
app.MapGet("/api/claims", (ClaimsPrincipal user) =>
{
var claims = user.Claims.Select(c => new { c.Type, c.Value });
return Results.Ok(claims);
})
.RequireAuthorization();
app.Run();Implement custom logic during JWT authentication events.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"],
Events = new JwtBearerEvents
{
OnTokenValidated = context =>
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILogger<Program>>();
var userId = context.Principal?.FindFirst("sub")?.Value;
logger.LogInformation("Token validated for user: {UserId}", userId);
// Add custom claims
var identity = context.Principal?.Identity as ClaimsIdentity;
identity?.AddClaim(new Claim("validated_at", DateTime.UtcNow.ToString()));
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILogger<Program>>();
logger.LogError(context.Exception, "Authentication failed");
if (context.Exception is SecurityTokenExpiredException)
{
context.Response.Headers.Append("Token-Expired", "true");
}
return Task.CompletedTask;
},
OnMessageReceived = context =>
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILogger<Program>>();
var hasToken = !string.IsNullOrEmpty(context.Token);
logger.LogDebug("Token received: {HasToken}", hasToken);
return Task.CompletedTask;
},
OnChallenge = context =>
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILogger<Program>>();
logger.LogWarning("Authentication challenge issued: {Error}", context.Error);
return Task.CompletedTask;
}
}
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/api/protected", (ClaimsPrincipal user) =>
{
var validatedAt = user.FindFirst("validated_at")?.Value;
return Results.Ok(new { message = "Authenticated", validatedAt });
})
.RequireAuthorization();
app.Run();Extract JWT tokens from query string for scenarios like SignalR.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"],
Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// Check for token in query string
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
// Allow token from query string for specific paths (e.g., SignalR hubs)
if (!string.IsNullOrEmpty(accessToken) &&
(path.StartsWithSegments("/hubs") || path.StartsWithSegments("/api/stream")))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
}
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
// Standard endpoint - expects token in Authorization header
app.MapGet("/api/data", () => Results.Ok(new { data = "Standard endpoint" }))
.RequireAuthorization();
// Streaming endpoint - can accept token from query string
app.MapGet("/api/stream", (HttpContext context) =>
{
var userId = context.User.FindFirst("sub")?.Value;
return Results.Ok(new { message = "Streaming data", userId });
})
.RequireAuthorization();
app.Run();Customize error responses for authentication failures.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Text.Json;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"],
Events = new JwtBearerEvents
{
OnChallenge = context =>
{
// Skip the default behavior
context.HandleResponse();
// Create custom error response
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
context.Response.ContentType = "application/json";
var errorResponse = new
{
error = "unauthorized",
message = "Authentication is required to access this resource",
timestamp = DateTime.UtcNow
};
return context.Response.WriteAsync(
JsonSerializer.Serialize(errorResponse));
},
OnAuthenticationFailed = context =>
{
if (context.Exception != null)
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILogger<Program>>();
logger.LogError(context.Exception,
"Authentication failed: {Message}", context.Exception.Message);
}
return Task.CompletedTask;
}
}
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/api/protected", () => Results.Ok(new { data = "Protected resource" }))
.RequireAuthorization();
app.Run();Integrate Auth0 authentication with SignalR hubs.
using Auth0.AspNetCore.Authentication.Api;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
using System.Security.Claims;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuth0ApiAuthentication(options =>
{
options.Domain = builder.Configuration["Auth0:Domain"];
options.JwtBearerOptions = new JwtBearerOptions
{
Audience = builder.Configuration["Auth0:Audience"],
Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// Extract token from query string for SignalR
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
path.StartsWithSegments("/hubs"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
}
};
});
builder.Services.AddAuthorization();
builder.Services.AddSignalR();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapHub<ChatHub>("/hubs/chat");
app.Run();
// SignalR Hub with authentication
[Authorize]
public class ChatHub : Hub
{
private readonly ILogger<ChatHub> _logger;
public ChatHub(ILogger<ChatHub> logger)
{
_logger = logger;
}
public override async Task OnConnectedAsync()
{
var userId = Context.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value
?? Context.User?.FindFirst("sub")?.Value;
var connectionId = Context.ConnectionId;
_logger.LogInformation("User {UserId} connected with ID {ConnectionId}",
userId, connectionId);
await Clients.All.SendAsync("UserConnected", userId);
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
var userId = Context.User?.FindFirst("sub")?.Value;
_logger.LogInformation("User {UserId} disconnected", userId);
await Clients.All.SendAsync("UserDisconnected", userId);
await base.OnDisconnectedAsync(exception);
}
public async Task SendMessage(string message)
{
var userId = Context.User?.FindFirst("sub")?.Value;
var userName = Context.User?.FindFirst("name")?.Value ?? "Anonymous";
await Clients.All.SendAsync("ReceiveMessage", userName, message);
}
// Only users with specific scope can broadcast
[Authorize(Policy = "write:messages")]
public async Task BroadcastMessage(string message)
{
var userName = Context.User?.FindFirst("name")?.Value ?? "System";
await Clients.All.SendAsync("ReceiveMessage", userName, message);
}
}To test these examples, you'll need an access token from Auth0. Here's how to get one:
curl --request POST \
--url https://your-tenant.auth0.com/oauth/token \
--header 'content-type: application/json' \
--data '{
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"audience": "https://your-api-identifier",
"grant_type": "client_credentials"
}'# Replace YOUR_ACCESS_TOKEN with the token from above
curl --request GET \
--url https://localhost:5000/api/protected \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'- Auth0 Documentation
- ASP.NET Core Authentication Documentation
- JWT Bearer Authentication
- Auth0 Community
If you have questions or need help with these examples:
- 📖 Check the main README for overview and setup instructions
- 💬 Visit the Auth0 Community
- 🐛 Report issues on GitHub Issues