Skip to content

NetAuth is an educational ASP.NET Core API built with .NET 10, designed to learn and apply modern software architecture patterns including Domain-Driven Design (DDD), CQRS, Clean Architecture, RBAC with permission-based authorization, and the Transactional Outbox Pattern.

License

Notifications You must be signed in to change notification settings

hoc081098/netauth-ddd-cqrs-clean

Repository files navigation

NetAuth - ASP.NET Core Authentication Service

NetAuth is an educational ASP.NET Core authentication service built with .NET 10, designed to learn and apply modern software architecture patterns including Domain-Driven Design (DDD), CQRS, Clean Architecture, RBAC with permission-based authorization, and the Transactional Outbox Pattern.

Build & Test πŸ§ͺ codecov Hits


Table of Contents


I. πŸ—οΈ Architecture

NetAuth follows Clean Architecture principles with clear separation of concerns:

  • Domain Layer - Core business logic and entities (framework-agnostic)
  • Application Layer - Use cases, commands, queries, and business workflows
  • Infrastructure Layer - Technical implementations and external dependencies
  • Web.Api Layer - HTTP endpoints and API contracts

II. ✨ Features

  • βœ… User Registration & Authentication
  • βœ… JWT-based Authentication with access tokens
  • βœ… Refresh Token Rotation with automatic revocation on reuse detection
  • βœ… Device Binding for enhanced security
  • βœ… Permission-Based Authorization (RBAC with fine-grained permissions)
  • βœ… Audit Logging via domain events
  • βœ… Outbox Pattern for reliable event processing (batching, SKIP LOCKED, retry with max attempts)
  • βœ… Rate Limiting on authentication endpoints
  • βœ… Health Checks for database and Redis
  • βœ… OpenAPI/Swagger documentation
  • βœ… Hybrid Cache for permission lookups (memory + Redis)
  • βœ… API Versioning (v1, v2) with grouped endpoints

III. πŸ› οΈ Technology Stack

Core

  • .NET 10 with C# 14
  • ASP.NET Core Minimal APIs
  • Entity Framework Core 10 with PostgreSQL
  • MediatR 12 for CQRS
  • FluentValidation 12 for validation
  • LanguageExt 4 for functional programming (Either, Option)
  • Ardalis.GuardClauses for defensive programming
  • Humanizer for string transformations

Security

  • JWT Bearer Authentication (Microsoft.AspNetCore.Authentication.JwtBearer)
  • PBKDF2 for password hashing
  • Permission-based authorization

Database & Data Access

  • Npgsql for PostgreSQL
  • Dapper for raw SQL queries (Outbox)
  • EFCore.NamingConventions for snake_case naming

Caching

  • HybridCache for distributed caching with local cache fallback
  • StackExchange.Redis for Redis connectivity

Background Jobs

  • Quartz.NET 3 for scheduled jobs (Outbox processing)

API & Documentation

  • Asp.Versioning for API versioning (v1, v2)
  • Swashbuckle for Swagger/OpenAPI documentation
  • Microsoft.AspNetCore.OpenApi for OpenAPI support

Observability

  • Serilog for structured logging (Console/File/Seq)
  • AspNetCore.HealthChecks for health monitoring (PostgreSQL, Redis, UI)

Testing

  • xUnit for test framework
  • NSubstitute for mocking
  • Testcontainers for integration tests (PostgreSQL, Redis)
  • NetArchTest for architecture tests
  • LanguageExt.UnitTesting for Either/Option assertions
  • Coverlet for code coverage

IV. πŸš€ Getting Started

Prerequisites

  • .NET 10 SDK
  • PostgreSQL 16+
  • Redis 7+
  • Docker & Docker Compose (optional)

Running with Docker Compose

# Start PostgreSQL and Redis (compose.yaml)
docker compose up -d

# Apply database migrations
dotnet ef database update --project src/NetAuth/NetAuth.csproj --startup-project src/NetAuth

# Run the application
dotnet run --project src/NetAuth/NetAuth.csproj

Running Locally

# Update connection strings in appsettings.Development.json
# Or copy .env.example to .env and fill Jwt__SecretKey, connection strings, Seq URL, etc.
# Then run:
dotnet run --project src/NetAuth/NetAuth.csproj

The API will be available at:

  • HTTPS: https://localhost:7169
  • HTTP: http://localhost:5215
  • Swagger UI: https://localhost:7169/swagger

V. πŸ“ Project Structure

