KMS ITC
CMS & Security 12 min read

Umbraco + Auth0 with RBAC: Secure Authentication for Your CMS

A technical guide on integrating Umbraco CMS with Auth0 for authentication, implementing role-based access control (RBAC) for secure, scalable user management.

KI

KMS ITC

#Umbraco #Auth0 #RBAC #OpenID Connect #Security

Modern web applications demand robust authentication and fine-grained access control. When building enterprise portals with Umbraco CMS, integrating with Auth0 provides a powerful, secure, and scalable identity solution. This guide walks through implementing Auth0 authentication with Role-Based Access Control (RBAC) in Umbraco.

Why Auth0 + Umbraco?

Umbraco is a flexible, developer-friendly CMS built on .NET. Auth0 is an industry-leading identity platform. Together, they offer:

  • Single Sign-On (SSO) across multiple applications
  • Social and enterprise logins (Google, Microsoft, SAML, etc.)
  • Multi-factor authentication (MFA) out of the box
  • Centralized user management with roles and permissions
  • Compliance-ready security (SOC 2, GDPR, HIPAA)

Architecture Overview

The integration follows the OpenID Connect (OIDC) protocol:

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Umbraco   │────▶│    Auth0    │────▶│   Identity  │
│   Website   │◀────│   Tenant    │◀────│   Provider  │
└─────────────┘     └─────────────┘     └─────────────┘
       │                   │
       │    JWT Token      │
       │◀──────────────────│
       │   (with roles)    │

Step 1: Configure Auth0

Create an Application

  1. Log into your Auth0 Dashboard
  2. Navigate to Applications > Applications > Create Application
  3. Select Regular Web Application
  4. Note your Domain, Client ID, and Client Secret

Configure Callbacks

Set the following URLs (adjust for your environment):

Allowed Callback URLs:
https://yourdomain.com/umbraco-signin-oidc/

Allowed Logout URLs:
https://yourdomain.com/

Set Up Roles

  1. Navigate to User Management > Roles
  2. Create roles that match your Umbraco member groups:
    • Admin
    • Editor
    • Member

Add Roles to Token

Create an Auth0 Action to include roles in the ID token:

exports.onExecutePostLogin = async (event, api) => {
  const namespace = 'https://yourdomain.com';
  
  if (event.authorization) {
    api.idToken.setCustomClaim(
      `${namespace}/roles`, 
      event.authorization.roles
    );
    api.accessToken.setCustomClaim(
      `${namespace}/roles`, 
      event.authorization.roles
    );
  }
};

Step 2: Configure Umbraco

Install Required Packages

dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect

Configure Authentication

In your Startup.cs or Program.cs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect(options =>
    {
        options.Authority = $"https://{Configuration["Auth0:Domain"]}";
        options.ClientId = Configuration["Auth0:ClientId"];
        options.ClientSecret = Configuration["Auth0:ClientSecret"];
        options.ResponseType = "code";
        options.Scope.Clear();
        options.Scope.Add("openid");
        options.Scope.Add("profile");
        options.Scope.Add("email");
        
        options.CallbackPath = new PathString("/umbraco-signin-oidc");
        options.ClaimsIssuer = "Auth0";
        
        options.Events = new OpenIdConnectEvents
        {
            OnTokenValidated = context =>
            {
                // Map Auth0 roles to Umbraco member groups
                var claimsIdentity = context.Principal.Identity as ClaimsIdentity;
                var rolesClaim = claimsIdentity?.FindFirst("https://yourdomain.com/roles");
                
                if (rolesClaim != null)
                {
                    var roles = JsonSerializer.Deserialize<string[]>(rolesClaim.Value);
                    foreach (var role in roles)
                    {
                        claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role));
                    }
                }
                
                return Task.CompletedTask;
            }
        };
    });
}

Create Login Controller

public class AuthController : SurfaceController
{
    public AuthController(
        IUmbracoContextAccessor umbracoContextAccessor,
        IUmbracoDatabaseFactory databaseFactory,
        ServiceContext services,
        AppCaches appCaches,
        IProfilingLogger profilingLogger,
        IPublishedUrlProvider publishedUrlProvider)
        : base(umbracoContextAccessor, databaseFactory, services, appCaches, profilingLogger, publishedUrlProvider)
    {
    }

    [HttpGet]
    public IActionResult Login(string returnUrl = "/")
    {
        var redirectUrl = Url.Action(nameof(LoginCallback), "Auth", new { returnUrl });
        var properties = new AuthenticationProperties { RedirectUri = redirectUrl };
        
        return Challenge(properties, OpenIdConnectDefaults.AuthenticationScheme);
    }

    [HttpGet]
    public async Task<IActionResult> LoginCallback(string returnUrl = "/")
    {
        var authenticateResult = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);

