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.
KMS ITC
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
- Log into your Auth0 Dashboard
- Navigate to Applications > Applications > Create Application
- Select Regular Web Application
- 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
- Navigate to User Management > Roles
- Create roles that match your Umbraco member groups:
AdminEditorMember
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
- Login Flow: Click login → redirected to Auth0 → authenticate → returned to Umbraco
- Role Assignment: Verify roles from Auth0 map to Umbraco member groups
- Protected Content: Confirm unauthorized users cannot access restricted areas
- 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
- Always use HTTPS in production
- Rotate client secrets periodically
- Enable MFA in Auth0 for sensitive applications
- Use short token lifetimes and refresh tokens
- Implement proper logout that clears both local and Auth0 sessions
- 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.