Module
Authentication and authorization architecture.
Core.Auth provides JWT authentication, session management, claims-based permissions, and a complete admin API. Three packages cover backend services, API endpoints, and Blazor UI components.
Overview
The Auth module is split into three NuGet packages. Use only what you need — the base package provides services and abstractions, the API package adds REST endpoints, and the Components package provides ready-to-use Blazor pages.
| Package | Purpose | Key Contents |
|---|---|---|
| Sillium.Core.Auth | Core services and abstractions | IAuthService, ISessionService, IProfileService, CoreAuthDbContext, JWT token generation, claims transformation |
| Sillium.Core.Auth.Api | Minimal API endpoints | Login, refresh, profile, admin user/role/claim management endpoints |
| Sillium.Core.Auth.Components | Blazor Server UI | Login page, profile, sessions, admin pages, CoreAuthStateProvider, CoreTokenStore |
Backend Setup
Register Core.Auth services and map endpoints in your Program.cs. The module integrates with ASP.NET Core Identity and JWT Bearer authentication.
Service Registration
AddCoreAuth registers Identity, JWT authentication, session management, claims transformation, and all business services.
Signing Key
The JWT signing key must be at least 256 bits (32 bytes). Store it in user secrets or a vault — never commit it to source control.
Show code
Copy code
// Program.cs — Backend
services.AddCoreAuth(options =>
{
options.Jwt.SigningKey = builder.Configuration["Auth:SigningKey"]!;
options.Jwt.Issuer = "my-app";
options.Jwt.Audience = "my-api";
options.Jwt.AccessTokenExpiryMinutes = 60;
options.Jwt.RefreshTokenExpiryDays = 7;
options.Sessions.MaxSessionsPerUser = 10;
options.Identity.RequireConfirmedEmail = true;
options.Identity.Password.RequiredLength = 8;
options.Identity.Password.RequireUppercase = true;
options.Identity.Password.RequireDigit = true;
options.Identity.Lockout.MaxFailedAccessAttempts = 5;
options.Identity.Lockout.DefaultLockoutMinutes = 15;
});
// Register custom claim providers (optional)
services.AddScoped<IClaimDefinitionProvider, MyAppClaimProvider>();
services.AddCoreAuthPoliciesFromProviders();Endpoint Mapping
MapCoreAuthEndpoints creates all auth, profile, and admin endpoint groups under the configured base path.
Show code
Copy code
// Program.cs — Endpoint mapping
app.MapCoreAuthEndpoints(options =>
{
options.BasePath = "/api/v1";
options.AdminReadPolicy = "identity.read";
options.AdminWritePolicy = "identity.update";
options.AdminUsersCreatePolicy = "identity.users.create";
options.ProfileReadPolicy = "auth.profile.read";
options.ProfileUpdatePolicy = "auth.profile.update";
});Database
CoreAuthDbContext extends IdentityDbContext and adds the UserSessions table for multi-device session tracking.
Show code
Copy code
// Program.cs — Database
services.AddDbContext<CoreAuthDbContext>(options =>
options.UseNpgsql(
builder.Configuration.GetConnectionString("auth")));Authentication Flow
Core.Auth implements a stateful JWT flow with refresh token rotation. Access tokens are short-lived (default 60 min), refresh tokens are long-lived (default 7 days) and rotated on each use to prevent replay attacks.
Flow Sequence
The complete authentication lifecycle from login to token refresh.
Show code
Copy code
// 1. Client sends credentials
POST /auth/login { "email": "...", "password": "..." }
// 2. Server validates, returns tokens + creates session
{ "accessToken": "eyJ...", "refreshToken": "abc123..." }
// 3. Client stores tokens, sends Bearer on every request
GET /me → Authorization: Bearer eyJ...
// 4. Token expires → client uses refresh token
POST /auth/refresh { "refreshToken": "abc123..." }
// 5. Server rotates session, returns new token pair
{ "accessToken": "eyJ...(new)", "refreshToken": "def456..." }Public Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /auth/login | Email/password login, returns access + refresh tokens |
| POST | /auth/token | OAuth 2.0 Resource Owner Password flow |
| POST | /auth/refresh | Exchange refresh token for new token pair |
| POST | /auth/revoke | Revoke a refresh token (logout) |
| POST | /auth/confirm-email | Confirm email address with token |
| POST | /auth/resend-confirmation | Resend confirmation email |
| GET | /me | Get current user profile |
| PUT | /me/change-password | Change password (requires current password) |
JWT Configuration
Token generation and validation are configured through CoreAuthOptions. Access tokens use HMAC-SHA512 signing. Refresh tokens are cryptographically random (64-byte entropy) and stored as SHA256 hashes.
CoreJwtOptions
| Property | Type | Default | Description |
|---|---|---|---|
| SigningKey | string | "" | Base64-encoded HMAC-SHA512 signing key (min. 256 bits) |
| Issuer | string | "" | Token issuer claim (iss) |
| Audience | string | "" | Expected audience claim (aud) |
| AccessTokenExpiryMinutes | int | 60 | Access token lifetime in minutes |
| RefreshTokenExpiryDays | int | 7 | Refresh token lifetime in days |
CoreSessionOptions
| Property | Type | Default | Description |
|---|---|---|---|
| MaxSessionsPerUser | int | 10 | Maximum concurrent sessions per user |
| RefreshTokenExpiryDays | int | 7 | Session refresh token lifetime in days |
Security Features
HMAC-SHA512 token signing — SHA256 refresh token hashing — Zero clock skew on validation — Security stamp check invalidates tokens on password change — Session rotation on every refresh prevents replay attacks
Permission System
Authorization uses claims-based policies. Users receive claims directly or through role membership. The system supports custom claim providers for application-specific permissions and implements deny-override semantics.
Custom Claim Provider
Implement IClaimDefinitionProvider to register application-specific claims that automatically become authorization policies.
Show code
Copy code
// Define custom claims for your application
public class MyAppClaimProvider : IClaimDefinitionProvider
{
public IEnumerable<ClaimDefinition> GetDefinitions() =>
[
new("orders.read", "Orders", "Read orders"),
new("orders.create", "Orders", "Create orders"),
new("reports.view", "Reports", "View reports"),
];
}
// Registration in Program.cs
services.AddScoped<IClaimDefinitionProvider, MyAppClaimProvider>();
services.AddCoreAuthPoliciesFromProviders();
// Now usable as authorization policies:
// [Authorize(Policy = "orders.read")]
// or in Minimal API: .RequireAuthorization("orders.read")Default Policies
| Policy | Protects |
|---|---|
| identity.read | Admin: list and view users, roles, claims |
| identity.update | Admin: modify users, roles, claim assignments |
| identity.users.create | Admin: create new user accounts |
| auth.profile.read | User: read own profile and sessions |
| auth.profile.update | User: update own profile, change password |
Deny Override
An explicit deny claim always takes precedence over allow claims. If a user has both 'orders.read' (allow) and 'orders.read' (deny), access is denied. This enables fine-grained permission revocation.
Session Management
Core.Auth tracks active sessions per user with device fingerprinting. Each login creates a session entry. Sessions are limited per user (default 10) — when the limit is reached, the oldest session is evicted.
| Field | Type | Description |
|---|---|---|
| TokenHash | string | SHA256 hash of the refresh token |
| DeviceName | string | Extracted device name (e.g. 'Chrome on Windows') |
| UserAgent | string | Raw User-Agent header |
| IpAddress | string | Client IP address |
| LocationHint | string | Approximate location from IP |
| CreatedAt | DateTime | Session creation timestamp |
| LastSeenAt | DateTime | Last token refresh timestamp |
| ExpiresAt | DateTime | Session expiration timestamp |
Token Rotation
On every token refresh, the old refresh token is invalidated and a new one is issued. This prevents replay attacks — if an attacker captures a refresh token, it becomes invalid after the legitimate user refreshes.
Blazor Integration
The Components package provides a complete frontend auth stack for Blazor Server. It handles token storage, automatic refresh, and exposes the authentication state to Blazor's built-in authorization system.
Frontend Registration
AddCoreAuthComponents registers the auth state provider, token store, API client, and session client.
Show code
Copy code
// Program.cs — Blazor frontend
services.AddCoreAuthComponents(options =>
{
options.ApiBaseUrl = "https://api.example.com/api/v1";
});
builder.Services.AddCascadingAuthenticationState();| Service | Responsibility |
|---|---|
| CoreAuthStateProvider | Implements AuthenticationStateProvider — provides ClaimsPrincipal to AuthorizeView and cascading auth state |
| CoreTokenStore | Persists access and refresh tokens in browser LocalStorage |
| CoreAuthApiClient | Type-safe HTTP client for all auth endpoints with automatic token attachment |
| CoreAuthSessionClient | Handles session validation and automatic token refresh on API calls |
Admin API
The admin endpoints provide full CRUD for users, roles, and claims. All admin endpoints require authorization and are protected by configurable policies.
User Management
| Method | Path | Description |
|---|---|---|
| GET | /admin/identity/users | List all users with pagination |
| GET | /admin/identity/users/{id} | Get user details including roles and claims |
| POST | /admin/identity/users | Create a new user account |
| PUT | /admin/identity/users/{id} | Update user details, assign roles |
| DELETE | /admin/identity/users/{id} | Delete or anonymize a user account |
Role & Claim Management
| Method | Path | Description |
|---|---|---|
| GET | /admin/identity/roles | List all roles with assigned claims |
| POST | /admin/identity/roles | Create a new role |
| DELETE | /admin/identity/roles/{id} | Delete a role |
| GET | /admin/identity/claims | List all available claim definitions |
| PUT | /admin/identity/users/{id}/claims | Assign or revoke claims for a user |
Policy Configuration
Admin endpoint access is controlled by AdminReadPolicy and AdminWritePolicy in CoreAuthEndpointOptions. Configure these to match your application's admin role structure.