Auth0 Integration Guide
See also: Identity Validator Overview for package versions, OIDC/JWT options, and Node.js + .NET entrypoints.
Complete guide to integrating Auth0 authentication with your .NET API using Primus Identity Validator.
Prerequisites
- Auth0 account with an API configured
- .NET 6.0+ project
- Your Auth0 Domain and API Identifier
Step 1: Install Package
dotnet add package PrimusSaaS.Identity.Validator
Step 2: Get Your Auth0 Credentials
From the Auth0 Dashboard:
- Go to Applications -> APIs
- Select your API (or create one)
- Note down:
- Domain:
your-tenant.auth0.com - API Identifier (Audience):
https://your-api
- Domain:
Step 3: Configure appsettings.json
{
"PrimusIdentity": {
"RequireHttpsMetadata": true,
"ValidateLifetime": true,
"Issuers": [
{
"Name": "Auth0-Production",
"Type": "Auth0",
"Authority": "https://YOUR-TENANT.auth0.com/",
"Issuer": "https://YOUR-TENANT.auth0.com/",
"Audiences": ["https://your-api-identifier"]
}
],
"Diagnostics": {
"EnableDetailedErrors": true,
"IncludeTokenHintsInChallenges": true,
"IncludeDebugHeaders": true,
"LogTokenRejectionReasons": true,
"MaxRecentFailures": 50,
"AutoDetectDevelopment": true
}
}
}
Configuration Reference
| Property | Required | Description |
|---|---|---|
Name | Yes | Friendly name for logging |
Type | Yes | Must be "Auth0" |
Authority | Yes | Your Auth0 domain with trailing slash |
Issuer | Yes | Usually the same as Authority |
Audiences | Yes | Array of API Identifiers from Auth0 dashboard |
AllowMachineToMachine | No | Allow client credentials tokens |
RequireHttpsMetadata | No | Require HTTPS for metadata (default: true) |
Diagnostics | No | Dev-only diagnostics (no secrets) |
Step 4: Complete Program.cs
using PrimusSaaS.Identity.Validator;
using Microsoft.AspNetCore.Authorization;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddPrimusIdentity(opts =>
builder.Configuration.GetSection("PrimusIdentity").Bind(opts));
builder.Services.AddControllers();
var app = builder.Build();
// ========================================
// Middleware Pipeline (ORDER MATTERS!)
// ========================================
app.UseHttpsRedirection();
app.UseAuthentication(); // Must come before Authorization
app.UseAuthorization();
app.MapControllers();
app.Run();
Step 5: Create a Protected Controller
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class ProfileController : ControllerBase
{
[HttpGet("public")]
public IActionResult GetPublic() => Ok(new { message = "This is public" });
[Authorize]
[HttpGet("me")]
public IActionResult GetMe() => Ok(new
{
userId = User.FindFirst("sub")?.Value,
email = User.FindFirst("email")?.Value
});
}
Step 6: Get a Test Token from Auth0
Option A: Using Auth0 Dashboard
- Go to your API in Auth0 Dashboard
- Click the Test tab
- Copy the test token
Option B: Using curl
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"
}'
Option C: Using Auth0 Test Application
# Get token using Resource Owner Password flow (if enabled)
curl --request POST \
--url https://YOUR-TENANT.auth0.com/oauth/token \
--header 'content-type: application/json' \
--data '{
"client_id": "YOUR-TEST-APP-CLIENT-ID",
"username": "test@example.com",
"password": "testpassword",
"audience": "https://your-api-identifier",
"grant_type": "password",
"scope": "openid profile email"
}'
Step 7: Test Your API
# Start your API
dotnet run
# Test public endpoint
curl http://localhost:5000/api/profile/public
# Test protected endpoint (should return 401)
curl http://localhost:5000/api/profile/me
# Test with valid token
curl http://localhost:5000/api/profile/me \
-H "Authorization: Bearer YOUR-AUTH0-TOKEN"
Example API (Identity Validator Example .zip)
The example zip includes a minimal API with Auth0 validation.
GET /status
Headers: none
Response 200:
{
"packageInstalled": true,
"packageName": "PrimusSaaS.Identity.Validator",
"issuers": [
{ "name": "Auth0", "type": "Auth0", "configured": true, "details": "Configured" }
],
"message": "Identity Validator is installed and configured",
"nextSteps": "Test endpoints: /local (easiest), /auth0, /azuread"
}
GET /auth0
Headers: Authorization: Bearer <JWT>
Response 200:
{
"provider": "Auth0",
"userId": "auth0|abc123",
"email": "user@example.com",
"validated": true,
"message": "Auth0 token validated successfully"
}
Notes:
userIduses thesubclaim.emailusesemailwhen present; otherwise a namespaced claim likehttps://example.com/email.
Working Example: Full API with Auth0
using PrimusSaaS.Identity.Validator;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
// Configure Auth0
builder.Services.AddPrimusIdentity(opts =>
builder.Configuration.GetSection("PrimusIdentity").Bind(opts));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthentication();
app.UseAuthorization();
// Public endpoint
app.MapGet("/", () => new { status = "healthy", auth = "Auth0" });
// Protected endpoint
app.MapGet("/me", [Authorize] (HttpContext ctx) =>
{
var claims = ctx.User.Claims.Select(c => new { c.Type, c.Value });
return new {
authenticated = true,
claims
};
});
// Claims inspection endpoint
app.MapGet("/debug/claims", [Authorize] (HttpContext ctx) =>
{
return ctx.User.Claims.Select(c => new {
type = c.Type,
value = c.Value
});
});
app.Run();
appsettings.json
{
"PrimusIdentity": {
"Issuers": [
{
"Name": "Auth0",
"Type": "Auth0",
"Authority": "https://dev-example.auth0.com/",
"Issuer": "https://dev-example.auth0.com/",
"Audiences": ["https://my-api"]
}
],
"RequireHttpsMetadata": true,
"Diagnostics": {
"EnableDetailedErrors": true,
"IncludeTokenHintsInChallenges": true,
"IncludeDebugHeaders": true,
"LogTokenRejectionReasons": true,
"MaxRecentFailures": 50,
"AutoDetectDevelopment": true
}
},
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
}
Troubleshooting
Error: "Bearer error=invalid_token"
Cause: Token validation failed.
Solutions:
- Verify Authority URL has trailing slash:
https://tenant.auth0.com/ - Verify Audience matches exactly what's in Auth0 dashboard
- Check token hasn't expired
- Ensure token was issued for correct audience
Error: 401 with no message
Cause: Token not being sent or malformed.
Solutions:
# Verify token is being sent
curl -v http://localhost:5000/api/profile/private \
-H "Authorization: Bearer YOUR-TOKEN"
# Check Authorization header format (must be "Bearer " with space)
Error: "IDX10501: Signature validation failed"
Cause: Token signed with different key than expected.
Solutions:
- Ensure Authority URL is correct
- Check if using RS256 algorithm (Auth0 default)
- Verify token wasn't modified
Debug: Enable detailed errors (development only)
{
"PrimusIdentity": {
"Diagnostics": {
"EnableDetailedErrors": true
},
"Issuers": [...]
}
}
Auth0 Best Practices
- Use RS256 algorithm (Auth0 default) - asymmetric signing
- Always validate audience - prevents token reuse across APIs
- Use short token expiration - 1 hour max for access tokens
- Store secrets securely - use Azure Key Vault or similar
- Enable refresh tokens for SPAs and mobile apps
Next Steps
| Want to... | See Guide |
|---|---|
| Add Azure AD as second issuer | Multi-Issuer Setup -> |
| Harden local/dev tokens | Local JWT Guide -> |
| Revisit basics quickly | Identity Quick Start -> |