Passport
Registering Custom Extensions
Register custom permission resolvers and role providers through ASP.NET Core DI — control evaluation order, configure lifetimes, and understand short-circuit behavior.
Registration Methods
// Program.cs — register custom IAM extensions
builder.Services
.AddPassportAuthentication(opts => { /* ... */ })
// Custom permission resolvers — evaluated in registration order
.AddCustomPermissionResolver<ContractorRestrictionResolver>()
.AddCustomPermissionResolver<BusinessHoursResolver>()
.AddCustomPermissionResolver<ClearanceLevelResolver>()
// Custom role providers — all run, results merged
.AddCustomRoleProvider<ProjectRoleProvider>()
.AddCustomRoleProvider<OnCallRoleProvider>();
Extension Methods Signature
// AddCustomPermissionResolver — adds a resolver to the pipeline
public static IServiceCollection AddCustomPermissionResolver<TResolver>(
this IServiceCollection services,
ServiceLifetime lifetime = ServiceLifetime.Scoped)
where TResolver : class, ICustomPermissionResolver;
// AddCustomRoleProvider — adds a role provider
public static IServiceCollection AddCustomRoleProvider<TProvider>(
this IServiceCollection services,
ServiceLifetime lifetime = ServiceLifetime.Scoped)
where TProvider : class, ICustomRoleProvider;
Recommended Lifetimes
| Scenario | Lifetime | Reason |
|---|---|---|
| Resolver with HttpContext dependency | Scoped | HttpContext is scoped to the request |
| Resolver with IMemoryCache only | Singleton | IMemoryCache is singleton; resolver is stateless |
| Resolver with database access | Scoped | DbContext is scoped; opens a new connection per request |
| Resolver calling external HTTP API | Scoped or Singleton with IHttpClientFactory | HttpClient should use IHttpClientFactory pattern |
Conditional Registration
// Only register custom IAM in production or when a feature flag is set
if (builder.Environment.IsProduction())
{
builder.Services
.AddCustomPermissionResolver<ContractorRestrictionResolver>()
.AddCustomRoleProvider<ProjectRoleProvider>();
}
// Feature flag controlled registration
if (builder.Configuration.GetValue<bool>("Features:AdvancedIAM"))
{
builder.Services.AddCustomPermissionResolver<ClearanceLevelResolver>();
}
Complete Program.cs Example
var builder = WebApplication.CreateBuilder(args);
// Configure services
builder.Services
.AddHttpContextAccessor()
.AddMemoryCache()
// Passport authentication
.AddPassportAuthentication(opts =>
{
opts.Authority = builder.Configuration["Passport:Authority"];
opts.ClientId = builder.Configuration["Passport:ClientId"];
opts.ClientSecret= builder.Configuration["Passport:ClientSecret"];
})
.AddPassportClient()
// Custom IAM pipeline
.AddCustomPermissionResolver<ContractorRestrictionResolver>()
.AddCustomPermissionResolver<BusinessHoursResolver>()
.AddCustomRoleProvider<ProjectRoleProvider>()
// Dependencies used by custom resolvers
.AddScoped<IUserProfileService, UserProfileService>()
.AddScoped<IProjectRepository, ProjectRepository>()
// Authorization
.AddAuthorization(opts =>
{
opts.AddPolicy("Finance", p => p.RequireClaim("roles", "finance-manager", "admin"));
opts.AddPolicy("Admin", p => p.RequireClaim("roles", "admin"));
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Order Matters for Resolvers
The first resolver to return Allow or Deny wins — subsequent resolvers are not called. Register the most specific or restrictive resolver first. For example, register ContractorRestrictionResolver before DepartmentResolver — if the user is a contractor, deny immediately without needing the department check.