NetAuth/
β”œβ”€β”€ Domain/                    # Core business logic
β”‚   β”œβ”€β”€ Core/                 # Base classes and abstractions
β”‚   β”‚   β”œβ”€β”€ Abstractions/     # Interfaces (IAuditableEntity, ISoftDeletableEntity)
β”‚   β”‚   β”œβ”€β”€ Events/           # Domain event base classes
β”‚   β”‚   └── Primitives/       # Entity, AggregateRoot, ValueObject, DomainError
β”‚   β”œβ”€β”€ Users/                # User bounded context
β”‚   β”‚   β”œβ”€β”€ User.cs           # User aggregate root
β”‚   β”‚   β”œβ”€β”€ Email.cs          # Email value object
β”‚   β”‚   β”œβ”€β”€ Username.cs       # Username value object
β”‚   β”‚   β”œβ”€β”€ Password.cs       # Password value object
β”‚   β”‚   β”œβ”€β”€ RefreshToken.cs   # Refresh token entity
β”‚   β”‚   β”œβ”€β”€ Role.cs           # Role entity with permissions
β”‚   β”‚   └── UsersDomainErrors.cs  # Domain errors (static readonly fields)
β”‚   └── TodoItems/            # TodoItem bounded context
β”‚       β”œβ”€β”€ TodoItem.cs       # TodoItem aggregate root
β”‚       β”œβ”€β”€ TodoTitle.cs      # TodoTitle value object
β”‚       β”œβ”€β”€ TodoDescription.cs # TodoDescription value object
β”‚       └── TodoItemDomainErrors.cs  # Domain errors
β”œβ”€β”€ Application/              # Use cases and workflows
β”‚   β”œβ”€β”€ Abstractions/         # Application interfaces
β”‚   β”‚   β”œβ”€β”€ Authentication/   # Auth abstractions (IJwtProvider, IUserContext)
β”‚   β”‚   β”œβ”€β”€ Common/           # Common abstractions (IClock)
β”‚   β”‚   β”œβ”€β”€ Cryptography/     # Password hashing
β”‚   β”‚   β”œβ”€β”€ Data/             # Repository, UnitOfWork
β”‚   β”‚   └── Messaging/        # CQRS abstractions (ICommand, IQuery)
β”‚   β”œβ”€β”€ Core/
β”‚   β”‚   β”œβ”€β”€ Behaviors/        # MediatR pipeline behaviors (Validation, Logging)
β”‚   β”‚   β”œβ”€β”€ Exceptions/       # Application exceptions
β”‚   β”‚   └── Extensions/       # Extension methods
β”‚   β”œβ”€β”€ Users/                # User feature slices
β”‚   β”‚   β”œβ”€β”€ Login/            # Login command, handler, validator
β”‚   β”‚   β”œβ”€β”€ LoginWithRefreshToken/
β”‚   β”‚   β”œβ”€β”€ Register/         # Registration command, handler, validator
β”‚   β”‚   β”œβ”€β”€ SetUserRoles/     # Role management
β”‚   β”‚   β”œβ”€β”€ GetRoles/         # Query all roles
β”‚   β”‚   └── GetUserRoles/     # Query user's roles
β”‚   └── TodoItems/            # TodoItem feature slices
β”‚       β”œβ”€β”€ Create/           # Create todo item
β”‚       β”œβ”€β”€ Update/           # Update todo item
β”‚       β”œβ”€β”€ Complete/         # Mark as completed
β”‚       β”œβ”€β”€ MarkAsIncomplete/ # Undo completion
β”‚       └── Get/              # Query todo items
β”œβ”€β”€ Infrastructure/           # Technical implementations
β”‚   β”œβ”€β”€ Authentication/       # JWT provider, refresh token generator
β”‚   β”œβ”€β”€ Authorization/        # Permission service, policies
β”‚   β”œβ”€β”€ Configurations/       # EF Core entity configurations
β”‚   β”œβ”€β”€ Cryptography/         # Password hasher
β”‚   β”œβ”€β”€ Interceptors/         # EF Core interceptors (audit, soft delete)
β”‚   β”œβ”€β”€ Migrations/           # EF Core migrations
β”‚   β”œβ”€β”€ Outbox/               # Outbox pattern implementation
β”‚   └── Repositories/         # Repository implementations
└── Web.Api/                  # HTTP layer
    β”œβ”€β”€ Endpoints/            # Minimal API endpoints
    β”œβ”€β”€ ExceptionHandlers/    # Global exception handling
    β”œβ”€β”€ Extensions/           # API extensions
    └── OpenApi/              # OpenAPI configuration

VI. 🎯 Design Patterns & Principles

Domain-Driven Design (DDD)

  • Aggregates: User is the aggregate root managing RefreshTokens
  • Value Objects: Email, Username, Password with validation
  • Domain Events: UserCreatedDomainEvent, UserRolesChangedDomainEvent, RefreshTokenCreated/Rotated/ReuseDetected/DeviceMismatchDetected/ExpiredUsage/ChainCompromised
  • Domain Errors: Immutable error types using static readonly fields for performance

