Introduction
In microservices applications, different types of users often access the same backend service:
Back-Office users – admins, support staff
Transporter Panel users – drivers, fleet managers
For security, certain endpoints should be restricted only to Transporter users, and internal logic may require UserId and CompanyId from the JWT token.
This article demonstrates:
Method-level endpoint security for Transporter users
JWT authentication with claims
Attribute-based policies in ASP.NET Core
Accessing
UserIdandCompanyIdin controllers
JWT Token Structure
For Transporter users, the JWT might contain claims like:
{
"UserType": "Transporter",
"CompanyId": "1002",
"UserId": "89",
"sub": "1001",
"exp": 1710000000
}
UserTypedifferentiates roles (TransportervsBackOffice)CompanyIdidentifies the organizationUserIduniquely identifies the user
Step 1: Create TransporterWithCompany Attribute
We create a custom attribute to restrict access to Transporter users with a company:
using Microsoft.AspNetCore.Authorization;
namespace MyApp.API.Attributes
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class TransporterWithCompanyAttribute : AuthorizeAttribute
{
public TransporterWithCompanyAttribute()
{
Policy = "TransporterWithCompany";
}
}
}
This attribute can be applied per method, leaving other endpoints unrestricted.
Step 2: Configure JWT Authentication and Authorization
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using MyApp.API.Attributes;
var builder = WebApplication.CreateBuilder(args);
// Add controllers
builder.Services.AddControllers();
// Swagger
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// JWT Authentication
var secret = builder.Configuration["Jwt:Secret"];
if (string.IsNullOrWhiteSpace(secret))
throw new Exception("JWT Secret is missing in configuration.");
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)),
ClockSkew = TimeSpan.Zero
};
});
// Authorization policy for Transporter users with CompanyId
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("TransporterWithCompany", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireAssertion(context =>
{
var userType = context.User.FindFirst("UserType")?.Value;
var companyId = context.User.FindFirst("CompanyId")?.Value;
return userType == "Transporter" && !string.IsNullOrWhiteSpace(companyId);
});
});
});
// Middleware registration
builder.Services.AddScoped<TokenContextMiddleware>();
var app = builder.Build();
// Swagger
app.UseSwagger();
app.UseSwaggerUI();
// HTTPS
app.UseHttpsRedirection();
// Authentication & Authorization
app.UseAuthentication();
app.UseAuthorization();
// Middleware to inject UserId and CompanyId into HttpContext.Items
app.UseMiddleware<TokenContextMiddleware>();
app.MapControllers();
app.Run();
Join the conversation! Your thoughts help the community grow.