        if (!authenticateResult.Succeeded)
            return RedirectToAction(nameof(Login));

        // Sync user with Umbraco Members
        await SyncMemberAsync(authenticateResult.Principal);

        return LocalRedirect(returnUrl);
    }

    [HttpGet]
    public async Task<IActionResult> Logout()
    {
        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
        await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);

        return Redirect("/");
    }

    private async Task SyncMemberAsync(ClaimsPrincipal principal)
    {
        var email = principal.FindFirst(ClaimTypes.Email)?.Value;
        var name = principal.FindFirst(ClaimTypes.Name)?.Value;
        var roles = principal.FindAll(ClaimTypes.Role).Select(c => c.Value);

        // Find or create member in Umbraco
        var memberService = Services.MemberService;
        var member = memberService.GetByEmail(email);

        if (member == null)
        {
            member = memberService.CreateMember(email, email, name, "Member");
            memberService.Save(member);
        }

        // Sync roles to member groups
        foreach (var role in roles)
        {
            memberService.AssignRole(member.Id, role);
        }
    }
}

Step 3: Create View Components

Login/Logout Partial

@using Microsoft.AspNetCore.Authentication
@{
    var isAuthenticated = User.Identity?.IsAuthenticated ?? false;
}

@if (isAuthenticated)
{
    <div class="user-menu">
        <span>Welcome, @User.Identity.Name</span>
        <a href="/umbraco/surface/auth/logout" class="btn btn-outline">Logout</a>
    </div>
}
else
{
    <a href="/umbraco/surface/auth/login" class="btn btn-primary">Login</a>
}

Role-Based Content Display

@if (User.IsInRole("Admin"))
{
    <div class="admin-panel">
        <h3>Admin Dashboard</h3>
        <!-- Admin-only content -->
    </div>
}

@if (User.IsInRole("Editor"))
{
    <div class="editor-tools">
        <h3>Editor Tools</h3>
        <!-- Editor-only content -->
    </div>
}

Step 4: Protect Routes

Attribute-Based Authorization

[Authorize(Roles = "Admin")]
public class AdminController : Controller
{
    public IActionResult Dashboard()
    {
        return View();
    }
}

[Authorize(Roles = "Admin,Editor")]
public class ContentController : Controller
{
    public IActionResult Manage()
    {
        return View();
    }
}

Policy-Based Authorization

For more complex scenarios:

services.AddAuthorization(options =>
{
    options.AddPolicy("CanEditContent", policy =>
        policy.RequireRole("Admin", "Editor"));
    
    options.AddPolicy("CanManageUsers", policy =>
        policy.RequireRole("Admin"));
    
    options.AddPolicy("PremiumMember", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c => c.Type == "subscription" && c.Value == "premium")));
});

Configuration Settings

Store your Auth0 settings securely in appsettings.json:

{
  "Auth0": {
    "Domain": "your-tenant.auth0.com",
    "ClientId": "your-client-id",
    "ClientSecret": "your-client-secret"
  }
}

For production, use Azure Key Vault, AWS Secrets Manager, or environment variables.

Testing the Integration

  1. Login Flow: Click login → redirected to Auth0 → authenticate → returned to Umbraco
  2. Role Assignment: Verify roles from Auth0 map to Umbraco member groups
  3. Protected Content: Confirm unauthorized users cannot access restricted areas
  4. Logout: Ensure session is cleared on both Umbraco and Auth0

Common Issues & Solutions

Roles Not Appearing

  • Ensure your Auth0 Action is deployed and attached to the Login flow
  • Verify the namespace in your claims matches your code
  • Check the JWT token in browser DevTools for the roles claim

Callback URL Mismatch

  • Double-check the callback URL in Auth0 matches exactly (including trailing slash)
  • Ensure HTTPS is configured correctly

Member Not Created

  • Verify the MemberService is available and the member type exists
  • Check Umbraco logs for any exceptions during member creation

Security Best Practices

  1. Always use HTTPS in production
  2. Rotate client secrets periodically
  3. Enable MFA in Auth0 for sensitive applications
  4. Use short token lifetimes and refresh tokens
  5. Implement proper logout that clears both local and Auth0 sessions
  6. Audit authentication events using Auth0 logs

Conclusion

Integrating Auth0 with Umbraco provides a robust, enterprise-grade authentication solution with minimal custom code. The RBAC implementation ensures fine-grained access control that scales with your application’s needs.

This pattern can be extended to support:

  • Multi-tenant applications
  • API authorization
  • Machine-to-machine authentication
  • Custom user metadata and claims

Need help implementing secure authentication for your Umbraco project? Contact KMS ITC for expert guidance on identity and access management.