CQRS (Command Query Responsibility Segregation)

  • Commands: Operations that change state (Login, Register)
  • Queries: Read operations (future: GetUserProfile)
  • Handlers: Separate handler per command/query
  • Validation: FluentValidation in pipeline behavior

Clean Architecture

  • Dependency Rule: Dependencies point inward (Infrastructure β†’ Application β†’ Domain)
  • Framework Independence: Domain layer has no external dependencies
  • Testability: Clear boundaries enable easy unit testing

Functional Programming

  • Railway-Oriented Programming: Using Either<DomainError, T> for operations that can fail
  • Option Type: Using Option<T> for nullable values
  • Monadic Composition: Chaining operations with Bind, Map, MapAsync

Outbox Pattern

Ensures reliable event processing:

  1. Domain events saved as OutboxMessage in same transaction
  2. Quartz job processes messages on an interval (Outbox:Interval) with batch size and max attempts
  3. Uses FOR UPDATE SKIP LOCKED to avoid double processing
  4. Parallel publish with a capped degree of parallelism and bulk update of processed rows

VII. πŸ”’ Security Features

Password Security

  • PBKDF2 algorithm with 80,000 iterations (v1, salted, constant-time verify)
  • Unique random salt per password
  • Versioned storage format: v1.{iterations}.{salt}.{hash}

Refresh Token Security

  • Token Rotation: New token issued on every refresh
  • Reuse Detection: Automatic chain revocation on suspicious activity
  • Device Binding: Tokens bound to specific devices
  • Expiration: Configurable token lifetime (default config 7 days; development config shorter)
  • Audit Trail: Complete history via domain events

Authorization

  • Permission-Based: Fine-grained permissions (permission:resource:action)
  • Claims Transformation: Role permissions loaded and cached
  • Policy-Based: Custom authorization policies

VIII. πŸ“ Domain Errors Best Practice

All domain and validation errors use static readonly fields for optimal performance:

public static class UsersDomainErrors
{
    public static class Email
    {
        // βœ… CORRECT - static readonly field (single allocation)
        public static readonly DomainError InvalidFormat = new(
            code: "User.Email.InvalidFormat",
            message: "The email format is invalid.",
            type: DomainError.ErrorType.Validation);
        
        // ❌ WRONG - property (new allocation on every access)
        // public static DomainError InvalidFormat => new(...);
    }
}

Benefits:

  • Single allocation per error, no per-call allocations
  • Thread-safe by CLR static initialization guarantee
  • Clear, centralized error catalog

IX. πŸ§ͺ Testing

Test Structure

tests/
β”œβ”€β”€ UnitTests/                    # 459 tests
β”‚   β”œβ”€β”€ Domain/
β”‚   β”‚   β”œβ”€β”€ Core/Primitives/     # ValueObject, Entity, AggregateRoot, DomainError tests
β”‚   β”‚   β”œβ”€β”€ Users/               # Email, Username, Password, User, RefreshToken tests
β”‚   β”‚   └── TodoItems/           # TodoItem, TodoTitle, TodoDescription tests
β”‚   └── Application/
β”‚       β”œβ”€β”€ Core/                # ValidationError, DateTimeExtensions tests
β”‚       β”œβ”€β”€ Users/               # Login, Register, RefreshToken handlers & validators
β”‚       └── TodoItems/           # Create, Update, Complete, MarkAsIncomplete handlers & validators
β”‚
β”œβ”€β”€ IntegrationTests/            # 24 tests
β”‚   β”œβ”€β”€ Users/                   # Register, Login, RefreshToken, SetUserRoles
β”‚   └── TodoItems/               # Create, Complete, MarkAsIncomplete, Update, GetTodoItems
β”‚
└── ArchitectureTests/           # 6 tests
    └── LayerTest.cs             # Domain, Application, Infrastructure, WebApi layer rules

Unit Tests

  • Domain logic (value objects, entities, aggregates)
  • Command/query handlers with mocked dependencies (NSubstitute)
  • Validators with FluentValidation test helpers
  • Uses xUnit and LanguageExt.UnitTesting

Integration Tests

  • Full application pipeline with real database (PostgreSQL via Testcontainers)
  • Tests DI wiring, EF Core mappings, transaction behavior
  • Verifies outbox pattern: domain events stored in same transaction
  • Tests authorization and ownership validation
  • Uses WebApplicationFactory for realistic HTTP pipeline

Architecture Tests

  • Dependency rules enforcement (NetArchTest)
  • Layer isolation verification
  • Naming conventions

Running Tests

# Run all tests
dotnet test

# Run with coverage
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover

# Run specific test category
dotnet test --filter "FullyQualifiedName~UnitTests.Domain"

