Idmt.Plugin 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Idmt.Plugin --version 1.0.0
                    
NuGet\Install-Package Idmt.Plugin -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Idmt.Plugin" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Idmt.Plugin" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Idmt.Plugin" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Idmt.Plugin --version 1.0.0
                    
#r "nuget: Idmt.Plugin, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Idmt.Plugin@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Idmt.Plugin&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Idmt.Plugin&version=1.0.0
                    
Install as a Cake Tool

IDMT Plugin

An opinionated .NET 10 library for self-hosted identity management and multi-tenancy. Built on top of ASP.NET Core Identity and Finbuckle.MultiTenant, it exposes a complete set of Minimal API endpoints for authentication, user management, and tenant administration with minimal configuration.

Key features:

  • Dual authentication: cookie-based and bearer token (opaque), resolved automatically per request
  • Multi-tenancy via header, claim, route, or base-path strategies (Finbuckle.MultiTenant)
  • Vertical slice architecture — each endpoint is a self-contained handler
  • Per-IP fixed-window rate limiting on all auth endpoints
  • Token revocation on logout with background cleanup
  • Account lockout (5 failed attempts / 5-minute window)
  • PII masking in all structured log output
  • Audit logging on all entity mutations
  • Per-tenant cookie isolation and bearer token tenant validation
  • Security headers on every response

Quick Start

dotnet add package Idmt.Plugin
// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddIdmt<MyDbContext>(
    builder.Configuration,
    db => db.UseSqlServer(connectionString)
);

var app = builder.Build();

app.UseIdmt();
app.MapGroup("").MapIdmtEndpoints();

await app.EnsureIdmtDatabaseAsync();
await app.SeedIdmtDataAsync();

app.Run();

MyDbContext must extend IdmtDbContext. Call AddIdmt (without the generic parameter) to use the base context directly.


Configuration

{
  "Idmt": {
    "Application": {
      "ApiPrefix": "/api/v1",
      "ClientUrl": "https://myapp.com",
      "ConfirmEmailFormPath": "/confirm-email",
      "ResetPasswordFormPath": "/reset-password",
      "EmailConfirmationMode": "ClientForm"
    },
    "Identity": {
      "Password": {
        "RequireDigit": true,
        "RequireLowercase": true,
        "RequireUppercase": true,
        "RequireNonAlphanumeric": false,
        "RequiredLength": 8,
        "RequiredUniqueChars": 1
      },
      "SignIn": {
        "RequireConfirmedEmail": true
      },
      "Cookie": {
        "Name": ".Idmt.Application",
        "HttpOnly": true,
        "SameSite": "Strict",
        "ExpireTimeSpan": "14.00:00:00",
        "SlidingExpiration": true
      },
      "Bearer": {
        "BearerTokenExpiration": "01:00:00",
        "RefreshTokenExpiration": "30.00:00:00"
      },
      "ExtraRoles": []
    },
    "MultiTenant": {
      "DefaultTenantName": "System Tenant",
      "Strategies": ["header", "claim", "route"],
      "StrategyOptions": {
        "header": "__tenant__",
        "claim": "tenant",
        "route": "__tenant__"
      }
    },
    "Database": {
      "DatabaseInitialization": "Migrate"
    },
    "RateLimiting": {
      "Enabled": true,
      "PermitLimit": 10,
      "WindowInSeconds": 60
    }
  }
}

Key options:

  • ApiPrefix — URI prefix applied to all endpoint groups (/auth, /manage, /admin, /healthz). Set to "" to remove the prefix.
  • EmailConfirmationModeServerConfirm sends a GET link that confirms directly on the server; ClientForm sends a link to ClientUrl/ConfirmEmailFormPath for SPA handling (default).
  • DatabaseInitializationMigrate runs pending EF Core migrations (production default); EnsureCreated skips migrations (development/testing); None leaves schema management to the consumer.
  • Strategies — ordered list of tenant resolution strategies. Valid values: header, claim, route, basepath.
  • RateLimiting — per-IP fixed-window limiter applied to all /auth endpoints. Set Enabled: false to opt out.

API Reference

All endpoints are mounted under ApiPrefix (default /api/v1).

Authentication — /auth

Rate-limited. All endpoints are public except /auth/logout.

Method Path Auth Required Description
POST /auth/login - Cookie login. Returns { userId } and sets the auth cookie.
POST /auth/token - Bearer token login. Returns { accessToken, refreshToken, expiresIn, tokenType }.
POST /auth/logout Yes Signs out and revokes bearer token.
POST /auth/refresh - Exchange a refresh token for a new bearer token.
POST /auth/confirm-email - Confirm email address (Base64URL-encoded token in request body).
GET /auth/confirm-email - Direct server-side email confirmation via query string (tenantIdentifier, email, token). Used when EmailConfirmationMode is ServerConfirm.
POST /auth/resend-confirmation-email - Resend the confirmation email.
POST /auth/forgot-password - Send a password reset email.
POST /auth/reset-password - Reset password with a Base64URL-encoded token.

Login requests accept email or username, password, rememberMe, and optionally twoFactorCode / twoFactorRecoveryCode.

User Management — /manage

All endpoints require authentication.

Method Path Policy Description
GET /manage/info Authenticated Get the current user's profile.
PUT /manage/info Authenticated Update profile, email, or password.
POST /manage/users TenantManager Register a new user (invite flow — sends password-setup email).
PUT /manage/users/{id} TenantManager Activate or deactivate a user.
DELETE /manage/users/{id} TenantManager Soft-delete a user.

