Auth0Net.DependencyInjection 8.0.0

dotnet add package Auth0Net.DependencyInjection --version 8.0.0
                    
NuGet\Install-Package Auth0Net.DependencyInjection -Version 8.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Auth0Net.DependencyInjection" Version="8.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Auth0Net.DependencyInjection" Version="8.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Auth0Net.DependencyInjection" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Auth0Net.DependencyInjection --version 8.0.0
                    
#r "nuget: Auth0Net.DependencyInjection, 8.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Auth0Net.DependencyInjection@8.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Auth0Net.DependencyInjection&version=8.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Auth0Net.DependencyInjection&version=8.0.0
                    
Install as a Cake Tool

Auth0.NET Dependency Injection Extensions

NuGet Nuget

<h1 align="center"> <img align="center" src="https://user-images.githubusercontent.com/975824/128343470-8d97e39d-ff8a-4daf-8ebf-f9039a46abd6.png" height="130px" /> </h1>

Integrating Auth0.NET into your project whilst following idiomatic .NET conventions can be cumbersome and involve a sizable amount of boilerplate shared between projects.

This library hopes to solve that problem, featuring:

✅ Extensions for Microsoft.Extensions.DependencyInjection.

✅ Automatic access token caching & renewal for the Management API and your own REST & Grpc services

✅ HttpClientFactory integration for centralized extensibility and management of the internal HTTP handlers.

✅ IHttpClientBuilder extensions, providing handlers to automatically append access tokens to outgoing requests.

✅ Cached On-Behalf-Of token exchange, for calling other APIs as the current user.

This library is compatible with .NET 8+ as well as .NET Framework 4.8 and is suitable for use in ASP.NET Core and standalone .NET Generic Host applications.

Install

Add Auth0Net.DependencyInjection to your project:

dotnet add package Auth0Net.DependencyInjection

Scenarios

Authentication Client Only

Auth0Authentication

If you're simply using the AuthenticationApiClient and nothing else, you can call AddAuth0AuthenticationClient and pass in your Auth0 Domain. This integration is lightweight and does not support any other features of this library.

services.AddAuth0AuthenticationClient("your-auth0-domain.auth0.com");

You can then request the IAuthenticationApiClient within your class:


public class AuthController : ControllerBase
{
    private readonly IAuthenticationApiClient _authenticationApiClient;

    public AuthController(IAuthenticationApiClient authenticationApiClient)
    {
        _authenticationApiClient = authenticationApiClient;
    }

Authentication Client + Management Client

Auth0AuthenticationAndManagement

Add the AuthenticationApiClient with AddAuth0AuthenticationClient, and provide a machine-to-machine application configuration that will be consumed by the Management Client, Token Cache and IHttpClientBuilder integrations. This extension must be called before using any other extensions within this library:

services.AddAuth0AuthenticationClient(config =>
{
   config.Domain = builder.Configuration["Auth0:Domain"];
   config.ClientId = builder.Configuration["Auth0:ClientId"];
   config.ClientSecret = builder.Configuration["Auth0:ClientSecret"];
});

Add the ManagementApiClient with AddAuth0ManagementClient(). The client will attach the Access Token automatically:

services.AddAuth0ManagementClient();

Ensure your Machine-to-Machine application is authorized to request tokens from the Managment API and it has the correct scopes for the features you wish to use.

You can then request the IManagementApiClient (or IAuthenticationApiClient) within your services:

public class MyAuth0Service : IAuth0Service
{
    private readonly IManagementApiClient _managementApiClient;

