Skip to main content

Overview

The Identity module provides comprehensive authentication and authorization features built on ASP.NET Core Identity. It handles users, roles, permissions, groups, and sessions with JWT-based authentication. Module Order: 100 (loads first) API Base Path: /api/v1/identity

Core Features

Users

Registration, profiles, status management, and password policies

Roles & Permissions

Role-based access control with granular permissions

Groups

User groups with inherited permissions

Sessions

JWT token management with refresh and revocation

Implementation

The module is defined in IdentityModule.cs (identity/Modules.Identity):
namespace FSH.Modules.Identity;

public class IdentityModule : IModule
{
    public void ConfigureServices(IHostApplicationBuilder builder)
    {
        var services = builder.Services;
        
        // Current user context
        services.AddScoped<ICurrentUserService, CurrentUserService>();
        services.AddScoped<ICurrentUser>(sp => sp.GetRequiredService<ICurrentUserService>());
        
        // User services - single-responsibility pattern
        services.AddTransient<IUserRegistrationService, UserRegistrationService>();
        services.AddTransient<IUserProfileService, UserProfileService>();
        services.AddTransient<IUserStatusService, UserStatusService>();
        services.AddTransient<IUserRoleService, UserRoleService>();
        services.AddTransient<IUserPasswordService, UserPasswordService>();
        services.AddTransient<IUserPermissionService, UserPermissionService>();
        
        // Role and token services
        services.AddTransient<IRoleService, RoleService>();
        services.AddTransient<ITokenService, TokenService>();
        
        // Password policy and history
        services.Configure<PasswordPolicyOptions>(builder.Configuration.GetSection("PasswordPolicy"));
        services.AddScoped<IPasswordHistoryService, PasswordHistoryService>();
        services.AddScoped<IPasswordExpiryService, PasswordExpiryService>();
        
        // Session management
        services.AddScoped<ISessionService, SessionService>();
        services.AddHostedService<SessionCleanupHostedService>();
        
        // Groups and group-derived permissions
        services.AddScoped<IGroupRoleService, GroupRoleService>();
        
        // Database and eventing
        services.AddHeroDbContext<IdentityDbContext>();
        services.AddEventingCore(builder.Configuration);
        services.AddEventingForDbContext<IdentityDbContext>();
        
        // ASP.NET Core Identity
        services.AddIdentity<FshUser, FshRole>(options =>
        {
            options.Password.RequiredLength = 8;
            options.Password.RequireDigit = false;
            options.Password.RequireLowercase = false;
            options.Password.RequireNonAlphanumeric = false;
            options.Password.RequireUppercase = false;
            options.User.RequireUniqueEmail = true;
        })
        .AddEntityFrameworkStores<IdentityDbContext>()
        .AddDefaultTokenProviders();
        
        // JWT authentication
        services.ConfigureJwtAuth();
        
        // Health checks
        services.AddHealthChecks()
            .AddDbContextCheck<IdentityDbContext>(
                name: "db:identity",
                failureStatus: HealthStatus.Unhealthy);
    }
    
    public void MapEndpoints(IEndpointRouteBuilder endpoints)
    {
        var apiVersionSet = endpoints.NewApiVersionSet()
            .HasApiVersion(new ApiVersion(1))
            .ReportApiVersions()
            .Build();
            
        var group = endpoints
            .MapGroup("api/v{version:apiVersion}/identity")
            .WithTags("Identity")
            .WithApiVersionSet(apiVersionSet);
            
        // Map all endpoints...
    }
}

User Management

User Endpoints

MethodEndpointDescriptionPermission
POST/users/registerAdmin user registrationUsers.Create
POST/users/self-registerPublic self-registrationAnonymous
GET/usersList all usersUsers.View
GET/users/searchSearch usersUsers.View
GET/users/{id}Get user by IDUsers.View
GET/users/meGet current user profileAuthenticated
PUT/users/{id}Update userUsers.Update
DELETE/users/{id}Delete userUsers.Delete
POST/users/{id}/toggle-statusActivate/deactivateUsers.Update
POST/users/{id}/change-passwordChange passwordUsers.Update
POST/users/{id}/reset-passwordReset passwordUsers.Update
GET/users/{id}/permissionsGet user permissionsUsers.View

User Services

The module uses specialized services following single-responsibility principle:
public interface IUserRegistrationService
{
    Task<string> RegisterAsync(
        string email,
        string userName,
        string password,
        string? firstName,
        string? lastName,
        string? phoneNumber,
        CancellationToken cancellationToken);
}

Roles & Permissions

Role Endpoints

MethodEndpointDescriptionPermission
GET/rolesList all rolesRoles.View
GET/roles/{id}Get role by IDRoles.View
GET/roles/{id}/permissionsGet role permissionsRoles.View
POST/rolesCreate or update roleRoles.Create
PUT/roles/{id}/permissionsUpdate permissionsRoles.Update
DELETE/roles/{id}Delete roleRoles.Delete

User-Role Association

MethodEndpointDescriptionPermission
GET/users/{id}/rolesGet user’s rolesUsers.View
POST/users/{id}/rolesAssign roles to userUsers.Update

Permission System

Endpoints use .RequirePermission() for authorization:
group.MapGetUsersListEndpoint()
    .WithName("ListUsers")
    .WithSummary("List users")
    .RequirePermission(IdentityPermissionConstants.Users.View);
Permission constants (identity/Contracts/v1):
public static class IdentityPermissionConstants
{
    public static class Users
    {
        public const string View = "Permissions.Identity.Users.View";
        public const string Create = "Permissions.Identity.Users.Create";
        public const string Update = "Permissions.Identity.Users.Update";
        public const string Delete = "Permissions.Identity.Users.Delete";
    }
    
