In modern enterprise multi-tenant B2B SaaS platforms, the Identity Subsystem is the central security backbone:
- One User, Multiple Tenants: A single user (e.g. corporate consultant or auditor) may belong to multiple tenant organizations with different roles and permissions in each;
- Enterprise Security Baseline: Defending against credential stuffing and weak passwords while providing out-of-the-box TOTP 2FA, operator impersonation, and enterprise SSO (SAML2 / OIDC);
- Stateless High-Throughput: Sub-millisecond login verifications with distributed token revocation and refresh mechanisms.
BitzOrcas.Modern Identity Module adopts Vertical Slice Architecture and multi-tenant domain isolation, delivering an impenetrable security foundation.
Identity Subsystem Interaction Topology
Step 1: Core Domain Entity UserAggregate
The user aggregate root adopts the Unified Aggregate pattern, mapping directly to SysUser:
using System;using System.Collections.Generic;using System.ComponentModel;using BitzOrcas.Domain.Entities;using BitzOrcas.Domain.Results;using BitzOrcas.Persistence.Metadata;
namespace BitzOrcas.Identity.Domain;
public static class UserErrors{ public static readonly Error InvalidEmail = Error.Validation("User.InvalidEmail", "Invalid email format.");}
[BitzTable("SysUser", IsTenant = false, IsSoftDelete = true, Description = "Global Platform Users")][BitzIndex("IX_SysUser_Email", nameof(Email), IsUnique = true)]public sealed class UserAggregate : AggregateRoot<string>{ [BitzColumn(Length = 120, IsRequired = true)] public string Email { get; private set; } = string.Empty;
[BitzColumn(Length = 200, IsRequired = true)] public string PasswordHash { get; private set; } = string.Empty;
[BitzColumn(Length = 50)] public string PhoneNumber { get; private set; } = string.Empty;
// Global user state: Active, Suspended, PendingActivation [BitzColumn(IsRequired = true)] public UserStatus Status { get; private set; } = UserStatus.PendingActivation;
// Declares whether TOTP 2FA is active [BitzColumn(IsRequired = true)] public bool IsTwoFactorEnabled { get; private set; }
// Multi-tenant membership collection private readonly List<UserTenantMembership> _memberships = []; public IReadOnlyCollection<UserTenantMembership> Memberships => _memberships.AsReadOnly();
[Obsolete("For ORM materialization only. Use Create.", error: true)] [EditorBrowsable(EditorBrowsableState.Never)] public UserAggregate() : base("0") { }
private UserAggregate(string id, string email, string passwordHash) : base(id) { Email = email; PasswordHash = passwordHash; Status = UserStatus.Active; }
// Domain factory method creating users public static Result<UserAggregate> Create(string email, string passwordHash, string initialTenantId) { // 1. In-memory invariant verification if (string.IsNullOrWhiteSpace(email) || !email.Contains('@')) { return Result<UserAggregate>.Failure(UserErrors.InvalidEmail); }
var user = new UserAggregate(Guid.NewGuid().ToString("N"), email.Trim().ToLowerInvariant(), passwordHash);
// 2. Bind initial tenant organization user._memberships.Add(new UserTenantMembership(initialTenantId, isDefault: true));
// 3. Emit user registered domain event user.AddDomainEvent(new UserRegisteredDomainEvent(user.Id, user.Email, initialTenantId));
return Result<UserAggregate>.Success(user); }}Step 2: Vertical Slice Use Case (LoginCommandHandler)
The login command is completely self-contained within its vertical slice:
using System.Threading;using System.Threading.Tasks;using BitzOrcas.Application.Abstractions.Security;using BitzOrcas.Domain.Results;using BitzOrcas.Identity.Contracts;
public static class AuthErrors{ public static readonly Error InvalidCredentials = Error.Unauthorized("Auth.InvalidCredentials", "Invalid email or password.");
public static readonly Error AccountSuspended = Error.Forbidden("Auth.AccountSuspended", "Account is suspended or pending activation.");}
public sealed class LoginCommandHandler( IUserRepository userRepository, IPasswordHasher passwordHasher, ITokenService tokenService){ public async ValueTask<Result<LoginResponseDto>> Handle(LoginCommand command, CancellationToken ct) { // 1. Retrieve user by canonical email var user = await userRepository.FindByEmailAsync(command.Email.ToLowerInvariant(), ct); if (user is null) { // Constant-time execution prevents timing attacks return Result<LoginResponseDto>.Failure(AuthErrors.InvalidCredentials); }
// 2. Verify password hash using Argon2id var isPasswordValid = passwordHasher.Verify(command.Password, user.PasswordHash); if (!isPasswordValid) { return Result<LoginResponseDto>.Failure(AuthErrors.InvalidCredentials); }
// 3. Check account lifecycle status if (user.Status != UserStatus.Active) { return Result<LoginResponseDto>.Failure(AuthErrors.AccountSuspended); }
// 4. Issue tenant-scoped JWT & Refresh Token pair var tokenPair = await tokenService.GenerateTokensAsync(user, command.TenantId, ct);
return Result<LoginResponseDto>.Success(new LoginResponseDto( AccessToken: tokenPair.AccessToken, RefreshToken: tokenPair.RefreshToken, ExpiresInSeconds: tokenPair.ExpiresIn)); }}Summary
The Identity Subsystem adheres to strict enterprise design standards:
- Security-First: Default Argon2id hashing prevents rainbow table exploits;
- Multi-Tenant Agility: Seamless switching between corporate tenants with a single identity;
- Event-Driven Decoupling: Lifecycle changes emit transactional events asynchronously.