    public MyAuth0Service(IManagementApiClient managementApiClient)
    {
        _managementApiClient = managementApiClient;
    }
Handling Custom Domains

If you're using a custom domain with your Auth0 tenant, and it's being specified when calling AddAuth0AuthenticationClient, you will run into a problem whereby the audience of the Management API is being incorrectly set. You can override this via the Audience property:

services.AddAuth0ManagementClient(c =>
    {
        // Set the audience to your default Auth0 domain.
        c.Audience = "my-tenant.au.auth0.com";
    });

External HttpClient & Grpc Services (Machine-To-Machine Tokens)

Auth0AuthenticationAll

Note: This feature relies on services.AddAuth0AuthenticationClient(config => ...) being called and configured as outlined in the previous section.

This library includes a delegating handler - effectively middleware for your HttpClient - that will append an access token to all outbound requests. This is useful for calling other services that are protected by Auth0. This integration requires your service implementation to use IHttpClientFactory as part of its registration. You can read more about it here

HttpClient

Use AddAccessToken along with the required audience:

services.AddHttpClient<MyHttpService>(x => x.BaseAddress = new Uri(builder.Configuration["MyHttpService:Url"]))
        .AddAccessToken(config => config.Audience = builder.Configuration["MyHttpService:Audience"]);
Grpc

This extension is compatible with any registration that returns a IHttpClientBuilder, thus it can be used with Grpc's client factory:

services.AddGrpcClient<UserService.UserServiceClient>(x => x.Address = new Uri(builder.Configuration["MyGrpcService:Url"]))
        .AddAccessToken(config => config.Audience = builder.Configuration["MyGrpcService:Audience"]);
Advanced

AddAccessToken also has an option for passing in a func that can resolve the audience at runtime. This can be useful if your expected audiences always follow a pattern, or if you rely on service discovery, such as from Steeltoe.NET:

services.AddHttpClient<MyHttpService>(x=> x.BaseAddress = new Uri("https://MyServiceName/"))
        .AddServiceDiscovery()
        .AddAccessToken(config => config.AudienceResolver = request => request.RequestUri.GetLeftPart(UriPartial.Authority));

M2M Organizations Support

This library includes support for Machine-to-Machine (M2M) Access for Organizations, including static and dynamic scenarios. This feature is important if your internal or third-party services expect a token to be scoped to a specific Auth0 organization.

Orgs support must be enabled for your combination of client/api/org(s) before usage.

Static Organization

Clients that simply require a single organization for a specific client can do so via setting the Organization property when configuring the access token:

builder.Services
    .AddGrpcClient<UserService.UserServiceClient>(x => x.Address = new Uri(builder.Configuration["MyApi:Url"]!))
    .AddAccessToken(config =>
    {
        config.Audience = builder.Configuration["MyApi:Audience"];
        config.Organization = builder.Configuration["MyApi:Organization"];
    });
Dynamic Organization via Request Metadata

If you already include org metadata as part of your network request or via the request options and would like to easily migrate to Org-scoped tokens, you can choose to resolve the organization at runtime via the OrganizationResolver:

builder.Services
    .AddGrpcClient<UserService.UserServiceClient>(x => x.Address = new Uri(builder.Configuration["MyApi:Url"]!))
    .AddAccessToken(config =>
    {
        config.Audience = builder.Configuration["MyApi:Audience"];
        config.OrganizationResolver = x =>
            x.Headers.TryGetValues("org-id", out var values) 
                ? values.SingleOrDefault() 
                : null;
    });
Dynamic Organization via Client Scope

If your organization source is scoped to the usage of your service, such as an ASP.NET Core request, then you'll want the ability to freely set the Organization. You can achieve this by injecting your client via OrganizationScopeFactory<TClient> and then creating an organization scope via .CreateScope:

// Inject the factory around your remote client
private readonly OrganizationScopeFactory<UsersService> _scopeFactory;

public QueryUsersService(OrganizationScopeFactory<UsersService> scopeFactory)
{
    _scopeFactory = scopeFactory;
}

public async Task CreateUserAsync(User user, string orgId)
{
    // Create the scope so the MTM token is generated with the current OrgId    
    using var orgScope = _scopeFactory.CreateScope(orgId);
    await orgScope.Client.CreateUser(user, stoppingToken);
}
  

ALWAYS ensure you dispose of the scope when finished.

There's a few limitations if you're using this functionality, as it uses AsyncLocal internally:

  • Never use multiple client scopes at the same time, either with the same or different client types. This will throw an exception.
  • Never call any other client that utilizes .AddAccessToken within a client scope. This may cause the wrong Organization ID/Name being used for a given request.

If you have a use-case for either of these items, please open an issue with an example.

On-Behalf-Of Token Exchange

Auth0's On-Behalf-Of (OBO) token exchange lets your API exchange the access token it received from a user for an access token to another API. The new token keeps the user's identity (sub, org_id) and records your API as the actor (act).

Prerequisite: create a Custom API client in Auth0 that is linked to your API, and allow it to exchange tokens for the target API. Its credentials are separate from the Machine-to-Machine credentials used elsewhere in this library.

It requires one of the AddAuth0AuthenticationClient overloads. The domain-only overload is enough if you don't need Machine-to-Machine tokens:

services.AddAuth0AuthenticationClient(builder.Configuration["Auth0:Domain"]);

services.AddAuth0OnBehalfOf(config =>
{
    config.ClientId = builder.Configuration["Auth0:OnBehalfOf:ClientId"];
    config.ClientSecret = builder.Configuration["Auth0:OnBehalfOf:ClientSecret"];
    // Or authenticate with Private Key JWT instead of a secret:
    // config.ClientAssertionSecurityKey = new RsaSecurityKey(rsa);
    // config.ClientAssertionSecurityKeyAlgorithm = SecurityAlgorithms.RsaSha256;
});

Inject IAuth0OnBehalfOfTokenCache and pass it the user's access token. In ASP.NET Core, JwtBearer saves the incoming token by default, so it can be read with GetTokenAsync("access_token"):

app.MapPost("/downstream-token", async (HttpContext ctx, IAuth0OnBehalfOfTokenCache tokenCache) =>
{
    var subjectToken = await ctx.GetTokenAsync("access_token");
    if (string.IsNullOrEmpty(subjectToken))
        return Results.Unauthorized();

    try
    {
        var token = await tokenCache.GetTokenAsync(
            subjectToken,
            audience: "https://downstream.example.com/",
            scope: "read:data",
            organization: ctx.User.FindFirstValue("org_id"),
            token: ctx.RequestAborted);

        return Results.Ok(new { token.AccessToken, token.ExpiresAt, token.Scope });
    }
    catch (Auth0OnBehalfOfException ex)
    {
        // 401: the user's token is invalid or expired. 403: the client, scope or organization is not allowed. 429: rate limited, see ex.RetryAfter.
        return Results.Problem(statusCode: ex.StatusCode, title: ex.Error, detail: ex.ErrorDescription);
    }
});

OnBehalfOfToken.Scope holds the scopes Auth0 granted, which may be narrower than the scopes requested. If the user's token has already expired, Auth0 is not called and a 401 Auth0OnBehalfOfException is thrown.

Calling a downstream API on behalf of the user

AddOnBehalfOfToken adds a handler to an HttpClient that exchanges the user's access token and sets the exchanged token as the Authorization header of each request. It requires AddAuth0OnBehalfOf; resolving the client without it throws an InvalidOperationException.

services.AddAuth0AuthenticationClient(builder.Configuration["Auth0:Domain"]);

services.AddAuth0OnBehalfOf(config =>
{
    config.ClientId = builder.Configuration["Auth0:OnBehalfOf:ClientId"];
    config.ClientSecret = builder.Configuration["Auth0:OnBehalfOf:ClientSecret"];
});

services.AddHttpContextAccessor();

services.AddHttpClient<MyClient>(x => x.BaseAddress = new Uri(builder.Configuration["MyHttpService:Url"]))
    .AddOnBehalfOfToken(o =>
    {
        o.Audience = builder.Configuration["MyHttpService:Audience"];
        o.Scope = "read:things";
        o.SubjectTokenResolver = (sp, _) =>
            sp.GetRequiredService<IHttpContextAccessor>().HttpContext?.Request.Headers.Authorization.ToString() is { } h
            && h.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)
                ? h["Bearer ".Length..]
                : null;
    });

SubjectTokenResolver receives the service provider the handler was created from, which is not the scope of the incoming request, so read request state through a singleton such as IHttpContextAccessor.

When there is no HttpContext, such as in a background job, set the user's token on the request instead. A token set this way takes precedence over SubjectTokenResolver:

var request = new HttpRequestMessage(HttpMethod.Get, "things").SetSubjectToken(token);
var response = await httpClient.SendAsync(request);

Audience, AudienceResolver, Organization and OrganizationResolver behave as they do for AddAccessToken.

Failures are not handled by the handler. Rejected exchanges propagate from SendAsync as an Auth0OnBehalfOfException, including a 401 when no subject token is available. Other failures, such as network errors or timeouts while calling Auth0, propagate unchanged.

Additional Functionality

Utility

This library exposes a simple string extension, ToHttpsUrl(), that can be used to format the naked Auth0 domain sitting in your configuration into a proper URL.

This is identical to https://{Configuration["Auth0:Domain"]}/ that you usually end up writing somewhere in your Program.cs.

For example, formatting the domain for the JWT Authority:

.AddJwtBearer(options =>
 {
     // "my-tenant.auth0.com" -> "https://my-tenant.auth0.com/"
     options.Authority = builder.Configuration["Auth0:Domain"].ToHttpsUrl();
     //...
 });

Internals

Client Lifetimes

Both the authentication and authorization clients are registered as singletons and are suitable for injection into any other lifetime.

Samples

Both a .NET Generic Host and ASP.NET Core examples are available in the samples directory.

Internal Cache

The Auth0TokenCache will cache a token for a given audience until at least 95% of the expiry time. If a request to the cache is made between 95% and 99% of expiry, the token will be refreshed in the background before expiry is reached.

An additional 1% of lifetime is removed to protect against clock drift between distributed systems.

In some situations you might want to request an access token from Auth0 manually. You can achieve this by injecting IAuth0TokenCache into a class and calling GetTokenAsync with the audience of the API you're requesting the token for.

An in-memory-only instance of FusionCache is used as the caching implementation. This instance is named and will not impact other usages of FusionCache.

If you want to use your own implementation of FusionCache, specify FusionCacheResolver when configurating the authentication client:

services.AddAuth0AuthenticationClient(x =>
 {
     //...
     // Use the default FusionCache instance registered via `.AddFusionCache()`
     x.FusionCacheResolver = provider => provider.GetDefaultCache();
 });

IAuth0OnBehalfOfTokenCache uses the same FusionCache instance, unless FusionCacheResolver is set on Auth0OnBehalfOfConfiguration. Unlike Machine-to-Machine tokens, these entries are never refreshed in the background, and fail-safe is disabled for them, so a token is never served after the user's token has expired.

Exchanged tokens are only kept in memory, even if the FusionCache instance has a distributed second level. To share them through the distributed cache, set UseDistributedCache = true in AddAuth0OnBehalfOf. They are short-lived user tokens, so make sure that cache is appropriately secured.

Disclaimer

I am not affiliated with nor represent Auth0. All implementation issues regarding the underlying ManagementApiClient and AuthenticationApiClient should go to the official Auth0.NET Respository.

License notices

Icons used under the MIT License from the Identicons pack.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Framework net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
8.0.0 220 10/8/2026
7.0.0 13,053 7/16/2026
6.0.0 13,357 4/3/2026
5.2.1 11,732 3/31/2026
5.2.0 60,937 11/20/2025
5.1.0 104,996 5/12/2025
5.0.0 54,907 1/30/2025
4.0.0 75,175 11/17/2024
3.2.0 124,599 3/8/2024
3.1.0 38,816 12/21/2023
3.0.0 66,287 6/20/2023
2.0.0 134,774 11/12/2022
1.7.0 8,454 8/30/2022
1.6.0 74,086 2/22/2022
1.5.0 17,462 8/27/2021
1.4.1 1,337 8/5/2021
1.4.0 4,035 4/3/2021
1.3.0 1,432 12/3/2020
1.2.0 833 11/27/2020
1.1.0 874 11/24/2020
Loading failed