DainnUser.PostgreSQL
9.0.3
dotnet add package DainnUser.PostgreSQL --version 9.0.3
NuGet\Install-Package DainnUser.PostgreSQL -Version 9.0.3
<PackageReference Include="DainnUser.PostgreSQL" Version="9.0.3" />
<PackageVersion Include="DainnUser.PostgreSQL" Version="9.0.3" />
<PackageReference Include="DainnUser.PostgreSQL" />
paket add DainnUser.PostgreSQL --version 9.0.3
#r "nuget: DainnUser.PostgreSQL, 9.0.3"
#:package DainnUser.PostgreSQL@9.0.3
#addin nuget:?package=DainnUser.PostgreSQL&version=9.0.3
#tool nuget:?package=DainnUser.PostgreSQL&version=9.0.3
DainnUser.PostgreSQL
A comprehensive, extensible user management library for ASP.NET Core APIs with support for multiple database providers, JWT authentication, two-factor authentication, role-based authorization, audit logging, and full observability.
Features
Core Functionality
- User Management: Registration, authentication, profile management, password reset
- ASP.NET Core Identity: Fully configured Identity system with customizable password, lockout, user, and sign-in options
- JWT Authentication: Access tokens and refresh tokens with configurable expiration
- Role-Based Authorization: Roles and permissions system with flexible assignment
- Two-Factor Authentication (2FA): TOTP-based 2FA using QR codes for authenticator apps
- Account Security: Login lockout, failed login attempt tracking, password complexity
- Audit Logging: Comprehensive audit trail for all user actions with device and IP tracking
- Device Tracking: Automatic device information detection (device name, type, browser, OS, IP address)
- Session Management: Active session tracking with device information for security monitoring
- Email Services: Extensible email service interface for notifications (password reset, email confirmation, etc.)
Database Support
- SQLite: Lightweight, file-based database (great for development)
- SQL Server: Full support for Microsoft SQL Server
- PostgreSQL: Native UUID support and advanced features
- MySQL: Full support via Pomelo provider
- InMemory: In-memory database for testing
Security Features
- ASP.NET Core Identity: Fully configurable Identity system with password policies, lockout settings, and sign-in requirements
- Rate Limiting: Configurable rate limiting for authentication endpoints
- Login Lockout: Automatic account lockout after failed login attempts (both Identity and application-level)
- Password Validation: Configurable password complexity requirements (length, characters, case, digits)
- CORS Support: Configurable Cross-Origin Resource Sharing
- HTTPS/HSTS: Optional enforcement of HTTPS and HSTS headers
- Antiforgery Tokens: CSRF protection support
- OAuth2 Authentication: External authentication with Google, Microsoft, Facebook, GitHub, and Apple
Observability
- OpenTelemetry: Full instrumentation for traces, metrics, and logs
- Serilog: Structured logging with OpenTelemetry integration
- Health Checks: Built-in health checks for database, JWT, and custom endpoints
- Metrics: Authentication metrics (login attempts, success/failure rates)
- Distributed Tracing: Request tracing across services
Developer Experience
- API Versioning: Built-in API versioning support with URL segment-based routing
- FluentValidation: Request validation using FluentValidation
- Event-Driven Architecture: Domain events for extensibility
- Exception Handling: Global exception handling middleware
- Extensibility: Service interfaces for custom implementations
- AutoMapper: DTO mapping support
Installation
Install via NuGet:
dotnet add package DainnUser.PostgreSQL
Or install locally:
cd DainnUser.PostgreSQL
dotnet pack
dotnet add ../YourProject/YourProject.csproj reference DainnUser.PostgreSQL --package-source ../DainnUser.PostgreSQL/bin/Debug
Quick Start
1. Configure Services
In your Program.cs:
using DainnUser.PostgreSQL.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Add user management with configuration
builder.AddDainnUserManagement(options =>
{
// Optional: Configure additional options
options.CustomUserServiceType = typeof(MyCustomUserService); // Optional: override default service
});
var app = builder.Build();
// Configure middleware pipeline
app.UseDainnUserManagementMiddleware();
app.MapControllers();
app.Run();
2. Configure appsettings.json
{
"DainnApplication": {
"Provider": "postgresql",
"ConnectionString": "Host=localhost;Database=userdb;Username=postgres;Password=postgres;Port=5432",
"JwtSecret": "YourSuperSecretKeyForJWTTokenGeneration12345678901234567890",
"ApplicationName": "MyApp",
"Enable2FA": true,
"AutoMigrateDatabase": true,
"SeedDefaultAdmin": true,
"DefaultAdminEmail": "admin@example.com",
"DefaultAdminPassword": "Admin@123!",
// When SeedDefaultAdmin is true, the following accounts are created:
// - Admin account: admin@example.com / Admin@123! (Admin role)
// - User account: user@example.com / User@123! (User role)
"JwtSettings": {
"AccessTokenHours": 1,
"RefreshTokenDays": 7
},
"SecuritySettings": {
"MaxFailedLoginAttempts": 5,
"LockoutDurationMinutes": 30,
"EnableRateLimiting": true,
"RateLimitRequestsPerMinute": 10
},
"CorsSettings": {
"Enabled": true,
"AllowedOrigins": ["http://localhost:3000", "https://myapp.com"],
"AllowedMethods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"AllowedHeaders": ["Content-Type", "Authorization"],
"AllowCredentials": true
},
"JwtValidation": {
"ValidateIssuer": true,
"ValidateAudience": true,
"ValidateLifetime": true,
"ValidateIssuerSigningKey": true,
"ValidIssuer": "MyApp",
"ValidAudience": "MyAppUsers",
"ClockSkew": "00:00:05"
},
"OAuth2Settings": {
"Google": {
"Enabled": true,
"ClientId": "YOUR_GOOGLE_CLIENT_ID",
"ClientSecret": "YOUR_GOOGLE_CLIENT_SECRET"
},
"Microsoft": {
"Enabled": false,
"ClientId": "YOUR_MICROSOFT_APPLICATION_ID",
"ClientSecret": "YOUR_MICROSOFT_CLIENT_SECRET"
},
"Facebook": {
"Enabled": false,
"AppId": "YOUR_FACEBOOK_APP_ID",
"AppSecret": "YOUR_FACEBOOK_APP_SECRET"
},
"GitHub": {
"Enabled": false,
"ClientId": "YOUR_GITHUB_CLIENT_ID",
"ClientSecret": "YOUR_GITHUB_CLIENT_SECRET"
},
"Apple": {
"Enabled": false,
"ClientId": "YOUR_APPLE_SERVICE_ID",
"KeyId": "YOUR_APPLE_KEY_ID",
"TeamId": "YOUR_APPLE_TEAM_ID",
"PrivateKeyPath": "path/to/AuthKey_XXXXXXXXXX.p8"
}
},
"IdentityOptions": {
"Password": {
"RequiredLength": 8,
"RequiredUniqueChars": 1,
"RequireNonAlphanumeric": true,
"RequireLowercase": true,
"RequireUppercase": true,
"RequireDigit": true
},
"Lockout": {
"AllowedForNewUsers": true,
"MaxFailedAccessAttempts": 5,
"DefaultLockoutTimeSpan": "00:30:00"
},
"User": {
"AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+",
"RequireUniqueEmail": true
},
"SignIn": {
"RequireConfirmedEmail": false,
"RequireConfirmedPhoneNumber": false,
"RequireConfirmedAccount": false
}
}
},
"OpenTelemetry": {
"Enabled": true,
"OtlpTracesEndpoint": "http://localhost:4317",
"ServiceName": "DainnUser.PostgreSQL",
"ServiceVersion": "2.0.0"
},
"Serilog": {
"Enabled": true,
"OtlpLogsEndpoint": "http://localhost:4318/v1/logs",
"MinimumLevel": {
"Default": "Information"
}
},
"ApiVersioning": {
"DefaultVersion": "1.0",
"AssumeDefaultVersionWhenUnspecified": true,
"ReportApiVersions": true
}
}
3. Create Controllers
Create controllers in your application project (not in the library). Example:
using Microsoft.AspNetCore.Mvc;
using DainnUser.PostgreSQL.Application.Dtos;
using DainnUser.PostgreSQL.Application.Interfaces;
using Microsoft.AspNetCore.Authorization;
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
public class AuthController : ControllerBase
{
private readonly IUserService _userService;
public AuthController(IUserService userService)
{
_userService = userService;
}
[HttpPost("register")]
public async Task<ActionResult<UserProfileDto>> Register([FromBody] RegisterDto dto)
{
var user = await _userService.RegisterAsync(dto);
return Ok(user);
}
[HttpPost("login")]
public async Task<ActionResult<TokenResponseDto>> Login([FromBody] LoginDto dto)
{
var token = await _userService.LoginAsync(dto);
return Ok(token);
}
[Authorize]
[HttpGet("me")]
public async Task<ActionResult<UserProfileDto>> GetProfile()
{
var userId = Guid.Parse(User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)!.Value);
var profile = await _userService.GetProfileAsync(userId);
return Ok(profile);
}
}
4. Register Controllers
In Program.cs, register controllers from your project:
builder.Services.AddControllers()
.AddApplicationPart(typeof(YourApp.Controllers.AuthController).Assembly);
Database Migrations
Creating Migrations
Migrations work with any configured database provider. To create and apply migrations:
# Navigate to your startup project (e.g., DainnUserManagement.Sample)
cd DainnUser.PostgreSQL.Sample
# Create a new migration
dotnet ef migrations add <MigrationName> --project ../DainnUserManagement --startup-project .
# Apply migrations
dotnet ef database update --project ../DainnUserManagement --startup-project .
Note: Migrations automatically adapt to the configured database provider. Simply update DainnApplication.Provider in appsettings.json to switch between providers.
Provider-Specific Migrations
For most providers, standard migrations work automatically. Migrations adapt to the configured database provider based on the DainnApplication.Provider setting in appsettings.json.
Database Seeding
When SeedDefaultAdmin is set to true in your configuration, the application will automatically seed the database with:
- Admin Role: Creates an "Admin" role with full access
- User Role: Creates a "User" role for standard users
- Admin Account:
- Email:
admin@example.com(configurable viaDefaultAdminEmail) - Password:
Admin@123!(configurable viaDefaultAdminPassword) - Role: Admin
- Email confirmed and active
- Email:
- User Account:
- Email:
user@example.com - Password:
User@123! - Role: User
- Email confirmed and active
- Email:
Both accounts are created only if they don't already exist. If accounts exist, the seeding process ensures they have the correct roles assigned.
Domain Model
Entities
- AppUser: Extended user entity with properties for full name, avatar, 2FA, login tracking, and timestamps
- AppRole: Role entity with permissions and timestamps
- Permission: Granular permissions that can be assigned to roles
- RolePermission: Many-to-many relationship between roles and permissions
- RefreshToken: Refresh tokens for JWT token renewal with device tracking (IP address, device name, browser, OS, device type)
- AuditLog: Comprehensive audit trail for all actions with device information (IP address, device name, browser, OS, device type, user agent)
Value Objects
- Custom value objects can be added to
Domain/ValueObjects/
Services
Core Services
- IUserService: User registration, authentication, profile management, password changes
- IAuthService: JWT token generation and validation
- IRoleService: Role and permission management
- IPermissionService: Permission operations
- ITwoFactorService: 2FA setup, verification, and QR code generation
- IAuditService: Audit logging
- IEmailService: Email notifications (implement your own or use the default)
Custom Services
All services implement interfaces, allowing you to provide custom implementations:
builder.AddDainnUserManagement(options =>
{
options.CustomUserServiceType = typeof(MyCustomUserService);
});
Your custom service must implement IUserService:
public class MyCustomUserService : IUserService
{
// Implement all interface methods
}
Events System
The library uses an event-driven architecture for extensibility. Domain events are published automatically:
- UserRegisteredEvent: Published when a new user registers
- UserLoggedInEvent: Published when a user logs in
- PasswordChangedEvent: Published when a user changes their password
- ProfileUpdatedEvent: Published when a user updates their profile
- RoleAssignedEvent: Published when a role is assigned to a user
- UserLockedEvent: Published when a user account is locked
Handling Events
Implement IEventHandler<T> to handle events:
public class UserRegisteredEventHandler : IEventHandler<UserRegisteredEvent>
{
public Task HandleAsync(UserRegisteredEvent @event, CancellationToken cancellationToken = default)
{
// Your custom logic here
return Task.CompletedTask;
}
}
// Register in DI
builder.Services.AddScoped<IEventHandler<UserRegisteredEvent>, UserRegisteredEventHandler>();
DTOs (Data Transfer Objects)
Authentication DTOs
- RegisterDto: User registration
- LoginDto: User login
- RefreshTokenDto: Token refresh
- TokenResponseDto: Authentication response with tokens
User DTOs
- UserProfileDto: User profile information
- UpdateProfileDto: Profile update request
- ChangePasswordDto: Password change request
- UserAdminDto: Admin view of user
Role & Permission DTOs
- RoleDto: Role information
- CreateRoleDto: Role creation
- AssignRoleDto: Role assignment
Two-Factor Authentication DTOs
- TwoFactorSetupDto: 2FA setup response
- Enable2FADto: Enable 2FA request
- Verify2FADto: 2FA verification
Other DTOs
- ForgotPasswordDto: Password reset request
- ResetPasswordDto: Password reset confirmation
- ConfirmEmailDto: Email confirmation
- LockUserDto: User lockout
Validation
All DTOs are validated using FluentValidation. Validators are automatically registered:
- RegisterDtoValidator: Validates registration requests
- LoginDtoValidator: Validates login requests
- ChangePasswordDtoValidator: Validates password changes
Security
Login Lockout
Automatic account lockout after failed login attempts. Configure via two settings:
Identity Lockout (ASP.NET Core Identity):
- Configure via
DainnApplication.IdentityOptions.Lockout.MaxFailedAccessAttempts - Lockout duration:
DainnApplication.IdentityOptions.Lockout.DefaultLockoutTimeSpan - Enable/disable:
DainnApplication.IdentityOptions.Lockout.AllowedForNewUsers
Application-Level Lockout (Custom Implementation):
- Configure via
DainnApplication.SecuritySettings.MaxFailedLoginAttempts - Lockout duration:
DainnApplication.SecuritySettings.LockoutDurationMinutes
Note: Both lockout mechanisms work together. Identity lockout is enforced by ASP.NET Core Identity, while application-level lockout provides additional custom tracking and business logic.
Rate Limiting
Rate limiting is enabled by default for authentication endpoints:
- Default: 10 requests per minute
- Configurable via
DainnApplication.SecuritySettings.RateLimitRequestsPerMinute - Applied to
/loginand/registerendpoints
Password Requirements
Password requirements are fully configurable via IdentityOptions.Password:
{
"DainnApplication": {
"IdentityOptions": {
"Password": {
"RequiredLength": 8,
"RequiredUniqueChars": 1,
"RequireNonAlphanumeric": true,
"RequireLowercase": true,
"RequireUppercase": true,
"RequireDigit": true
},
"Lockout": {
"AllowedForNewUsers": true,
"MaxFailedAccessAttempts": 5,
"DefaultLockoutTimeSpan": "00:30:00"
},
"User": {
"AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+",
"RequireUniqueEmail": true
},
"SignIn": {
"RequireConfirmedEmail": false,
"RequireConfirmedPhoneNumber": false,
"RequireConfirmedAccount": false
}
}
}
}
Default Requirements:
- Minimum length: 8 characters
- At least 1 unique character
- Must contain a non-alphanumeric character (e.g.,
!@#$%^&*) - Must contain a lowercase letter
- Must contain an uppercase letter
- Must contain a digit
All requirements can be customized or disabled as needed.
CORS
Configure CORS settings in DainnApplication.CorsSettings:
{
"DainnApplication": {
"CorsSettings": {
"Enabled": true,
"AllowedOrigins": ["http://localhost:3000"],
"AllowedMethods": ["GET", "POST", "PUT", "DELETE"],
"AllowedHeaders": ["Content-Type", "Authorization"],
"AllowCredentials": true
}
}
}
OAuth2 External Authentication
The library supports OAuth2 authentication with external providers: Google, Microsoft, Facebook, GitHub, and Apple.
Setup Google Sign-In
Get OAuth2 Credentials from Google:
- Go to Google Cloud Console
- Create a new project or select an existing one
- Enable the Google+ API
- Create OAuth2 credentials (Web application)
- Add your callback URL:
https://yourdomain.com/api/v1/oauth2/google/callback
Configure in appsettings.json:
{
"OAuth2Settings": {
"Google": {
"Enabled": true,
"ClientId": "YOUR_GOOGLE_CLIENT_ID",
"ClientSecret": "YOUR_GOOGLE_CLIENT_SECRET"
}
}
}
- Use the endpoints:
- Challenge:
GET /api/v1/oauth2/google/challenge- Redirects to Google login - Callback:
GET /api/v1/oauth2/google/callback- Handles the callback and returns JWT tokens
- Challenge:
Setup Microsoft Sign-In
Get OAuth2 Credentials from Microsoft:
- Go to Microsoft Azure Portal
- Navigate to Azure Active Directory > App registrations
- Create a new registration or select an existing one
- Generate a client secret
- Add redirect URI:
https://yourdomain.com/api/v1/oauth2/microsoft/callback
Configure in appsettings.json:
{
"OAuth2Settings": {
"Microsoft": {
"Enabled": true,
"ClientId": "YOUR_MICROSOFT_APPLICATION_ID",
"ClientSecret": "YOUR_MICROSOFT_CLIENT_SECRET"
}
}
}
- Use the endpoints:
- Challenge:
GET /api/v1/oauth2/microsoft/challenge - Callback:
GET /api/v1/oauth2/microsoft/callback
- Challenge:
Setup Facebook Sign-In
Get OAuth2 Credentials from Facebook:
- Go to Facebook Developers
- Create a new app or select an existing one
- Add Facebook Login product
- Set Valid OAuth Redirect URIs:
https://yourdomain.com/api/v1/oauth2/facebook/callback - Get App ID and App Secret
Configure in appsettings.json:
{
"OAuth2Settings": {
"Facebook": {
"Enabled": true,
"AppId": "YOUR_FACEBOOK_APP_ID",
"AppSecret": "YOUR_FACEBOOK_APP_SECRET"
}
}
}
- Use the endpoints:
- Challenge:
GET /api/v1/oauth2/facebook/challenge - Callback:
GET /api/v1/oauth2/facebook/callback
- Challenge:
Setup GitHub Sign-In
Get OAuth2 Credentials from GitHub:
- Go to GitHub Settings > Developer settings > OAuth Apps
- Create a new OAuth App
- Set Authorization callback URL:
https://yourdomain.com/api/v1/oauth2/github/callback - Get Client ID and Client Secret
Configure in appsettings.json:
{
"OAuth2Settings": {
"GitHub": {
"Enabled": true,
"ClientId": "YOUR_GITHUB_CLIENT_ID",
"ClientSecret": "YOUR_GITHUB_CLIENT_SECRET"
}
}
}
- Use the endpoints:
- Challenge:
GET /api/v1/oauth2/github/challenge - Callback:
GET /api/v1/oauth2/github/callback
- Challenge:
Setup Apple Sign-In
Note: Apple Sign-In requires additional configuration and currently requires a third-party library like AspNet.Security.OAuth.Apple.
Get credentials from Apple Developer:
- Enroll in the Apple Developer Program
- Create an App ID with "Sign in with Apple" capability
- Create a Services ID and configure return URLs
- Generate a private key (download the
.p8file)
Configure in appsettings.json:
{
"OAuth2Settings": {
"Apple": {
"Enabled": true,
"ClientId": "com.example.app", // Your Service ID
"KeyId": "KEYID123", // Your Key ID
"TeamId": "TEAMID123", // Your Team ID
"PrivateKeyPath": "path/to/AuthKey.p8"
}
}
}
Note: For production Apple Sign-In, install the AspNet.Security.OAuth.Apple NuGet package and configure it in your ServiceExtensions.cs.
Using OAuth2 Authentication
The OAuth2 controller automatically handles:
- Creating new user accounts from external providers
- Linking external logins to existing accounts
- Generating JWT tokens for authenticated users
Response format:
{
"isNewUser": true,
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "refresh_token_here",
"expiresIn": 3600
},
"user": {
"id": "user-guid",
"email": "user@example.com",
"fullName": "John Doe",
"avatarUrl": "https://...",
"isActive": true,
"twoFactorEnabled": false
}
}
Observability
OpenTelemetry
Full OpenTelemetry instrumentation is included:
- Traces: Request tracing with ASP.NET Core and EF Core instrumentation
- Metrics: Custom metrics for authentication operations
- Logs: Structured logging via Serilog with OTLP export
Configure in appsettings.json:
{
"OpenTelemetry": {
"Enabled": true,
"OtlpTracesEndpoint": "http://localhost:4317",
"ServiceName": "DainnUser.PostgreSQL",
"ServiceVersion": "2.0.0",
"EnableAspNetCoreInstrumentation": true,
"EnableHttpClientInstrumentation": true,
"EnableEntityFrameworkCoreInstrumentation": true,
"EnablePrometheusExporter": true
}
}
Health Checks
Built-in health checks:
- Database Health Check: Checks database connectivity
- JWT Health Check: Validates JWT configuration
- Custom Endpoints: Add your own health checks
Access health checks:
- Ready:
/health/ready - Live:
/health/live
Metrics
Authentication metrics are automatically collected:
- Login attempts (success/failure)
- Registration attempts
- Token refresh operations
API Versioning
The library includes built-in API versioning support:
- URL Segment-based: Routes use
v{version:apiVersion}(e.g.,/api/v1/auth/login) - Default Version: Configurable default version
- Version Discovery: Automatic version reporting
Configure in appsettings.json:
{
"ApiVersioning": {
"DefaultVersion": "1.0",
"AssumeDefaultVersionWhenUnspecified": true,
"ReportApiVersions": true,
"EnableUrlSegmentReader": true
}
}
Configuration Reference
UserManagementOptions
| Property | Type | Default | Description |
|---|---|---|---|
Provider |
string | Required | Database provider: sqlite, sqlserver, postgresql, mysql, inmemory |
ConnectionString |
string | Required | Database connection string |
JwtSecret |
string | Required | Secret key for JWT token signing |
ApplicationName |
string | "DainnUserManagement" |
Application name for 2FA QR codes |
Enable2FA |
bool | false |
Enable two-factor authentication |
AutoMigrateDatabase |
bool | false |
Automatically apply migrations on startup |
SeedDefaultAdmin |
bool | false |
Seed default admin and user accounts with roles |
DefaultAdminEmail |
string | "admin@example.com" |
Default admin email |
DefaultAdminPassword |
string | "Admin@123!" |
Default admin password |
EnforceHttps |
bool | false |
Enforce HTTPS redirection |
EnableHsts |
bool | false |
Enable HSTS headers |
JwtSettings |
JwtSettings | - | JWT token configuration |
SecuritySettings |
SecuritySettings | - | Security settings (lockout, rate limiting) |
CorsSettings |
CorsSettings | - | CORS configuration |
JwtValidation |
JwtValidationOptions | - | JWT validation options |
OAuth2Settings |
OAuth2Settings | - | OAuth2 external authentication configuration |
IdentityOptions |
IdentityConfigurationOptions | - | ASP.NET Core Identity configuration (password, lockout, user, sign-in) |
CustomUserServiceType |
Type? | null |
Custom user service implementation |
JwtSettings
| Property | Type | Default | Description |
|---|---|---|---|
AccessTokenHours |
int | 1 |
Access token expiration in hours |
RefreshTokenDays |
int | 7 |
Refresh token expiration in days |
SecuritySettings
| Property | Type | Default | Description |
|---|---|---|---|
MaxFailedLoginAttempts |
int | 5 |
Max failed login attempts before lockout |
LockoutDurationMinutes |
int | 30 |
Account lockout duration in minutes |
EnableRateLimiting |
bool | true |
Enable rate limiting |
RateLimitRequestsPerMinute |
int | 10 |
Rate limit for auth endpoints |
CorsSettings
| Property | Type | Default | Description |
|---|---|---|---|
Enabled |
bool | false |
Enable CORS |
AllowedOrigins |
List<string> | [] |
Allowed origins |
AllowedMethods |
List<string> | ["GET","POST","PUT","DELETE","OPTIONS"] |
Allowed HTTP methods |
AllowedHeaders |
List<string> | ["Content-Type","Authorization","X-Requested-With"] |
Allowed headers |
AllowCredentials |
bool | true |
Allow credentials |
OAuth2Settings
| Property | Type | Default | Description |
|---|---|---|---|
Google |
GoogleOAuth2Settings | - | Google OAuth2 configuration |
Microsoft |
MicrosoftOAuth2Settings | - | Microsoft OAuth2 configuration |
Facebook |
FacebookOAuth2Settings | - | Facebook OAuth2 configuration |
GitHub |
GitHubOAuth2Settings | - | GitHub OAuth2 configuration |
Apple |
AppleOAuth2Settings | - | Apple OAuth2 configuration |
GoogleOAuth2Settings
| Property | Type | Default | Description |
|---|---|---|---|
Enabled |
bool | false |
Enable Google authentication |
ClientId |
string | "" |
Google OAuth2 client ID |
ClientSecret |
string | "" |
Google OAuth2 client secret |
MicrosoftOAuth2Settings
| Property | Type | Default | Description |
|---|---|---|---|
Enabled |
bool | false |
Enable Microsoft authentication |
ClientId |
string | "" |
Microsoft OAuth2 application ID |
ClientSecret |
string | "" |
Microsoft OAuth2 client secret |
FacebookOAuth2Settings
| Property | Type | Default | Description |
|---|---|---|---|
Enabled |
bool | false |
Enable Facebook authentication |
AppId |
string | "" |
Facebook OAuth2 App ID |
AppSecret |
string | "" |
Facebook OAuth2 App Secret |
GitHubOAuth2Settings
| Property | Type | Default | Description |
|---|---|---|---|
Enabled |
bool | false |
Enable GitHub authentication |
ClientId |
string | "" |
GitHub OAuth2 Client ID |
ClientSecret |
string | "" |
GitHub OAuth2 Client Secret |
AppleOAuth2Settings
| Property | Type | Default | Description |
|---|---|---|---|
Enabled |
bool | false |
Enable Apple authentication |
ClientId |
string | "" |
Apple OAuth2 Service ID |
KeyId |
string | "" |
Apple OAuth2 key ID |
TeamId |
string | "" |
Apple OAuth2 team ID |
PrivateKeyPath |
string | "" |
Path to Apple private key file (.p8) |
IdentityConfigurationOptions
| Property | Type | Default | Description |
|---|---|---|---|
Password |
PasswordOptions | - | Password requirements configuration |
Lockout |
LockoutOptions | - | Account lockout configuration |
User |
UserOptions | - | User validation configuration |
SignIn |
SignInOptions | - | Sign-in requirements configuration |
PasswordOptions
| Property | Type | Default | Description |
|---|---|---|---|
RequiredLength |
int | 8 |
Minimum password length |
RequiredUniqueChars |
int | 1 |
Minimum number of unique characters required |
RequireNonAlphanumeric |
bool | true |
Require at least one non-alphanumeric character |
RequireLowercase |
bool | true |
Require at least one lowercase letter |
RequireUppercase |
bool | true |
Require at least one uppercase letter |
RequireDigit |
bool | true |
Require at least one digit |
LockoutOptions
| Property | Type | Default | Description |
|---|---|---|---|
AllowedForNewUsers |
bool | true |
Allow lockout for newly created users |
MaxFailedAccessAttempts |
int | 5 |
Maximum failed login attempts before lockout |
DefaultLockoutTimeSpan |
TimeSpan | 00:30:00 |
Duration of lockout after max failed attempts (format: HH:mm:ss) |
UserOptions
| Property | Type | Default | Description |
|---|---|---|---|
AllowedUserNameCharacters |
string | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+" |
Characters allowed in usernames |
RequireUniqueEmail |
bool | true |
Require unique email addresses across all users |
SignInOptions
| Property | Type | Default | Description |
|---|---|---|---|
RequireConfirmedEmail |
bool | false |
Require confirmed email before sign-in |
RequireConfirmedPhoneNumber |
bool | false |
Require confirmed phone number before sign-in |
RequireConfirmedAccount |
bool | false |
Require confirmed account before sign-in |
Database Provider Details
SQLite
{
"Provider": "sqlite",
"ConnectionString": "Data Source=app.db"
}
SQL Server
{
"Provider": "sqlserver",
"ConnectionString": "Server=localhost;Database=userdb;User Id=sa;Password=YourPassword;TrustServerCertificate=true;"
}
PostgreSQL
{
"Provider": "postgresql",
"ConnectionString": "Host=localhost;Database=userdb;Username=postgres;Password=postgres;Port=5432"
}
Note: PostgreSQL uses native UUID types for all Guid properties automatically.
MySQL
{
"Provider": "mysql",
"ConnectionString": "Server=localhost;Database=userdb;User Id=root;Password=password;"
}
InMemory
{
"Provider": "inmemory",
"ConnectionString": "InMemoryDb"
}
Extensibility
Custom User Service
Override the default UserService with your own implementation:
public class MyCustomUserService : IUserService
{
// Implement interface methods
}
builder.AddDainnUserManagement(options =>
{
options.CustomUserServiceType = typeof(MyCustomUserService);
});
Custom Email Service
Implement IEmailService:
public class MyEmailService : IEmailService
{
public Task SendEmailAsync(string to, string subject, string body, CancellationToken cancellationToken = default)
{
// Your email sending logic
return Task.CompletedTask;
}
}
// Register in DI
builder.Services.AddScoped<IEmailService, MyEmailService>();
Custom Event Handlers
Handle domain events for custom logic:
public class MyEventHandler : IEventHandler<UserRegisteredEvent>
{
public Task HandleAsync(UserRegisteredEvent @event, CancellationToken cancellationToken = default)
{
// Custom logic
return Task.CompletedTask;
}
}
// Register in DI
builder.Services.AddScoped<IEventHandler<UserRegisteredEvent>, MyEventHandler>();
Device Tracking
The library automatically detects and tracks device information from HTTP requests during authentication. This provides valuable security insights and allows you to monitor active sessions.
Automatic Device Detection
Device information is automatically extracted from the User-Agent header and stored during:
- User login
- Token refresh
- External OAuth2 authentication
- All audit log entries
Tracked Device Information
The following device information is automatically captured:
| Field | Description | Examples |
|---|---|---|
| DeviceName | Specific device model name | "iPhone 14 Pro", "Samsung Galaxy S21", "Mac", "Windows PC" |
| DeviceType | Device category | "Mobile", "Desktop", "Tablet" |
| Browser | Browser name | "Chrome", "Safari", "Firefox", "Edge" |
| OperatingSystem | Operating system | "iOS", "Android", "Windows 11", "macOS" |
| UserAgent | Raw User-Agent string | Full browser user agent header |
| IpAddress | Client IP address | IPv4/IPv6 address with proxy support |
Device Name Detection
The system intelligently detects device names from User-Agent strings:
iOS Devices:
- iPhone models: "iPhone 14 Pro", "iPhone 15", "iPhone 13 Pro Max", etc.
- iPad models: "iPad Pro", "iPad Air", "iPad mini"
- Falls back to generic "iPhone", "iPad" if specific model cannot be determined
Android Devices:
- Samsung Galaxy models: "Samsung Galaxy S21", "Samsung Galaxy Note", etc.
- Other Android device models when available in User-Agent
- Falls back to "Android Device" if model cannot be determined
Desktop Devices:
- macOS: "Mac" or "Mac (Apple Silicon)"
- Windows: "Windows PC"
- Linux: "{OS} Device"
Note: Desktop User-Agent strings don't typically contain specific laptop model names (e.g., "MacBook Pro"). For more accurate desktop device names, consider using client-side JavaScript or device fingerprinting libraries.
Where Device Information is Stored
1. RefreshTokens Table
Stores device information for active sessions:
IpAddress: IP address when the refresh token was createdUserAgent: Raw User-Agent stringDeviceName: Detected device nameDeviceType: Device categoryBrowser: Browser nameOperatingSystem: Operating system
Use this to:
- View all active sessions for a user
- Monitor suspicious logins from unknown devices
- Implement "active devices" management
- Display session lists (e.g., "Macbook Pro - Chrome" or "iPhone 14 Pro - Safari")
2. AuditLogs Table
Stores device information for all user actions:
IpAddress: IP address of the actionUserAgent: Raw User-Agent stringDeviceName: Detected device nameDeviceType: Device categoryBrowser: Browser nameOperatingSystem: Operating system
Use this to:
- Track login history with device information
- Investigate security incidents
- Audit user actions with context
- Generate reports on device usage
Querying Device Information
Get Active Sessions
var activeSessions = await context.RefreshTokens
.Where(rt => rt.UserId == userId && !rt.IsRevoked && rt.Expires > DateTime.UtcNow)
.Select(rt => new
{
DeviceName = rt.DeviceName,
DeviceType = rt.DeviceType,
Browser = rt.Browser,
OperatingSystem = rt.OperatingSystem,
IpAddress = rt.IpAddress,
Created = rt.Created,
LastUsed = rt.Expires
})
.ToListAsync();
Get Login History
var loginHistory = await context.AuditLogs
.Where(al => al.UserId == userId && al.Action == "Login")
.OrderByDescending(al => al.Timestamp)
.Select(al => new
{
DeviceName = al.DeviceName,
DeviceType = al.DeviceType,
Browser = al.Browser,
OperatingSystem = al.OperatingSystem,
IpAddress = al.IpAddress,
Timestamp = al.Timestamp
})
.ToListAsync();
Revoke Sessions from Specific Device
// Revoke all sessions from a specific device
var deviceTokens = await context.RefreshTokens
.Where(rt => rt.UserId == userId && rt.DeviceName == "iPhone 14 Pro")
.ToListAsync();
foreach (var token in deviceTokens)
{
token.IsRevoked = true;
}
await context.SaveChangesAsync();
Displaying Device Information
Example format for displaying active sessions:
Active Sessions:
- Macbook Pro - Chrome (Hanoi, Vietnam • Just now)
- iPhone 14 Pro - Safari (Ho Chi Minh, Vietnam • Active 2 hours ago)
The device information is automatically formatted as:
- Display Name:
{DeviceName} - {Browser} - Location: IP-based geolocation (if implemented)
- Last Activity: Time since last refresh token usage
Security Benefits
- Suspicious Login Detection: Identify logins from unknown or unusual devices
- Session Management: Allow users to view and manage active sessions
- Security Auditing: Track device patterns for security analysis
- Account Recovery: Identify trusted devices for account recovery
- Compliance: Maintain audit trails with device context
Testing
The library includes test infrastructure. See DainnUserManagement.Tests for examples.
For unit testing, use the InMemory database provider:
{
"Provider": "inmemory",
"ConnectionString": "TestDb"
}
Docker Support
The library is fully compatible with Docker. See docker-compose.yml for examples of:
- PostgreSQL setup
- SQL Server setup
- Observability stack (Prometheus, Grafana, Loki, Jaeger)
- OpenTelemetry Collector
Requirements
- .NET 9.0 or later
- ASP.NET Core 9.0 or later
- Entity Framework Core 9.0 or later
Database Provider Requirements
- SQLite: No additional packages
- SQL Server:
Microsoft.EntityFrameworkCore.SqlServer(included) - PostgreSQL:
Npgsql.EntityFrameworkCore.PostgreSQL(included) - MySQL:
Pomelo.EntityFrameworkCore.MySql(included)
License
[Specify your license]
Contributing
[Contributing guidelines]
Support
[Support information]
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net9.0
- AspNet.Security.OAuth.GitHub (>= 9.1.0)
- AspNetCore.HealthChecks.Uris (>= 9.0.0)
- AutoMapper.Extensions.Microsoft.DependencyInjection (>= 12.0.1)
- DainnCommon (>= 9.0.3)
- FluentValidation.AspNetCore (>= 11.3.1)
- Microsoft.AspNetCore.Authentication.Facebook (>= 9.0.0)
- Microsoft.AspNetCore.Authentication.Google (>= 9.0.0)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 9.0.0)
- Microsoft.AspNetCore.Authentication.MicrosoftAccount (>= 9.0.0)
- Microsoft.AspNetCore.Authorization (>= 9.0.0)
- Microsoft.AspNetCore.Identity.EntityFrameworkCore (>= 9.0.0)
- Microsoft.AspNetCore.Mvc.Versioning (>= 5.1.0)
- Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer (>= 5.1.0)
- Microsoft.EntityFrameworkCore.InMemory (>= 9.0.0)
- Microsoft.EntityFrameworkCore.Sqlite (>= 9.0.0)
- Microsoft.EntityFrameworkCore.SqlServer (>= 9.0.0)
- Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore (>= 9.0.0)
- Microsoft.Extensions.Logging.Console (>= 9.0.0)
- Microsoft.Extensions.Logging.Debug (>= 9.0.0)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 9.0.0)
- OpenTelemetry.Exporter.OpenTelemetryProtocol (>= 1.9.0)
- OpenTelemetry.Exporter.Prometheus.AspNetCore (>= 1.9.0-beta.1)
- OpenTelemetry.Extensions.Hosting (>= 1.9.0)
- OpenTelemetry.Instrumentation.AspNetCore (>= 1.9.0)
- OpenTelemetry.Instrumentation.EntityFrameworkCore (>= 1.0.0-beta.7)
- OpenTelemetry.Instrumentation.Http (>= 1.9.0)
- Otp.NET (>= 1.4.0)
- Pomelo.EntityFrameworkCore.MySql (>= 9.0.0)
- Serilog.AspNetCore (>= 8.0.2)
- Serilog.Enrichers.Span (>= 3.1.0)
- Serilog.Sinks.OpenTelemetry (>= 1.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.