Skip to main content

Documentation Index

Fetch the complete documentation index at: https://auth0.com/llms.txt

Use this file to discover all available pages before exploring further.

This Quickstart is currently in Beta. We’d love to hear your feedback!

Use AI to integrate Auth0

If you use an AI coding assistant like Claude Code, Cursor, or GitHub Copilot, you can add Auth0 authentication automatically in minutes using agent skills.Install:
npx skills add auth0/agent-skills --skill auth0-quickstart --skill auth0-aspnetcore-api
Then ask your AI assistant:
Add Auth0 JWT authentication to my ASP.NET Core Web API
Your AI assistant will automatically create your Auth0 API, fetch credentials, install the Auth0 ASP.NET Core Authentication API SDK, configure JWT bearer authentication, and implement protected API endpoints. Full agent skills documentation →
Prerequisites: Before you begin, ensure you have the following installed:
  • .NET 8.0 SDK or higher
  • Your preferred IDE (Visual Studio 2022, VS Code, or Rider)
.NET Version Compatibility: This quickstart works with .NET 8.0 and newer.

Get Started

This quickstart demonstrates how to add Auth0 JWT authentication to an ASP.NET Core Web API. You’ll build a secure API with protected endpoints using the Auth0 ASP.NET Core API SDK.
1

Create a new project

Create a new ASP.NET Core Web API project for this Quickstart
dotnet new webapi -n Auth0Api
Open the project
cd Auth0Api
2

Install the Auth0 SDK

dotnet add package Auth0.AspNetCore.Authentication.Api
3

Setup your Auth0 API

Next up, you need to create a new API on your Auth0 tenant and add the configuration to your project.You can choose to do this automatically by running a CLI command or do it manually via the Dashboard:
Run the following shell command on your project’s root directory to create an Auth0 API and update your appsettings.json file:
AUTH0_API_NAME="My ASP.NET Core API" && \
AUTH0_API_IDENTIFIER="https://my-api" && \
brew tap auth0/auth0-cli && \
brew install auth0 && \
auth0 login --no-input && \
auth0 apis create -n "${AUTH0_API_NAME}" -i "${AUTH0_API_IDENTIFIER}" --offline-access --token-lifetime 86400 --signing-alg RS256 --json > auth0-api-details.json && \
DOMAIN=$(auth0 tenants list --json | jq -r '.[] | select(.active == true) | .name') && \
AUDIENCE=$(jq -r '.identifier' auth0-api-details.json) && \
jq --arg domain "$DOMAIN" --arg audience "$AUDIENCE" \
  '.Auth0.Domain = $domain | .Auth0.Audience = $audience' \
  appsettings.json > appsettings.tmp.json && \
mv appsettings.tmp.json appsettings.json && \
rm auth0-api-details.json && \
echo "✅ appsettings.json updated with your Auth0 API details:" && \
cat appsettings.json
4

Configure authentication

Replace the entire contents of Program.cs with the following code:
Program.cs
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();

if (!app.Environment.IsDevelopment())
{
    app.UseHttpsRedirection();
}

app.UseAuthentication();
app.UseAuthorization();

app.Run();
5

Create public and protected endpoints

Add endpoints to test authentication. Add the following code in Program.cs before app.Run():
Program.cs
// Public endpoint - no authentication required
app.MapGet("/api/public", () => 
    Results.Ok(new { Message = "This endpoint is public" }))
    .WithName("GetPublic");

// Protected endpoint - requires authentication
app.MapGet("/api/private", () => 
    Results.Ok(new { Message = "This endpoint requires authentication" }))
    .RequireAuthorization()
    .WithName("GetPrivate");
6

Run your API

dotnet run
Your API is now running on https://localhost:7190 (or similar - check your console output for the exact URL).
CheckpointYou should now have a fully functional Auth0-protected API running on your localhost

Advanced Usage

Test your protected endpoints with an access token.1. Get an access token from Auth0 using the Client Credentials flow:
curl --request POST \
  --url https://YOUR_DOMAIN/oauth/token \
  --header 'content-type: application/json' \
  --data '{"client_id":"YOUR_CLIENT_ID","client_secret":"YOUR_CLIENT_SECRET","audience":"YOUR_AUDIENCE","grant_type":"client_credentials"}'
To get YOUR_CLIENT_ID and YOUR_CLIENT_SECRET, create a Machine to Machine Application in the Auth0 Dashboard and authorize it for your API.
2. Test the public endpoint (should return 200 OK):
curl https://localhost:7190/api/public
3. Test the protected endpoint without authentication (should return 401 Unauthorized):
curl https://localhost:7190/api/private
4. Call the protected endpoint with the token:
curl https://localhost:7190/api/private \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
For larger APIs, use controllers instead of minimal API endpoints.1. Add controllers support:
Program.cs
builder.Services.AddControllers();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseHttpsRedirection();
}

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();
2. Create a controller:Create Controllers/MessagesController.cs:
Controllers/MessagesController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Auth0Api.Controllers;

[ApiController]
[Route("api/[controller]")]
public class MessagesController : ControllerBase
{
    [HttpGet]
    public IActionResult GetPublic()
    {
        return Ok(new { Message = "This endpoint is public" });
    }

    [Authorize]
    [HttpGet("private")]
    public IActionResult GetPrivate()
    {
        var userId = User.FindFirst("sub")?.Value;
        return Ok(new { Message = "This endpoint is protected", UserId = userId });
    }

    [Authorize(Policy = "read:messages")]
    [HttpGet("messages")]
    public IActionResult GetMessages()
    {
        return Ok(new { Messages = new[] { "Message 1", "Message 2" } });
    }
}
Protect endpoints based on specific scopes in the access token.1. Define scopes in your Auth0 API:In the Auth0 Dashboard → APIs → Your API → Permissions, add scopes:
  • read:messages - Read messages
  • write:messages - Write messages
2. Configure authorization policies:
Program.cs
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("read:messages", policy =>
        policy.RequireClaim("scope", "read:messages"));
    
    options.AddPolicy("write:messages", policy =>
        policy.RequireClaim("scope", "write:messages"));
});
3. Apply policies to endpoints:
app.MapGet("/api/messages", () => 
    Results.Ok(new { Messages = new[] { "Message 1", "Message 2" } }))
    .RequireAuthorization("read:messages");

app.MapPost("/api/messages", () => 
    Results.Created("/api/messages/1", new { Id = 1, Text = "New message" }))
    .RequireAuthorization("write:messages");
When requesting an access token, include the required scope:
curl --request POST \
  --url https://YOUR_DOMAIN/oauth/token \
  --header 'content-type: application/json' \
  --data '{"client_id":"YOUR_CLIENT_ID","client_secret":"YOUR_CLIENT_SECRET","audience":"YOUR_AUDIENCE","grant_type":"client_credentials","scope":"read:messages write:messages"}'
DPoP (Demonstration of Proof-of-Possession) binds access tokens to cryptographic keys, preventing token theft and replay attacks.Enable DPoP support:
Program.cs
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
DPoP Modes:Accept both DPoP and Bearer tokens (default):
using Auth0.AspNetCore.Authentication.Api.DPoP;

.WithDPoP(dpopOptions =>
{
    dpopOptions.Mode = DPoPModes.Allowed;
});
Only accept DPoP tokens, reject Bearer tokens:
using Auth0.AspNetCore.Authentication.Api.DPoP;

.WithDPoP(dpopOptions =>
{
    dpopOptions.Mode = DPoPModes.Required;
});
Configure time validation parameters:
.WithDPoP(dpopOptions =>
{
    dpopOptions.Mode = DPoPModes.Allowed;
    dpopOptions.IatOffset = 300;  // Allow DPoP proof up to 5 minutes old
    dpopOptions.Leeway = 30;      // 30 seconds clock skew tolerance
});
Learn more about DPoP in the Auth0 DPoP Documentation.
Create reusable authorization policies for complex requirements.1. Create a custom requirement:
Authorization/HasScopeRequirement.cs
using Microsoft.AspNetCore.Authorization;

namespace Auth0Api.Authorization;

public class HasScopeRequirement : IAuthorizationRequirement
{
    public string Scope { get; }
    public string Issuer { get; }

    public HasScopeRequirement(string scope, string issuer)
    {
        Scope = scope;
        Issuer = issuer;
    }
}
2. Create a handler:
Authorization/HasScopeHandler.cs
using Microsoft.AspNetCore.Authorization;

namespace Auth0Api.Authorization;

public class HasScopeHandler : AuthorizationHandler<HasScopeRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        HasScopeRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == "scope" && c.Issuer == requirement.Issuer))
        {
            return Task.CompletedTask;
        }

        var scopes = context.User
            .FindFirst(c => c.Type == "scope" && c.Issuer == requirement.Issuer)?
            .Value.Split(' ');

        if (scopes?.Any(s => s == requirement.Scope) == true)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}
3. Register and use the policy:
Program.cs
using Auth0Api.Authorization;

var builder = WebApplication.CreateBuilder(args);

var domain = $"https://{builder.Configuration["Auth0:Domain"]}";

