This project implements a minimal IAM using Minimal API + CQRS (MinimalCqrs), organized by layers:
src/
MiniIAM.Api/ # Endpoints (Auth, Users, Roles)
Endpoints/
AuthEndpoints.cs
UsersEndpoints.cs
Extensions/
WebApplicationExtensions.cs
WebApplicationExtensions.Cqrs.cs
Swagger/
DefaultResponsesOperationFilter.cs
Program.cs
MiniIAM.Application/ # Use cases (Commands/Queries)
UseCases/
Auth/
LogInUser.cs
LogOutUser.cs
Users/
AddUser.cs
GetUser.cs
AddUserRole.cs
MiniIAM.Domain/ # Entities + DTOs
Abstractions/
Roles/
Users/
MiniIAM.Infrastructure/ # EF Core, Repositories, Auth, Caching
Auth/
Caching/
Cqrs/
Data/
Extensions/
MiniIAM.Shared/ # Extensions & utilities (AddMiniIamInfrastructure, UseMiniIamApi)
Extensions/
Middlewares/
tests/
MiniIAM.Tests/ # Test project (Unit + Integration)
Unit/ # Unit tests
Integration/ # Integration tests
- Minimal APIs with
MapGroup
and JWT protection: all routes are protected by default, exceptPOST /auth/login
andPOST /auth/logout
(anonymous allowed). - CQRS using
MinimalCqrs
(ICommandDispatcher.Dispatch
) to connect endpoints with use cases inMiniIAM.Application
. - EF Core InMemory for dev/test: configured in
AddMiniIamInfrastructure
(you can later switch to SQL Server). - Swagger/OpenAPI enabled in
Development
. - Tests with xUnit, FluentAssertions and WebApplicationFactory.
Prerequisites: .NET 9 SDK
cd src
dotnet restore
dotnet build
dotnet run --project MiniIAM.Api
cd src
dotnet restore
dotnet build
dotnet run --project MiniIAM.Api
Swagger: https://localhost:5001/swagger
(or whatever Kestrel port you see).
- Docker Desktop installed and running
- Docker Compose v2.0+ (included with Docker Desktop)
# Navigate to project root
cd D:\aviater-code-test\mini-iam-api
# Build and start the container
docker-compose up --build
# Or run in detached mode (background)
docker-compose up --build -d
# View logs
docker-compose logs -f
# Stop the container
docker-compose down
# Navigate to project root
cd /path/to/mini-iam-api
# Build and start the container
docker-compose up --build
# Or run in detached mode (background)
docker-compose up --build -d
# View logs
docker-compose logs -f
# Stop the container
docker-compose down
- Swagger UI:
http://localhost:3000/swagger
- Health Check:
http://localhost:3000/health
- Detailed Health:
http://localhost:3000/health/detailed
- API Base URL:
http://localhost:3000
# Build and start services
docker-compose up --build
# Start in background (detached mode)
docker-compose up -d
# View running containers
docker-compose ps
# View logs
docker-compose logs
# Follow logs in real-time
docker-compose logs -f
# Stop services
docker-compose down
# Stop and remove volumes
docker-compose down -v
# Rebuild without cache
docker-compose build --no-cache
# Restart services
docker-compose restart
If port 3000 is already in use, you can change it in docker-compose.yml
:
ports:
- "3001:80" # Change 3000 to 3001 or any available port
# Check container logs
docker-compose logs
# Check if port is available
netstat -an | grep :3000 # Windows
lsof -i :3000 # Linux/macOS
# Kill processes using the port (Windows)
netstat -ano | findstr :3000
taskkill /PID <PID> /F
# Remove all containers and images
docker-compose down --rmi all
# Remove unused Docker resources
docker system prune -a
# Rebuild from scratch
docker-compose up --build --force-recreate
# Test health endpoint
curl http://localhost:3000/health
# Test with PowerShell (Windows)
Invoke-RestMethod -Uri "http://localhost:3000/health" -Method GET
# Test login endpoint
curl -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "admin@demo.com", "password": "Demo@321"}'
POST /auth/login
— body:{ "email": "", "password": "" }
→ returns{ accessToken, refreshToken }
.POST /auth/logout
— headerAuthorization: Bearer <token>
.
GET /health
— basic health check, returns{ "status": "Healthy", "timestamp": "..." }
.GET /health/detailed
— detailed health check with all registered checks and their status.GET /health/ready
— readiness check, indicates if service is ready to accept requests.GET /health/live
— liveness check, indicates if service is alive.
POST /users
— create a user (protected).GET /users/{id}
— get user by ID (protected, cached for 15 minutes).GET /users/{id}/roles
— get user roles by user ID (protected, cached for 10 minutes).PUT /users/{id}/roles
— assign roles to user (protected, invalidates cache).
All user endpoints require authentication via JWT token in the Authorization
header.
# Run all tests from solution root
cd src
dotnet test
# Run with coverage
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
# Run all tests from solution root
cd src
dotnet test
# Run with coverage
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
# Run only unit tests
cd tests/MiniIAM.Tests
dotnet test --filter "Category=Unit" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
# Run only integration tests
dotnet test --filter "Category=Integration" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
# Run tests by namespace
dotnet test --filter "FullyQualifiedName~Unit" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
dotnet test --filter "FullyQualifiedName~Integration" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
# Run only unit tests
cd tests/MiniIAM.Tests
dotnet test --filter "Category=Unit" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
# Run only integration tests
dotnet test --filter "Category=Integration" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
# Run tests by namespace
dotnet test --filter "FullyQualifiedName~Unit" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
dotnet test --filter "FullyQualifiedName~Integration" /p:CollectCoverage=true /p:CoverletOutputFormat=lcov
- MiniIAM.Tests: Main test project containing both unit and integration tests
- Unit/: Unit tests for individual components (AuthService, Validators, etc.)
- Integration/: Integration tests for API endpoints and use cases
The provided tests exercise API, Use Case, and Infrastructure layers. Current test coverage target: ≥85%
- Unit Tests: Test individual components in isolation
AuthServiceTests
: JWT token generation and validationLogInUserValidatorTests
: Input validation for login requests
- Integration Tests: Test API endpoints and use cases
AuthAndUsersEndpointsTests
: Endpoint behavior and request/response validation- Tests for
GET /users/{id}
,POST /users
,PUT /users/{id}/roles
See src/MiniIAM.Api/MiniIAM.Api.http
for ready-to-run endpoint calls.
### Login
POST https://localhost:5001/auth/login
Content-Type: application/json
{
"email": "demo@aviater.com",
"password": "Demo@321"
}
### Get User by ID
GET https://localhost:5001/users/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
Authorization: Bearer <your-jwt-token>
### Get User Roles
GET https://localhost:5001/users/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/roles
Authorization: Bearer <your-jwt-token>
### Create User
POST https://localhost:5001/users
Authorization: Bearer <your-jwt-token>
Content-Type: application/json
{
"email": "user@example.com",
"name": "John Doe",
"password": "password123",
"byUserId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
}
### Assign Roles to User
PUT https://localhost:5001/users/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/roles
Authorization: Bearer <your-jwt-token>
Content-Type: application/json
{
"userId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
"rolesIds": [
"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
"cccccccc-cccc-cccc-cccc-cccccccccccc"
],
"byUserId": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
}
src/MiniIAM.Api/Dockerfile
builds & runs the API on ASP.NET 9.docker-compose.yml
exposes the API athttp://localhost:3000
.- The container includes all necessary dependencies and runs the application in production mode.
- Health checks are configured to monitor application status.
The application can be configured using environment variables:
Jwt__Key
— HMAC key to sign JWTs (required)Jwt__Issuer
— JWT issuer (default: "MiniIAM")Jwt__Audience
— JWT audience (default: "MiniIAMClients")Jwt__ExpireMinutes
— JWT expiration time in minutes (default: 20)ASPNETCORE_ENVIRONMENT
— Environment (Development/Production)ASPNETCORE_URLS
— URLs to bind to (default: "http://+:80")
The docker-compose.yml
file includes default environment variables:
environment:
- Jwt__Key=your-super-secret-jwt-key-here-must-be-at-least-32-characters
- Jwt__Issuer=MiniIAM
- Jwt__Audience=MiniIAMClients
- Jwt__ExpireMinutes=20
- ASPNETCORE_ENVIRONMENT=Production
To use your own configuration, create a .env
file in the project root:
JWT__KEY=your-custom-jwt-key-here
JWT__ISSUER=YourCompany
JWT__AUDIENCE=YourApp
JWT__EXPIRE_MINUTES=30
Then modify docker-compose.yml
to use the .env
file:
services:
miniiam-api:
env_file:
- .env
- Uses EF Core InMemory database
- Swagger UI enabled
- Detailed error messages
- Hot reload support when running locally
- Uses EF Core InMemory database (can be configured for SQL Server)
- Swagger UI disabled
- Optimized for performance
- Health checks enabled
- Structured logging with Serilog
To use SQL Server instead of InMemory database:
- Update
docker-compose.yml
:
services:
miniiam-api:
environment:
- ConnectionStrings__DefaultConnection=Server=db;Database=MiniIAM;User Id=sa;Password=YourPassword123!;TrustServerCertificate=true;
depends_on:
- db
db:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=YourPassword123!
ports:
- "1433:1433"
- Update
WebApplicationExtensions.cs
to use SQL Server provider instead of InMemory.
- Session Entity: Tracks user sessions with access/refresh tokens
- Session Expiration: Automatic cleanup of sessions older than 20 minutes
- JWT Claims: Session ID included in JWT tokens for tracking
- Logout: Proper session deactivation on logout
- Basic Health:
/health
- Simple health status - Detailed Health:
/health/detailed
- Comprehensive health information - Readiness:
/health/ready
- Service readiness check - Liveness:
/health/live
- Service liveness check
- 53 Tests: Comprehensive test suite covering all endpoints
- Unit Tests: Individual component testing
- Integration Tests: End-to-end API testing
- Health Check Tests: Dedicated health endpoint testing
- Session Tests: Session management functionality testing
- User Data Caching: GET /users/{id} endpoint with 15-minute TTL
- User Roles Caching: GET /users/{id}/roles endpoint with 10-minute TTL
- Cache Invalidation: Automatic cache cleanup on user updates
- Memory Cache Provider: In-memory caching for development
- Redis Cache Provider: Redis support for production environments
- Cache Management: Add, Get, Remove operations with TTL support