Get it here: fenixkit.dev
Ship faster. Build smarter.
A production-ready .NET Minimal API starter with Keycloak JWT authentication, MongoDB, and zero manual setup.
Keycloak JWT auth is the hardest part to get right in a new .NET API. Wrong token validation, missing role checks, broken Swagger login flows, no health check on the auth server — all fixable, all time-consuming. FenixKit Auth ships with all of it wired up from day one.
Keycloak runs out of the box. A pre-built realm with two test users, a registered client, and role mappings is imported automatically when the Docker stack starts. No Keycloak console setup required.
| Feature | Details |
|---|---|
| Keycloak Auth | JWT Bearer validation |
| Role-based policies | Authenticated and AdminOnly policies wired in from the start |
| Swagger OAuth2 PKCE | Authorize button in Swagger UI logs in via Keycloak — tokens injected automatically |
| Pre-built realm | realm-export.json imported at startup — two test users, one client, two roles |
| Keycloak health check | /health/ready includes Keycloak reachability via OIDC discovery |
| Auth example endpoints | /api/auth-examples/me and /api/auth-examples/admin — working patterns to copy |
| Minimal API | .NET 8 / .NET 10 — route grouping, no controllers, fast startup |
| MongoDB | MongoRepository via IDBRepository, singleton, health-checked |
| ErrorOr | Result pattern throughout — no exceptions for control flow |
| Offset + Cursor pagination | Both strategies included, pick the right one per endpoint |
| BaseRepository | 7 overridable hooks — extend CRUD without rewriting it |
| Global error handler | RFC 7807 ProblemDetails on every unhandled exception |
| Docker + Compose | API + MongoDB + Keycloak, healthcheck-gated startup order |
| Environment variables | .env.example with placeholder resolution via Steeltoe |
| Starting from scratch | Using FenixKit Auth |
|---|---|
| Hours configuring JWT Bearer options | Pre-configured, tested, ready |
| Manual Keycloak realm + client setup | realm-export.json imported on first docker compose up |
| Swagger Authorize button doesn't work | OAuth2 PKCE flow wired in — click Authorize, log in, done |
| Role checks scattered across handlers | Centralised policies: RequireAuthorization("AdminOnly") |
| No health check on the auth server | Keycloak OIDC discovery check in /health/ready |
| Token validation breaks on key rotation | Keycloak public keys fetched automatically via metadata URL |
| Reading claims is inconsistent | Typed UserInfoResponse with username, email, roles, subject |
Keycloak issues JWT tokens. The API validates every token against the Keycloak — fetched automatically from the OIDC discovery document.
Client → POST /realms/fenixkit/protocol/openid-connect/token → JWT
Client → GET /api/products/ + Bearer <JWT> → API validates → 200 OK
→ 401 if missing/invalid
→ 403 if wrong role
// Any authenticated user
group.MapGet("/orders", GetOrders)
.RequireAuthorization("Authenticated");
// Admin role only
group.MapDelete("/orders/{id}", DeleteOrder)
.RequireAuthorization("AdminOnly");Both policies are defined in Auth/Keycloak/Extensions/AuthExtensions.cs. Adding a new policy is one block:
.AddPolicy("PremiumOnly", policy =>
policy.RequireAuthenticatedUser()
.RequireRole("premium"))Then assign the premium realm role to users in the Keycloak admin console.
private static IResult Me(HttpContext ctx)
{
var username = ctx.User.Identity?.Name; // preferred_username claim
var email = ctx.User.FindFirst("email")?.Value;
var subject = ctx.User.FindFirst("sub")?.Value;
var isAdmin = ctx.User.IsInRole("admin");
var roles = ctx.User.FindAll("roles").Select(c => c.Value);
return Results.Ok(new UserInfoResponse(username, email, subject, roles));
}/api/auth-examples/me is a working example of this pattern included in the kit.
keycloak/realm-export.json is imported automatically on first startup. No manual steps.
| Item | Value |
|---|---|
| Realm | fenixkit |
| Client | fenixkit-api (Authorization Code + PKCE) |
| Redirect URIs | http://localhost:8081/* (Swagger UI) |
| Realm roles | admin, user |
| Test user | admin-test / admin123 — roles: admin, user |
| Test user | user-test / user123 — roles: user |
To customise the realm, edit keycloak/realm-export.json or use the Keycloak admin console at http://localhost:8082.
GET /health/live → Liveness — is the process alive?
GET /health/ready → Readiness — is MongoDB reachable? Is Keycloak reachable?
The Keycloak check fetches the OIDC discovery document (/.well-known/openid-configuration). A non-2xx response returns Degraded; a network error returns Unhealthy.
{
"status": "Healthy",
"entries": {
"mongodb": { "status": "Healthy" },
"keycloak": { "status": "Healthy" }
}
}# 1. Copy the environment file
cp .env.example .env
# 2. Start the full stack — API + MongoDB + Keycloak
docker compose up --build
# API → http://localhost:8081
# Swagger → http://localhost:8081/swagger
# Keycloak admin → http://localhost:8082 (admin / changeme)Open Swagger, click Authorize, log in as admin-test / admin123. All requests will carry the Bearer token automatically.
All errors — including auth errors — follow RFC 7807 application/problem+json:
| Status | Title | Cause |
|---|---|---|
401 |
Auth.Unauthorized |
Missing, expired, or invalid JWT |
403 |
Auth.Forbidden |
Valid JWT but insufficient role |
404 |
Resource.NotFound |
Entity not found |
409 |
Resource.Conflict |
Duplicate detected |
422 |
Validation Error |
Input validation failed |
500 |
Server Error |
Unhandled exception — ProblemDetails, never HTML |
| Package | Role |
|---|---|
| .NET 8 LTS (C# 12) · .NET 10 (C# 14) | Runtime and language |
| Keycloak 24 | OIDC / OAuth2 identity provider |
| Microsoft.AspNetCore.Authentication.JwtBearer | JWT Bearer validation |
| MongoDB.Driver | Official MongoDB .NET driver |
| ErrorOr v2 | Result pattern — no exceptions for domain errors |
| Swashbuckle.AspNetCore | Swagger UI + OAuth2 PKCE flow |
| DotNetEnv + Steeltoe | .env file + ${VAR} placeholder resolution in appsettings |
| Docker + Docker Compose | Full stack in one command |
Already own the base kit? See MIGRATION.md — step-by-step instructions for adding Keycloak auth to an existing FenixKit project or any .NET 8 Minimal API.
FenixKit Auth is a commercial product. Each purchase grants a lifetime licence for unlimited personal and commercial projects.