Administration — /admin

All endpoints require the RequireSysUser policy (SysAdmin or SysSupport role).

Method Path Description
POST /admin/tenants Create a new tenant.
DELETE /admin/tenants/{identifier} Soft-delete a tenant.
GET /admin/tenants List all tenants (paginated; query params: page, pageSize, max 100).
GET /admin/users/{id}/tenants List tenants accessible by a user.
POST /admin/users/{id}/tenants/{identifier} Grant a user access to a tenant.
DELETE /admin/users/{id}/tenants/{identifier} Revoke a user's access to a tenant.

Health — /healthz

Requires RequireSysUser. Returns database connectivity status via ASP.NET Core Health Checks.


Authorization Policies

Policy Roles
RequireSysAdmin SysAdmin
RequireSysUser SysAdmin, SysSupport
RequireTenantManager SysAdmin, SysSupport, TenantAdmin

Default roles seeded at startup: SysAdmin, SysSupport, TenantAdmin. Add custom roles via Identity.ExtraRoles in configuration.

The default authentication scheme (CookieOrBearer) routes to bearer token authentication when an Authorization: Bearer header is present, and falls back to cookie authentication otherwise.


Multi-Tenancy

Tenant resolution strategies are evaluated in the order they appear in Strategies. The first strategy that resolves a tenant wins.

Strategy Config key Default value
header StrategyOptions.header __tenant__
claim StrategyOptions.claim tenant
route StrategyOptions.route __tenant__
basepath

Authentication cookies are isolated per tenant — the cookie name includes the tenant identifier, preventing session leakage across tenants.

Route strategy example:

app.MapGroup("/{__tenant__}").MapIdmtEndpoints();

Bearer token tenant validation:

When using bearer tokens, a middleware (ValidateBearerTokenTenantMiddleware) validates that the tenant embedded in the token matches the resolved tenant context on every request.


Security

  • Per-IP fixed-window rate limiting on all /auth endpoints (configurable via RateLimiting)
  • SameSite=Strict cookies by default — browser never sends the auth cookie on cross-site requests; SameSiteMode.None is blocked and falls back to Strict
  • Security headers on every response: X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy
  • Token revocation on logout stored in the database; a background IHostedService periodically purges expired revoked tokens
  • Account lockout: 5 failed attempts triggers a 5-minute lockout
  • PII masking: email addresses and other sensitive values are masked in all structured log output
  • Audit logging on all entity create/update/delete operations
  • Per-tenant cookie isolation: each tenant gets a distinct cookie name
  • Bearer token tenant validation middleware on all authenticated bearer requests

Customization

Service registration hooks

builder.Services.AddIdmt<MyDbContext>(
    builder.Configuration,
    db => db.UseNpgsql(connectionString),
    options =>
    {
        options.Application.ApiPrefix = "/api/v2";
    },
    customizeAuth: auth =>
    {
        // Add additional authentication schemes
    },
    customizeAuthz: authz =>
    {
        // Add additional authorization policies
    }
);

Email delivery

The library registers a no-op IEmailSender<IdmtUser> by default and logs a startup warning when it is still active. Replace it with a real implementation before calling app.Run():

builder.Services.AddTransient<IEmailSender<IdmtUser>, MySmtpEmailSender>();

The sender is used for email confirmation, password reset, and the invite-based user registration flow.

Database seeding

Pass a custom seed delegate to SeedIdmtDataAsync to run additional seeding after the default system tenant is created:

await app.SeedIdmtDataAsync(async services =>
{
    var userManager = services.GetRequiredService<UserManager<IdmtUser>>();
    // seed initial admin user, etc.
});

OpenAPI / Swagger

IDMT does not configure OpenAPI. To expose the bearer token scheme in Swagger UI, register a document transformer in the host application:

builder.Services.AddOpenApi(options =>
{
    options.AddDocumentTransformer((document, context, ct) =>
    {
        document.Components ??= new OpenApiComponents();
        document.Components.SecuritySchemes["Bearer"] = new OpenApiSecurityScheme
        {
            Type = SecuritySchemeType.Http,
            Scheme = "bearer",
            BearerFormat = "opaque"
        };
        return Task.CompletedTask;
    });
});

Requirements

  • .NET 10
  • Any EF Core-supported database (SQL Server, PostgreSQL, SQLite, etc.)

Key dependencies:

Package Purpose
Finbuckle.MultiTenant Tenant resolution and per-tenant authentication
Microsoft.AspNetCore.Identity User, role, and sign-in management
ErrorOr Discriminated union error handling in handlers
FluentValidation Request validation
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 116 3/15/2026
1.0.0 80 3/6/2026
0.5.6 121 1/4/2026
0.5.5 107 1/3/2026
0.5.4 116 1/2/2026
0.5.3 102 1/2/2026
0.5.1 105 12/31/2025
0.5.0 110 12/31/2025
0.4.2 104 12/30/2025
0.4.1 106 12/27/2025
0.4.0 185 12/23/2025
0.3.0 184 12/22/2025
0.2.1 143 12/20/2025
0.2.0 287 12/18/2025
0.1.4 283 12/17/2025
0.1.3 281 12/17/2025
0.1.2 285 12/16/2025
0.1.1 287 12/15/2025
0.1.0 165 12/14/2025