builder.Services.AddAuth0ApiAuthentication(options =>
{
    options.Domain = builder.Configuration["Auth0:Domain"];
    options.JwtBearerOptions = new JwtBearerOptions
    {
        Audience = builder.Configuration["Auth0:Audience"]
    };
});

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("read:messages", policy =>
        policy.Requirements.Add(new HasScopeRequirement("read:messages", domain)));
});

builder.Services.AddSingleton<IAuthorizationHandler, HasScopeHandler>();

var app = builder.Build();
// ... rest of configuration
Extract user information from the authenticated token.
app.MapGet("/api/user-info", (HttpContext context) =>
{
    var userId = context.User.FindFirst("sub")?.Value;
    var email = context.User.FindFirst("email")?.Value;
    var name = context.User.FindFirst("name")?.Value;
    var scopes = context.User.FindAll("scope")
        .SelectMany(c => c.Value.Split(' '))
        .Distinct();

    return Results.Ok(new
    {
        UserId = userId,
        Email = email,
        Name = name,
        Scopes = scopes
    });
})
.RequireAuthorization();
Customize JWT token validation parameters for specific requirements.
Program.cs
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;

builder.Services.AddAuth0ApiAuthentication(options =>
{
    options.Domain = builder.Configuration["Auth0:Domain"];
    options.JwtBearerOptions = new JwtBearerOptions
    {
        Audience = builder.Configuration["Auth0:Audience"],
        
        TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.FromMinutes(5),
            NameClaimType = ClaimTypes.NameIdentifier,
            RoleClaimType = "https://my-app.com/roles"
        },
        
        Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                Console.WriteLine($"Authentication failed: {context.Exception.Message}");
                return Task.CompletedTask;
            },
            
            OnTokenValidated = context =>
            {
                var userId = context.Principal?.FindFirst("sub")?.Value;
                Console.WriteLine($"Token validated for user: {userId}");
                return Task.CompletedTask;
            }
        }
    };
});

Additional Resources

SDK Documentation

Complete SDK documentation and API reference

Migration Guide

Migrate from JWT Bearer authentication

Code Examples

Comprehensive code examples and patterns

DPoP Documentation

Learn about proof-of-possession security

Token Best Practices

Security best practices for tokens

Community Forum

Get help from the Auth0 community

Common Issues

Problem: Token validation fails with audience mismatch error.Solution: Ensure the Audience in appsettings.json exactly matches the Identifier of your Auth0 API. The audience claim in the token must match this value.
{
  "Auth0": {
    "Audience": "https://my-api"  // Must match Auth0 API Identifier
  }
}
Problem: Token validation fails with issuer error.Solution: Verify your Domain is correct and does not include https://. The library automatically constructs the authority as https://{Domain}.
{
  "Auth0": {
    "Domain": "your-tenant.auth0.com"  // No https://
  }
}
Problem: ArgumentNullException: Value cannot be null. (Parameter 'Domain') or similar.Solution: Ensure appsettings.json contains the Auth0 section with Domain and Audience values. Check that configuration is being read correctly:
builder.Services.AddAuth0ApiAuthentication(options =>
{
    options.Domain = builder.Configuration["Auth0:Domain"]
        ?? throw new InvalidOperationException("Auth0:Domain is required");
    options.JwtBearerOptions = new JwtBearerOptions
    {
        Audience = builder.Configuration["Auth0:Audience"]
            ?? throw new InvalidOperationException("Auth0:Audience is required")
    };
});
Problem: SSL/TLS certificate errors when running locally.Solution: Trust the development certificate:
dotnet dev-certs https --trust
Or generate a new certificate:
dotnet dev-certs https --clean
dotnet dev-certs https --trust
Problem: Authentication not working despite correct configuration.Solution: Ensure middleware is in the correct order. UseAuthentication() must come before UseAuthorization():
app.UseAuthentication();  // Must be before UseAuthorization
app.UseAuthorization();
app.MapControllers();
Problem: Scope-based authorization policies always fail.Solution: Ensure your access token includes the required scopes. When requesting a token, specify the scopes:
curl --request POST \
  --url https://YOUR_DOMAIN/oauth/token \
  --data '{"client_id":"...","client_secret":"...","audience":"...","grant_type":"client_credentials","scope":"read:messages write:messages"}'
Also verify scopes are defined in your Auth0 API settings (Dashboard → APIs → Your API → Permissions).

Sample Application

A complete sample application demonstrating all features is available in the SDK repository.

Playground Application

Includes public and protected endpoints, DPoP support, Swagger UI integration, and Postman collection
Clone and run:
git clone https://github.com/auth0/aspnetcore-api.git
cd aspnetcore-api/Auth0.AspNetCore.Authentication.Api.Playground
# Update appsettings.json with your Auth0 configuration
dotnet run