When building modern web applications, securing your backend API and ensuring proper authorization are critical. In this post, we’ll guide you through integrating Auth0 with a Next.js frontend and a .NET 8 API to implement resource-based and role-based authorization.
Auth0 is a robust platform that makes it easy to implement authentication and authorization across your apps. By offloading authentication to Auth0, you can focus on building your app while using secure and scalable authentication services. Auth0 also provides built-in support for JWT tokens, user roles, and resource-based authorization, making it a great fit for modern, secure web apps.
http://localhost:3000/callback
http://localhost:3000
http://localhost:3000
https://my-api.com
.If you don’t have a .NET 8 project, create one:
dotnet new webapi -n Auth0ResourceBasedApi
cd Auth0ResourceBasedApi
In Program.cs
, configure your API to validate tokens from Auth0:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
var builder = WebApplication.CreateBuilder(args);
// Configure JWT Bearer authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = $"https://{builder.Configuration["Auth0:Domain"]}/";
options.Audience = builder.Configuration["Auth0:Audience"];
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = $"https://{builder.Configuration["Auth0:Domain"]}/",
ValidateAudience = true,
ValidAudience = builder.Configuration["Auth0:Audience"],
ValidateLifetime = true
};
});
builder.Services.AddAuthorization();
builder.Services.AddControllers();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
appsettings.json
In the appsettings.json
file, add your Auth0 domain and API identifier:
{
"Auth0": {
"Domain": "your-auth0-domain.us.auth0.com",
"Audience": "https://my-api.com"
}
}
Role-based authorization allows you to restrict API access based on user roles assigned in Auth0.
Admin
or User
, and assign these roles to users in your application.In your .NET 8 API, add role-based authorization to your controller actions using the [Authorize]
attribute with a specified role.
For example, protecting a specific endpoint to only allow users with the Admin
role:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class DocumentsController : ControllerBase
{
// Protect this endpoint for users with the "Admin" role
[HttpGet("{id}")]
[Authorize(Roles = "Admin")]
public IActionResult GetDocument(int id)
{
return Ok(new { id, content = "This is a protected document for Admins." });
}
}
Resource-based authorization ensures that only resource owners can access or modify specific resources.
Create a custom AuthorizationHandler to handle resource-based checks:
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;
public class ResourceOwnerRequirement : IAuthorizationRequirement { }
public class ResourceOwnerAuthorizationHandler : AuthorizationHandler<ResourceOwnerRequirement, Resource>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
ResourceOwnerRequirement requirement,
Resource resource)
{
var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
if (userId == resource.OwnerId)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
public class Resource
{
public string Id { get; set; }
public string OwnerId { get; set; }
}
Apply the custom authorization handler in the controller to protect specific resources:
[HttpGet("{id}")]
[Authorize(Policy = "ResourceOwner")]
public IActionResult GetDocument(string id)
{
var document = Documents.FirstOrDefault(d => d.Id == id);
if (document == null)
{
return NotFound();
}
var resource = new Resource { Id = document.Id, OwnerId = document.OwnerId };
return Ok(resource);
}
First, create your Next.js project and install the Auth0 SDK:
npx create-next-app@latest my-auth0-app
cd my-auth0-app
npm install @auth0/auth0-react
layout.tsx
To handle authentication, wrap your entire app with the Auth0Provider
component:
"use client";
import { Auth0Provider } from '@auth0/auth0-react';
import { useRouter } from 'next/navigation';
import { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
const router = useRouter();
const onRedirectCallback = (appState: any) => {
router.push(appState?.returnTo || '/');
};
return (
<Auth0Provider
domain="your-auth0-domain.us.auth0.com"
clientId="your-auth0-client-id"
authorizationParams={{
redirect_uri: window.location.origin,
audience: "https://my-api.com",
}}
onRedirectCallback={onRedirectCallback}
>
{children}
</Auth0Provider>
);
}
Use getAccessTokenSilently
from the Auth0 SDK to fetch a token and include it in your requests to the .NET 8 API.
Example for fetching protected resources:
"use client";
import { useAuth0 } from '@auth0/auth0-react';
import { useEffect, useState } from 'react';
export default function ProtectedApiCall() {
const { getAccessTokenSilently } = useAuth0();
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const token = await getAccessTokenSilently();
const response = await fetch('https://localhost:5001/api/documents/1', {
headers: {
Authorization: `Bearer ${token}`,
},
});
const result = await response.json();
setData(result);
} catch (error) {
console.error('Error fetching data:', error);
}
};
fetchData();
}, [getAccessTokenSilently]);
return (
<div>
<h1>Protected API Call</h1>
{data ? <pre>{JSON.stringify(data, null, 2)}</pre> : 'Loading...'}
</div>
);
}
In this post, we’ve built a secure Next.js frontend and .NET 8 API using Auth0 for authentication and both resource-based and role-based authorization. This setup ensures that:
By leveraging Auth0, Next.js, and .NET 8, you
can build secure applications with ease.
This setup should give you a solid foundation for securely handling authentication and authorization in your full-stack applications. Let me know if you need more details!