X. πŸ”§ Configuration

First-Time Setup (Required)

Before running the application, you must configure the JWT Secret Key. This key is required for generating and validating JWT tokens.

Option 1: Using User Secrets (Recommended for Development)

# Navigate to the project directory
cd src/NetAuth

# Initialize user secrets (if not already done)
dotnet user-secrets init

# Set the JWT Secret Key (minimum 32 characters)
dotnet user-secrets set "Jwt:SecretKey" "your-super-secret-key-here-minimum-32-characters-long"

Option 2: Using Environment Variables (Recommended for Production)

# Linux/macOS
export Jwt__SecretKey="your-super-secret-key-here-minimum-32-characters-long"

# Windows (PowerShell)
$env:Jwt__SecretKey="your-super-secret-key-here-minimum-32-characters-long"

# Windows (Command Prompt)
set Jwt__SecretKey=your-super-secret-key-here-minimum-32-characters-long

Option 3: Using Docker Compose

Add to your compose file (e.g., compose.yaml):

services:
  netauth:
    environment:
      - Jwt__SecretKey=${JWT_SECRET_KEY}

Then set the environment variable before running Docker Compose:

export JWT_SECRET_KEY="your-super-secret-key-here-minimum-32-characters-long"
docker compose up -d

⚠️ Security Notes:

  • Never commit the actual secret key to source control
  • Use a cryptographically secure random key (minimum 32 bytes / 256 bits for HS256)
  • Rotate keys periodically in production
  • Use different keys for each environment (dev, staging, production)

Configuration Reference

Key configuration sections in appsettings.json:

{
  "Jwt": {
    "SecretKey": "",
    "Issuer": "hoc081098",
    "Audience": "MyAppClients",
    "Expiration": "00:10:00",
    "RefreshTokenExpiration": "7.00:00:00"
  },
  "Outbox": {
    "Interval": "00:00:10",
    "BatchSize": 500,
    "MaxAttempts": 3,
    "CleanupRetention": "30.00:00:00",
    "CleanupBatchSize": 5000
  }
}

Development settings override JWT expirations (access: 1 hour, refresh: 2 hours) and include localhost connection strings for PostgreSQL and Redis.

XI. πŸ“š API Documentation

Visit /swagger for interactive API documentation.

Available in Development environment (enabled when ASPNETCORE_ENVIRONMENT=Development).

Key Endpoints (versioned)

Authentication

  • POST /v1/auth/register - Register new user
  • POST /v1/auth/login - Login with email/password
  • POST /v1/auth/refresh - Refresh access token

Replace v1 with v2 for the alternate API version.

Rate Limiting

Authentication endpoints are protected with rate limiting:

  • /auth/login: Sliding window 5 requests per 20s per IP
  • /auth/register: Sliding window 3 requests per minute per IP
  • /auth/refresh: Sliding window 20 requests per minute per IP
  • Global: Sliding window 100 requests per minute per IP for all other endpoints

XII. 🎯 Performance Considerations

  • Static readonly domain errors (zero allocation per access)
  • Outbox processor uses SKIP LOCKED + bulk updates + limited parallel publish
  • Permission caching via HybridCache (memory + Redis)
  • Rate limiting on auth endpoints and global limiter

XIII. πŸ“Š Observability

Health Checks

  • PostgreSQL database connectivity
  • Redis connectivity
  • DbContext health
  • Outbox backlog/processing health

Logging

  • Structured logging with Serilog
  • Correlation ID tracking for request tracing (X-Correlation-Id header)
  • Audit logging via domain events
  • Request/response logging with timing

XIV. πŸ›£οΈ Roadmap

βœ… Completed

  • Unit tests and architecture tests (465 tests: 459 Unit + 6 Architecture)
  • Integration tests for critical flows (24 tests: Users + TodoItems)
  • CI/CD pipeline with GitHub Actions
  • Correlation ID logging for request tracing
  • JWT SecretKey configuration with documentation
  • XML documentation for complex business logic
  • API versioning (v1, v2)

πŸ”„ In Progress / Planned

  • Implement user profile management
  • Add email verification
  • Implement password reset flow
  • Add account lockout after failed attempts
  • Implement MFA (Multi-Factor Authentication)
  • Add distributed tracing with OpenTelemetry
  • Add GraphQL endpoint
  • Add response compression and caching
  • Implement pagination and sorting

XV. πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.


Built with ❀️ using .NET 10 and Clean Architecture principles

About

NetAuth is an educational ASP.NET Core API built with .NET 10, designed to learn and apply modern software architecture patterns including Domain-Driven Design (DDD), CQRS, Clean Architecture, RBAC with permission-based authorization, and the Transactional Outbox Pattern.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •