June 7, 2026

How to use Auth0 as a backoffice authentication provider in Umbraco 17

How to register Auth0 as an OpenID Connect backoffice authentication provider in Umbraco 17 using the new backoffice extension model.

In 2024 I wrote a short article about using Auth0 as a backoffice authentication provider in Umbraco 12:

That solution worked well for Umbraco 12, but the backoffice has changed a lot since then. Umbraco 17 uses the new backoffice UI, the external-login APIs live in a different namespace, and the login button is now registered as a backoffice extension through an umbraco-package.json manifest.

So this is the updated version of the same idea: use Auth0 as the identity provider for Umbraco backoffice users.

The article is written as a standalone guide. You do not need to read the Umbraco 12 version first, but if you did, the biggest differences are:

  • the C# setup now uses Umbraco.Cms.Api.Management.Security;
  • the login button is configured with a package manifest in App_Plugins;
  • provider options such as the label and button behavior are no longer the old ButtonStyle / Icon properties;
  • the OpenID Connect callback path should be explicitly reserved in Umbraco configuration.

The code below is based on a real Umbraco 17 project using Umbraco 17.4.2, but the same approach should apply to any recent Umbraco 17 installation.

The goal

When an editor goes to /umbraco, they should be able to authenticate with Auth0 instead of using a local Umbraco username and password.

In this setup:

  • Auth0 is the OpenID Connect provider;
  • Umbraco still owns the backoffice user record;
  • users can be auto-linked into Umbraco the first time they log in;
  • the login screen shows a “Login with Auth0” button.

You can decide whether to keep local Umbraco login enabled. I usually keep it enabled at first, test the Auth0 flow, and only then consider disabling local login.

1. Create the Auth0 application

In Auth0, create a new application:

  1. Go to Applications -> Applications.
  2. Click Create Application.
  3. Choose Regular Web Application.
  4. Open the Settings tab.

Take note of:

  • Domain
  • Client ID
  • Client Secret

Then configure the callback URL. In the code below I use:

/umbraco-auth0-signin

So for local development, the allowed callback URL will look like:

https://localhost:44300/umbraco-auth0-signin

And for production:

https://www.example.com/umbraco-auth0-signin

The path can be changed, but the value in Auth0 and the value in the ASP.NET Core OpenID Connect options must match.

Auth0 documents this requirement in its callback URL documentation:

https://auth0.github.io/auth0-oidc-client-net/documentation/getting-started/callbacks.html

2. Add the required packages

For a clean OpenID Connect setup, add the ASP.NET Core OpenID Connect handler:

dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect

Depending on your project and target framework, you might already get this transitively from another authentication package, but I prefer making the dependency explicit when the application calls AddOpenIdConnect directly.

I also had to reference a recent Microsoft.IdentityModel.Protocols.OpenIdConnect package in my project:

dotnet add package Microsoft.IdentityModel.Protocols.OpenIdConnect

In my project the relevant package versions are currently:

<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.18.0" />
<PackageReference Include="Umbraco.Cms" Version="17.4.2" />

Do not copy these versions blindly. Use versions compatible with your target framework and Umbraco installation.

3. Add the configuration

Add an Auth0 section to your configuration.

For example, in appsettings.Development.json:

{
  "Auth0": {
    "Authority": "https://YOUR_AUTH0_DOMAIN",
    "ClientId": "YOUR_CLIENT_ID",
    "ClientSecret": "YOUR_CLIENT_SECRET",
    "CallbackPath": "/umbraco-auth0-signin"
  }
}

In a real project, do not commit the client secret to source control. Use user secrets, environment variables, your hosting provider’s secret manager, or your deployment pipeline.

Umbraco also recommends reserving the callback path so that Umbraco does not try to handle the callback as a content request:

{
  "Umbraco": {
    "CMS": {
      "Global": {
        "ReservedPaths": "~/app_plugins/,~/install/,~/mini-profiler-resources/,~/umbraco/,~/umbraco-auth0-signin/,"
      }
    }
  }
}

For backoffice external login providers, the Umbraco documentation also mentions enabling RedirectToLoginPage in the BasicAuth section:

{
  "Umbraco": {
    "CMS": {
      "BasicAuth": {
        "RedirectToLoginPage": true
      }
    }
  }
}

The current Umbraco external login provider documentation for the Umbraco 17 docs set is here:

https://docs.umbraco.com/umbraco-cms/run-in-production/security/external-login-providers

4. Register the Auth0 backoffice provider

Create a folder in your Umbraco web project:

ExternalUserLogin

Then add this file:

ExternalUserLogin/Auth0BackOfficeAuthenticationOptions.cs
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Umbraco.Cms.Api.Management.Security;

namespace MyProject.ExternalUserLogin;

public static class Auth0AuthenticationExtensions
{
    public static IUmbracoBuilder AddAuth0Authentication(this IUmbracoBuilder builder)
    {
        builder.Services.ConfigureOptions<Auth0BackOfficeExternalLoginProviderOptions>();

        builder.AddBackOfficeExternalLogins(logins =>
        {
            logins.AddBackOfficeLogin(backOfficeAuthenticationBuilder =>
            {
                var schemeName = BackOfficeAuthenticationBuilder.SchemeForBackOffice(
                    Auth0BackOfficeExternalLoginProviderOptions.SchemeName);

                ArgumentNullException.ThrowIfNull(schemeName);

                backOfficeAuthenticationBuilder.AddOpenIdConnect(
                    schemeName,
                    options =>
                    {
                        var configuration = builder.Config;

                        options.Authority = configuration["Auth0:Authority"];
                        options.ClientId = configuration["Auth0:ClientId"];
                        options.ClientSecret = configuration["Auth0:ClientSecret"];
                        options.CallbackPath = configuration["Auth0:CallbackPath"];

                        options.ResponseType = OpenIdConnectResponseType.Code;

                        options.Scope.Clear();
                        options.Scope.Add("openid");
                        options.Scope.Add("email");
                        options.Scope.Add("profile");

                        options.SaveTokens = true;
                        options.GetClaimsFromUserInfoEndpoint = true;

                        options.TokenValidationParameters.NameClaimType = "name";
                        options.TokenValidationParameters.RoleClaimType = "role";
                    });
            });
        });

        return builder;
    }
}

There are a few important details here.

First, the scheme name is passed through BackOfficeAuthenticationBuilder.SchemeForBackOffice. In this example the local scheme name is:

OpenIdConnect

The final provider name used by Umbraco becomes:

Umbraco.OpenIdConnect

We will need that value again when registering the login button.

Second, I am using the authorization code flow:

options.ResponseType = OpenIdConnectResponseType.Code;

Third, the scopes are intentionally simple:

openid email profile

For a basic backoffice login, this is enough to identify the user and read common profile claims.

5. Configure auto-linking

Now add the provider options file:

ExternalUserLogin/Auth0BackOfficeExternalLoginProviderOptions.cs
using Microsoft.Extensions.Options;
using Umbraco.Cms.Api.Management.Security;
using Umbraco.Cms.Core;

namespace MyProject.ExternalUserLogin;

public class Auth0BackOfficeExternalLoginProviderOptions :
    IConfigureNamedOptions<BackOfficeExternalLoginProviderOptions>
{
    public const string SchemeName = "OpenIdConnect";

    public void Configure(string? name, BackOfficeExternalLoginProviderOptions options)
    {
        ArgumentNullException.ThrowIfNull(name);

        if (name != Constants.Security.BackOfficeExternalAuthenticationTypePrefix + SchemeName)
        {
            return;
        }

        Configure(options);
    }

    public void Configure(BackOfficeExternalLoginProviderOptions options)
    {
        options.AutoLinkOptions = new ExternalSignInAutoLinkOptions(
            autoLinkExternalAccount: true,
            defaultUserGroups: []
        )
        {
            OnAutoLinking = (autoLinkUser, loginInfo) =>
            {
                if (loginInfo.Principal is null)
                {
                    return;
                }

                var nameClaim = loginInfo.Principal.FindFirst("name")?.Value;

                if (!string.IsNullOrWhiteSpace(nameClaim))
                {
                    autoLinkUser.Name = nameClaim;
                }
            },

            OnExternalLogin = (user, loginInfo) =>
            {
                return true;
            }
        };

        options.DenyLocalLogin = false;
    }
}

The two most important choices are autoLinkExternalAccount and defaultUserGroups.

With autoLinkExternalAccount: true, Umbraco creates or links the backoffice user when the external login succeeds.

The defaultUserGroups value deserves some attention. In this example it is empty:

defaultUserGroups: []

That is conservative, but not necessarily useful for a real editorial team. If you want auto-linked users to become editors immediately, assign the appropriate group:

defaultUserGroups: [Constants.Security.EditorGroupAlias]

Be careful with this. Auth0 may be a private tenant, but auto-linking still means that whoever can authenticate through that Auth0 application can potentially become a backoffice user. In a production setup, you should usually restrict access by email domain, Auth0 organization, role, group, or a custom claim.

The OnExternalLogin callback returns true to allow the sign-in to continue. This is a good place to enforce additional rules:

OnExternalLogin = (user, loginInfo) =>
{
    var email = loginInfo.Principal?.FindFirst("email")?.Value;

    return email?.EndsWith("@example.com", StringComparison.OrdinalIgnoreCase) == true;
}

If you want Auth0 to be the only login method, set:

options.DenyLocalLogin = true;

I recommend doing that only after the external login flow has been tested with at least one admin account.

6. Attach the extension to the Umbraco builder

In a minimal hosting setup, you will usually have something like this in Program.cs or in a bootstrap extension:

var umbracoBuilder = builder.Services.AddUmbraco(builder.Environment, builder.Configuration)
    .AddBackOffice()
    .AddWebsite()
    .AddDeliveryApi()
    .AddComposers()
    .AddAuth0Authentication();

umbracoBuilder.Build();

In my project I keep this inside an application bootstrap extension rather than directly in Program.cs, but the important part is the same: call AddAuth0Authentication() as part of the Umbraco builder chain.

7. Add the login button in the Umbraco 17 backoffice

This is one of the main differences from my old Umbraco 12 article.

In Umbraco 12, I configured the button with properties such as ButtonStyle and Icon in BackOfficeExternalLoginProviderOptions.

In Umbraco 17, the new backoffice uses extension manifests. Create this file:

App_Plugins/Auth0Provider/umbraco-package.json
{
  "$schema": "../../umbraco-package-schema.json",
  "name": "Auth0 Authentication Package",
  "allowPublicAccess": true,
  "extensions": [
    {
      "type": "authProvider",
      "alias": "MyProject.AuthProvider.Auth0",
      "name": "Auth0 Auth Provider",
      "forProviderName": "Umbraco.OpenIdConnect",
      "meta": {
        "label": "Login with Auth0"
      }
    }
  ]
}

The critical value is:

"forProviderName": "Umbraco.OpenIdConnect"

It must match the backoffice provider name generated by:

BackOfficeAuthenticationBuilder.SchemeForBackOffice("OpenIdConnect")

If the manifest points to the wrong provider name, the button will not be wired to the login provider you registered in C#.

You can customize the button further with the manifest metadata described in the Umbraco documentation, including auto-redirect behavior and custom views.

8. Reverse proxy note

If the site runs behind a reverse proxy, make sure ASP.NET Core sees the original scheme and host.

Otherwise, the OpenID Connect middleware may generate a callback URL with the wrong protocol or host, for example http:// instead of https://. Auth0 will reject that with a callback or redirect URI mismatch.

In ASP.NET Core this usually means configuring forwarded headers before the authentication flow needs them:

app.UseForwardedHeaders();

And configuring ForwardedHeadersOptions for your hosting environment.

This is not specific to Umbraco or Auth0. It is a common OpenID Connect issue when the public URL and the internal application URL are different.

9. Testing the flow

Once the code and configuration are in place:

  1. Start the Umbraco site.
  2. Go to /umbraco.
  3. Click Login with Auth0.
  4. Authenticate on Auth0.
  5. Check that Auth0 redirects back to /umbraco-auth0-signin.
  6. Check that Umbraco creates or links the backoffice user.
  7. Check that the user has the expected Umbraco groups and permissions.

If the login fails, the first things I check are:

  • the Auth0 allowed callback URLs;
  • the CallbackPath value in the OpenID Connect options;
  • the forProviderName value in umbraco-package.json;
  • whether the user has been assigned useful Umbraco groups;
  • whether a reverse proxy is changing the public scheme or host;
  • whether local login was disabled before the external provider was fully tested.

Final thoughts

The idea is the same as in Umbraco 12: Auth0 handles identity, Umbraco handles the backoffice user.

The implementation is cleaner in some ways, especially because the backoffice login button is now a proper extension manifest. But the upgrade is not just a copy-paste of the old code. The namespace changed, the provider UI registration changed, and you need to be more explicit about the provider name that connects the C# authentication setup to the backoffice extension.

For me, the final shape is:

  • one extension method that registers OpenID Connect for the backoffice;
  • one options class that configures auto-linking and local-login behavior;
  • one umbraco-package.json manifest that exposes the Auth0 button;
  • configuration values kept outside the code.

Once those pieces line up, Auth0 works nicely as an Umbraco 17 backoffice authentication provider.

References