    public static class Roles
    {
        public const string View = "Permissions.Identity.Roles.View";
        public const string Create = "Permissions.Identity.Roles.Create";
        public const string Update = "Permissions.Identity.Roles.Update";
        public const string Delete = "Permissions.Identity.Roles.Delete";
    }
}

Groups

Groups allow organizing users and assigning permissions at the group level.

Group Endpoints

MethodEndpointDescriptionPermission
GET/groupsList all groupsGroups.View
GET/groups/{id}Get group by IDGroups.View
POST/groupsCreate groupGroups.Create
PUT/groups/{id}Update groupGroups.Update
DELETE/groups/{id}Delete groupGroups.Delete
GET/groups/{id}/membersGet group membersGroups.View
POST/groups/{id}/membersAdd users to groupGroups.Update
DELETE/groups/{id}/members/{userId}Remove user from groupGroups.Update
GET/users/{id}/groupsGet user’s groupsUsers.View

Group-Derived Permissions

Users inherit permissions from their groups via IGroupRoleService:
public interface IGroupRoleService
{
    Task<IEnumerable<string>> GetGroupPermissionsAsync(
        string userId, 
        CancellationToken cancellationToken);
}

Sessions

Session management provides JWT token tracking and revocation.

User Session Endpoints

MethodEndpointDescription
GET/sessions/myGet current user’s sessions
DELETE/sessions/{id}Revoke a session
DELETE/sessionsRevoke all sessions

Admin Session Endpoints

MethodEndpointDescriptionPermission
GET/users/{id}/sessionsGet user’s sessionsUsers.View
DELETE/users/{id}/sessions/{sessionId}Revoke user sessionUsers.Update
DELETE/users/{id}/sessionsRevoke all user sessionsUsers.Update

Session Cleanup

Expired sessions are automatically cleaned up by SessionCleanupHostedService:
services.AddScoped<ISessionService, SessionService>();
services.AddHostedService<SessionCleanupHostedService>();

Authentication

Token Endpoints

MethodEndpointDescriptionRate Limited
POST/tokensGenerate access tokenYes
POST/tokens/refreshRefresh access tokenYes

JWT Configuration

Configure JWT options in appsettings.json:
{
  "JwtOptions": {
    "Issuer": "fullstackhero",
    "Audience": "fullstackhero-api",
    "SigningKey": "your-256-bit-secret-key-min-32-chars",
    "AccessTokenMinutes": 30,
    "RefreshTokenDays": 7
  }
}
The SigningKey must be at least 32 characters long. Never commit secrets to source control.

JWT Options Model

public class JwtOptions : IValidatableObject
{
    public string Issuer { get; init; } = string.Empty;
    public string Audience { get; init; } = string.Empty;
    public string SigningKey { get; init; } = string.Empty;
    public int AccessTokenMinutes { get; init; } = 30;
    public int RefreshTokenDays { get; init; } = 7;
}

Password Policy

Enforce password history and expiry policies.

Configuration

{
  "PasswordPolicy": {
    "PasswordHistoryCount": 5,
    "PasswordExpiryDays": 90,
    "PasswordExpiryWarningDays": 14,
    "EnforcePasswordExpiry": true
  }
}

Password Policy Options

public class PasswordPolicyOptions
{
    /// <summary>Number of previous passwords to keep in history (prevent reuse)</summary>
    public int PasswordHistoryCount { get; set; } = 5;

    /// <summary>Number of days before password expires and must be changed</summary>
    public int PasswordExpiryDays { get; set; } = 90;

    /// <summary>Number of days before expiry to show warning to user</summary>
    public int PasswordExpiryWarningDays { get; set; } = 14;

    /// <summary>Set to false to disable password expiry enforcement</summary>
    public bool EnforcePasswordExpiry { get; set; } = true;
}

Services

public interface IPasswordHistoryService
{
    Task<bool> IsPasswordInHistoryAsync(
        string userId, 
        string password, 
        CancellationToken cancellationToken);
        
    Task AddPasswordToHistoryAsync(
        string userId, 
        string passwordHash, 
        CancellationToken cancellationToken);
}

Email Confirmation

MethodEndpointDescriptionRate Limited
POST/users/confirm-emailConfirm email addressYes

Database Context

The module uses IdentityDbContext for persistence:
public class IdentityDbContext : DbContext
{
    public DbSet<FshUser> Users { get; set; }
    public DbSet<FshRole> Roles { get; set; }
    public DbSet<IdentityUserRole<string>> UserRoles { get; set; }
    public DbSet<IdentityUserClaim<string>> UserClaims { get; set; }
    public DbSet<IdentityRoleClaim<string>> RoleClaims { get; set; }
    public DbSet<Group> Groups { get; set; }
    public DbSet<GroupMember> GroupMembers { get; set; }
    public DbSet<Session> Sessions { get; set; }
    public DbSet<PasswordHistory> PasswordHistory { get; set; }
}

Events

The Identity module publishes integration events for cross-module communication:
  • UserRegisteredEvent
  • UserDeletedEvent
  • UserStatusChangedEvent
  • RoleCreatedEvent
  • RolePermissionsUpdatedEvent
services.AddEventingCore(builder.Configuration);
services.AddEventingForDbContext<IdentityDbContext>();
services.AddIntegrationEventHandlers(typeof(IdentityModule).Assembly);

Metrics

The module includes Prometheus-compatible metrics:
services.AddSingleton<IdentityMetrics>();
Metrics tracked:
  • User registrations
  • Login attempts (success/failure)
  • Token generations
  • Password changes

Next Steps

Multitenancy Module

Learn about tenant isolation and provisioning

Creating Modules

Build your own custom modules

Build docs developers (and LLMs) love