Apped.BffAuth
2025.1.4
dotnet add package Apped.BffAuth --version 2025.1.4
NuGet\Install-Package Apped.BffAuth -Version 2025.1.4
<PackageReference Include="Apped.BffAuth" Version="2025.1.4" />
<PackageVersion Include="Apped.BffAuth" Version="2025.1.4" />
<PackageReference Include="Apped.BffAuth" />
paket add Apped.BffAuth --version 2025.1.4
#r "nuget: Apped.BffAuth, 2025.1.4"
#:package Apped.BffAuth@2025.1.4
#addin nuget:?package=Apped.BffAuth&version=2025.1.4
#tool nuget:?package=Apped.BffAuth&version=2025.1.4
Apped.BffAuth
Secure Backend-for-Frontend (BFF) authentication for .NET 8 APIs with OAuth/OIDC support, multi-tenancy, and complete Swagger UI integration.
🚀 Quick Start Guide
1. Installation
dotnet add package Apped.BffAuth --version 2025.1.4
2. 🔄 Migration Guide (Adding to Existing Project)
📋 What You Need to Do:
Step 1: Keep Swagger services AND add BFF Auth
// ✅ KEEP your existing Swagger services (required for core functionality):
builder.Services.AddSwaggerGen();
// ✅ ADD BFF Auth with Swagger integration:
builder.Services.AddBffAuth("your-client-id", "your-client-secret",
"https://auth.example.com/realms/your-realm", "your-32-char-signing-key")
.EnableSwagger("Your API Name", "Description");
Step 3: Update your middleware
// ✅ KEEP this unchanged:
if (app.Environment.IsDevelopment()) {
app.UseSwagger();
// ❌ REMOVE this line:
// app.UseSwaggerUI();
// ✅ REPLACE with BFF-integrated Swagger UI:
app.UseBffAuthSwagger(); // Swagger UI with OAuth integration
}
// ✅ ADD this for authentication:
app.UseBffAuth(); // Handles BFF authentication middleware
🎯 That's it! Your Swagger UI now has OAuth authentication with popup login.
🤖 For AI Agents & Future Developers
✅ CRITICAL: Correct Configuration Pattern
The BFF Auth Swagger integration requires BOTH standard Swagger services AND BFF Auth integration:
// ✅ REQUIRED: Core Swagger services (DO NOT REMOVE)
builder.Services.AddSwaggerGen();
// ✅ REQUIRED: BFF Auth integration (adds HTML injection)
builder.Services.AddBffAuth(config)
.EnableSwagger("Title", "Description");
// ✅ REQUIRED: Use BFF Auth Swagger UI (NOT standard UseSwaggerUI)
app.UseBffAuthSwagger();
❌ Common Mistakes That Break HTML Injection
// ❌ WRONG: Missing AddSwaggerGen() causes "ISwaggerProvider not found" error
builder.Services.AddBffAuth(config)
.EnableSwagger("Title", "Description");
// Missing: builder.Services.AddSwaggerGen();
// ❌ WRONG: Using UseSwaggerUI() prevents BFF Auth HTML injection
app.UseSwaggerUI(); // This blocks the custom authentication widget
// ❌ WRONG: Forgetting .EnableSwagger() means no HTML injection happens
builder.Services.AddBffAuth(config);
// Missing: .EnableSwagger("Title", "Description");
🎯 HTML Injection Verification
When correctly configured, you should see:
- Custom "BFF Authentication" widget at top of Swagger UI
- Sign In/Sign Out buttons with tenant ID input
- Real-time authentication status display
- No standard Swagger "Authorize" button (replaced by BFF widget)
🏢 Multi-Tenancy: Header-Only Approach (v2025.1.4+)
BREAKING CHANGE: Tenant ID is now passed via headers only, NOT in request body.
// ✅ CORRECT: Tenant passed via header
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Tenant-ID", "tenant123");
await client.PostAsync("/auth/start", new StringContent("{\"redirect_hint\": \"...\"}"));
// ❌ WRONG: Tenant in request body (deprecated since v2025.1.4)
await client.PostAsync("/auth/start", new StringContent("{\"redirect_hint\": \"...\", \"tenant_id\": \"tenant123\"}"));
The Swagger UI widget automatically handles this - enter tenant ID in the widget and it's sent via X-Tenant-ID
header.
3. Alternative Setup Methods
If you prefer configuration files:
// appsettings.json
{
"BffAuth": {
"Providers": {
"Keycloak": {
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret",
"RealmBaseUrl": "https://auth.example.com/realms/your-realm"
}
},
"Security": {
"SigningKey": "your-32-character-signing-key-here"
}
}
}
// Program.cs
builder.Services.AddBffAuth(builder.Configuration.GetSection("BffAuth"))
.EnableSwagger("My API", "Description");
If you need advanced settings:
builder.Services.AddBffAuth(options => {
options.ConfigureKeycloak()
.WithClientCredentials("client-id", "secret")
.WithRealmUrl("https://auth.example.com/realms/test")
.WithScopes("openid", "profile", "email", "roles")
.And()
.ConfigureSecurity()
.WithSigningKey("your-32-char-signing-key")
.And()
.ConfigureSessions()
.WithSessionTtlMinutes(120) // 2 hours
.And()
.EnableSwagger("My API", "Description");
});
3. Configuration (Option A Only)
Add to your appsettings.json
:
{
"BffAuth": {
"Providers": {
"keycloak": {
"Type": "keycloak",
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret",
"AuthorizationEndpoint": "https://your-keycloak/realms/your-realm/protocol/openid-connect/auth",
"TokenEndpoint": "https://your-keycloak/realms/your-realm/protocol/openid-connect/token",
"UserInfoEndpoint": "https://your-keycloak/realms/your-realm/protocol/openid-connect/userinfo",
"Issuer": "https://your-keycloak/realms/your-realm",
"Scopes": ["openid", "profile", "email"],
"UsePkce": true,
"RedirectUris": [
"myapp://callback",
"https://myapp.com/callback"
]
}
},
"Security": {
"SigningKey": "your-super-secret-signing-key-at-least-32-characters"
}
}
}
✨ What's New in v2025.1.4
🔧 Latest Updates
- BREAKING CHANGE: Tenant ID no longer accepted in request body for
/auth/start
- Header-based tenancy: All tenant information now passed via
X-Tenant-ID
header only - Consistent tenant handling: Unified approach across all authentication endpoints
- Updated examples: All documentation reflects current header-based approach
✨ What's New in v2025.1.1
🎯 Self-Guiding Fluent Builder API
- IntelliSense-driven setup: Discover all configuration options through autocomplete
- 3 setup approaches: Choose what works for your project (config, fluent, or quick)
- Built-in validation: Helpful error messages guide you to correct configuration
- One-line middleware:
app.UseBffAuth()
replaces all previous middleware calls - Backward compatible: Existing code continues to work (marked as obsolete)
🔧 Enhanced Developer Experience
- Self-documenting builders: Method names clearly indicate what they configure
- Preset configurations:
WithDevelopmentDefaults()
- Optimized for local development and testingWithProductionDefaults()
- Security-first settings for productionWithSecureDefaults()
- Maximum security configuration
- Automatic endpoint generation: All auth endpoints work out-of-the-box
🌐 Complete Swagger UI Integration
- "Authorize (BFF)" button with professional UI design
- Popup-based OAuth flow - no page redirects
- Session persistence across browser refreshes
- Real-time authentication status with user details and session expiry
- Multi-tenant support with tenant ID input field
- Automatic header injection for all API calls:
Authorization: Session {sessionId}
X-Tenant-ID: {tenantId}
(when provided)
🚀 Streamlined Setup Process
Before:
// 4+ method calls, complex configuration
builder.Services.AddBffAuthentication(builder.Configuration);
builder.Services.AddBffAuthSwagger();
app.UseBffAuthSwaggerAssets();
app.UseBffAuthentication();
After:
// 2 method calls, self-guiding configuration
builder.Services.AddBffAuth(config => config.WithDevelopmentDefaults());
app.UseBffAuth();
Automatic Endpoints
The package automatically registers these endpoints when you call app.UseBffAuth()
:
🔐 Authentication Endpoints
- POST
/auth/start
- Start OAuth flow, returns authorization URL and session ID - POST
/auth/callback
- Handle OAuth callback, returns authenticated session - POST
/auth/refresh
- Refresh expired session with new tokens - POST
/auth/logout
- Logout user and invalidate session - POST
/auth/logout-url
- Get provider logout URL for complete logout
👤 Session Info Endpoint
- GET
/auth/me
- Get current session information including user details, roles, and expiry timestamps (requires session)
🔧 Swagger Integration Endpoints (when using AddBffAuthSwagger)
- GET
/swagger-auth/callback
- OAuth callback handler for Swagger UI popup integration - GET
/bff-auth-swagger.js
- Custom JavaScript for Swagger UI integration - GET
/bff-auth-swagger.css
- Custom styles for Swagger UI integration
📚 API Reference
Request/Response Models
AuthStartRequest
{
"redirect_hint": "string" // Required: OAuth redirect URI hint for provider selection
}
AuthStartResponse
{
"sid": "string", // Session ID for callback
"authorize_url": "string", // OAuth provider authorization URL
"expires_in": 600 // Expiration time in seconds (default: 10 minutes)
}
AuthCallbackRequest
{
"sid": "string", // Required: Session ID from start response
"code": "string", // Required: OAuth authorization code
"state": "string", // Required: OAuth state parameter
"user_agent": "string", // Optional: User agent for device binding
"device_id": "string" // Optional: Device identifier for device binding
}
AuthCallbackResponse
{
"session_id": "string", // New authenticated session ID
"user": { // User information and roles
"sub": "string", // User ID
"email": "string", // Email address
"name": "string", // Display name
"username": "string", // Username
"firstName": "string", // First name
"lastName": "string", // Last name
"globalRoles": ["string"], // Global/realm roles
"tenants": [ // Tenant-specific roles
{
"tenantId": "string",
"roles": ["string"]
}
]
},
"expires_in": 3600 // Session expiration in seconds
}
SessionInfo Response (GET /auth/me)
{
"sessionId": "string", // Current session identifier
"user": { // Complete user information (same as above)
"sub": "string",
"email": "string",
"name": "string",
"username": "string",
"firstName": "string",
"lastName": "string",
"globalRoles": ["string"],
"tenants": [
{
"tenantId": "string",
"roles": ["string"]
}
]
},
"expiresAt": "2024-01-15T10:30:00Z", // Session expiration timestamp (ISO 8601)
"createdAt": "2024-01-15T09:00:00Z", // Session creation timestamp
"lastAccessedAt": "2024-01-15T10:25:00Z", // Last access timestamp
"expiresIn": 300, // Seconds until expiration
"isExpired": false // Whether session has expired
}
UserInfo Model (returned by /auth/me
)
{
"sub": "string", // Unique user identifier
"email": "string", // User email
"name": "string", // Full display name
"username": "string", // Username/login
"firstName": "string", // First name
"lastName": "string", // Last name
"globalRoles": ["string"], // Global roles (e.g., ["admin", "user"])
"tenants": [ // Per-tenant role assignments
{
"tenantId": "tenant1",
"roles": ["manager", "user"]
}
]
}
🌐 Swagger UI Integration Guide
The package provides seamless integration with Swagger UI, adding a professional OAuth authentication experience directly in your API documentation.
Complete Integration Setup (New API)
// Program.cs
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "My API",
Version = "v1"
});
// No manual security scheme configuration needed!
});
// Single call enables everything (authentication + swagger integration)
builder.Services.AddBffAuth(builder.Configuration.GetSection("BffAuth"))
.EnableSwagger(); // This handles all Swagger integration automatically
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(); // Standard Swagger UI, no changes needed
}
// Single middleware call handles everything
app.UseBffAuth(); // Replaces UseBffAuthSwaggerAssets + UseBffAuthentication
app.UseRouting();
app.MapControllers();
app.Run();
🔧 Advanced Configuration
If you need custom Swagger title/description:
builder.Services.AddBffAuth(options => {
options.ConfigureKeycloak()
.WithClientCredentials("client-id", "client-secret")
.WithRealmUrl("https://auth.example.com/realms/myrealm")
.And()
.EnableSwagger("Custom API Title", "Custom description");
});
If you store config in appsettings.json:
// appsettings.json
{
"BffAuth": {
"Providers": {
"Keycloak": {
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret",
"RealmBaseUrl": "https://auth.example.com/realms/your-realm"
}
},
"Security": {
"SigningKey": "your-32-character-signing-key-here"
}
}
}
// Program.cs
builder.Services.AddBffAuth(builder.Configuration.GetSection("BffAuth"))
.EnableSwagger("My API", "Description");
🔍 Troubleshooting
Problem: "Swagger is already configured" error
- ✅ Fix: This error is outdated - you now NEED both
AddSwaggerGen()
AND.EnableSwagger()
- ✅ Current approach: Keep
AddSwaggerGen()
for core Swagger, add.EnableSwagger()
for BFF Auth integration
Problem: "Not Found /swagger/v1/swagger.json"
- ✅ Fix: Make sure you're using
app.UseBffAuthSwagger()
instead ofapp.UseSwaggerUI()
- ✅ Fix: Keep
app.UseSwagger()
but replaceUseSwaggerUI()
withUseBffAuthSwagger()
Problem: No "Authorize (BFF)" button in Swagger
- ✅ Fix: Make sure you called
.EnableSwagger("Title", "Description")
- ✅ Fix: Use
app.UseBffAuthSwagger()
instead ofapp.UseSwaggerUI()
- ✅ Fix: Check browser console for JavaScript errors
Problem: Popup gets blocked during authentication
- ✅ Fix: Allow popups for localhost in your browser settings
Problem: API calls don't include authentication
- ✅ Fix: Click the "Authorize (BFF)" button and complete login first
- ✅ Check: Look for
bff-session-id
in browser localStorage
✨ Swagger Features
- 🎯 Custom "Authorize (BFF)" Button: Replaces default Swagger authorization with integrated OAuth flow
- 🔄 Popup Authentication: No page redirects, all authentication happens in popup window
- 💾 Session Persistence: Sessions survive browser refreshes via localStorage
- 🔗 Automatic Headers: All API requests automatically include
Authorization: Session {sessionId}
- 📱 Real-time Status: Button shows authentication state and user info
- 🖥️ Development Ready: Works with localhost redirects and provides testing tools
- 🎨 Professional UI: Beautiful gradient styling with hover effects and smooth transitions
- 🔄 Smart Retry: Robust initialization that handles dynamic content loading
Authentication Flow
1. Start Authentication
POST /auth/start
{
"redirect_hint": "myapp://callback"
}
Response:
{
"sid": "opaque-session-id",
"authorize_url": "https://keycloak.../auth?client_id=...",
"expires_in": 600
}
2. Handle OAuth Callback
POST /auth/callback
{
"sid": "session-id-from-start",
"code": "oauth-authorization-code",
"state": "oauth-state-parameter"
}
Response:
{
"session_id": "new-authenticated-session-id",
"user": {
"sub": "user-123",
"email": "user@example.com",
"name": "John Doe",
"globalRoles": ["admin"],
"tenants": [
{ "tenantId": "tenant1", "roles": ["manager"] }
]
},
"expires_in": 3600
}
3. Use Session
Include in all API requests:
Authorization: Session your-session-id
4. Protect Your Endpoints
[RequireSession]
public class MyController : ControllerBase
{
[RequireRole("admin")]
public IActionResult AdminOnly() => Ok();
[RequireRole("manager", requireCurrentTenant: true)]
public IActionResult TenantManager(string tenantId) => Ok();
public IActionResult GetUserInfo()
{
var user = (UserInfo)HttpContext.Items["User"]!;
return Ok(user);
}
}
📖 Self-Guiding Builder API Reference
The fluent builder API is designed to be discoverable through IntelliSense. Simply start typing and let your IDE guide you through all available options.
Core Builder Methods
AddBffAuth()
- Main Entry Point
// Method 1: Configuration-based (reads from appsettings.json)
services.AddBffAuth(configuration.GetSection("BffAuth"))
// Method 2: Fluent configuration with lambda
services.AddBffAuth(builder => { /* configure */ })
// Method 3: Quick setup with parameters
services.AddBffAuth("clientId", "clientSecret", "realmUrl", "signingKey", "redirect1", "redirect2")
ConfigureKeycloak()
- OAuth Provider Setup
.ConfigureKeycloak()
.WithClientCredentials("client-id", "client-secret") // Required
.WithRealmUrl("https://auth.example.com/realms/realm") // Auto-generates endpoints
.WithScopes("openid", "profile", "email") // OAuth scopes
.WithRedirectUris("myapp://callback", "https://...") // Callback URLs
.And() // Return to main builder
ConfigureSecurity()
- Security Settings
.ConfigureSecurity()
.WithSigningKey("32-char-minimum-signing-key") // JWT signing key
.WithGeneratedSigningKey() // Auto-generate for development
.RequireHttps(true) // HTTPS enforcement
.ValidateIssuer(true) // Token validation
.ValidateAudience(true) // Token validation
.ValidateAll() // Enable all validations
.WithProductionDefaults("signing-key") // Secure preset
.WithDevelopmentDefaults("signing-key") // Development preset
.And()
ConfigureSessions()
- Session Management
.ConfigureSessions()
.WithSessionTtl(TimeSpan.FromMinutes(60)) // Session lifetime
.WithSessionTtlMinutes(60) // Session lifetime (minutes)
.WithPendingAuthTtl(TimeSpan.FromMinutes(10)) // Auth flow timeout
.WithPendingAuthTtlMinutes(10) // Auth flow timeout (minutes)
.EnableDeviceBinding() // Device security
.EnableUserAgentBinding() // Browser security
.UseRedis("connection-string") // Redis storage
.UseInMemory() // In-memory storage
.WithSecureDefaults() // Production preset
.WithDevelopmentDefaults() // Development preset
.And()
ConfigureSwagger()
- Swagger UI Integration
.ConfigureSwagger() // Enable Swagger integration
.WithTitle("My API") // Swagger document title
.WithDescription("API Description") // Document description
.WithCallbackRoute("/auth/swagger-callback") // OAuth callback route
.EnableAuthButton(true) // Show auth button
.WithDefaults("Custom Title") // Apply recommended settings
.And()
// OR use the convenience method:
.EnableSwagger("My API", "API Description") // Quick setup with title/description
Preset Configurations
Development Presets - Optimized for Local Development
// Security: Relaxed HTTPS, generated signing key
.ConfigureSecurity().WithDevelopmentDefaults()
// Sessions: Longer duration, no device binding
.ConfigureSessions().WithDevelopmentDefaults()
Production Presets - Maximum Security
// Security: HTTPS required, all validations enabled
.ConfigureSecurity().WithProductionDefaults("your-signing-key")
// Sessions: Shorter duration, full device binding
.ConfigureSessions().WithSecureDefaults()
Complete Examples
Development Setup
builder.Services.AddBffAuth(options => {
options.ConfigureKeycloak()
.WithClientCredentials("dev-client", "dev-secret")
.WithRealmUrl("http://localhost:8080/realms/dev")
.WithRedirectUris("http://localhost:5058/callback")
.And()
.ConfigureSecurity()
.WithDevelopmentDefaults() // Generated key, no HTTPS
.And()
.ConfigureSessions()
.WithDevelopmentDefaults() // 60min sessions, no binding
.And()
.ConfigureSwagger()
.WithDefaults("Development API")
.And();
});
Production Setup
builder.Services.AddBffAuth(options => {
options.ConfigureKeycloak()
.WithClientCredentials("prod-client", Environment.GetEnvironmentVariable("CLIENT_SECRET"))
.WithRealmUrl("https://auth.mycompany.com/realms/production")
.WithRedirectUris("https://myapp.com/callback", "myapp://callback")
.And()
.ConfigureSecurity()
.WithProductionDefaults(Environment.GetEnvironmentVariable("SIGNING_KEY"))
.And()
.ConfigureSessions()
.WithSecureDefaults() // 30min sessions, full binding
.And()
.ConfigureSwagger()
.WithDefaults("Production API")
.And();
});
Features
- Self-Guiding: IntelliSense reveals all configuration options
- Secure: OAuth tokens never exposed to clients
- PKCE: Enhanced security for all flows
- Multi-tenant: Role-based access with tenant support
- Redis: Automatic detection with in-memory fallback
- Keycloak: Built-in provider support
- Flexible: 3 setup approaches to fit any project
Multi-Tenancy
Keycloak groups from JWT tokens automatically map to tenant roles:
Example Token:
"groups": [
"/tenants/tenant1",
"/tenants/tenant2/manager"
]
Results in:
tenant1/user
(default role)tenant2/manager
(specific role)
Access user info in controllers:
[RequireSession]
public IActionResult GetUser()
{
var user = (UserInfo)HttpContext.Items["User"]!;
return Ok(user);
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. 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. |
-
net8.0
- Microsoft.AspNetCore.Http.Abstractions (>= 2.2.0)
- Microsoft.AspNetCore.Mvc.Core (>= 2.2.5)
- Microsoft.Extensions.Caching.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Caching.Memory (>= 8.0.1)
- Microsoft.Extensions.Caching.StackExchangeRedis (>= 8.0.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Http (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.2)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
- Microsoft.IdentityModel.Protocols.OpenIdConnect (>= 8.0.0)
- Swashbuckle.AspNetCore (>= 6.4.0)
- System.IdentityModel.Tokens.Jwt (>= 8.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.
v2025.1.4: Documentation and README Updates
- IMPROVED: Updated repository and NuGet README documentation to reflect latest changes
- IMPROVED: Accurate documentation of tenant_id header-based approach
- IMPROVED: Current Swagger integration instructions
- IMPROVED: Up-to-date examples and troubleshooting guidance
v2025.1.3: Critical Tenant ID and Swagger Integration Fixes
- BREAKING: Removed tenant_id from auth/start request body - now uses X-Tenant-ID header only
- FIXED: Swagger UI integration documentation and middleware setup
- FIXED: "Not Found /swagger/v1/swagger.json" error - now uses correct middleware replacement
- FIXED: Sample application now properly demonstrates BFF Auth Swagger integration
- IMPROVED: Consistent tenant handling across all endpoints (header-based only)
- IMPROVED: Clear migration guide showing exactly what to remove/replace/keep
- IMPROVED: Simplified documentation - eliminated confusing multiple approaches
- IMPROVED: Comprehensive troubleshooting section for common Swagger issues
- IMPROVED: Swagger conflict detection with helpful error messages
- IMPROVED: Better code coverage reporting by excluding sample projects
v2025.1.2: Documentation and Testing Improvements
- IMPROVED: Enhanced test coverage for AuthController, BffAuthBuilder, SwaggerExtensions
- IMPROVED: Visual code coverage integration and reporting
- IMPROVED: Error handling and validation in fluent builders
v2025.1.1: Self-Guiding Fluent Builder API
- NEW: Self-guiding fluent builder API with IntelliSense-driven setup
- NEW: 3 flexible setup approaches (config-based, fluent, quick setup)
- NEW: Built-in validation with helpful error messages
- NEW: Unified UseBffAuth() middleware (replaces 4+ method calls)
- NEW: Preset configurations (WithDevelopmentDefaults, WithProductionDefaults)
- NEW: Comprehensive README with self-documenting examples
- IMPROVED: Complete Swagger UI integration with automatic setup
- IMPROVED: Streamlined developer experience
- BACKWARD COMPATIBLE: Existing code continues to work (marked obsolete)
- BREAKING: None - fully backward compatible
v1.2.0: Complete Swagger UI Integration
- Full Swagger UI integration with "Authorize (BFF)" button
- Popup-based OAuth authentication with session persistence
- Automatic header injection and multi-tenant support
- Real-time authentication status display
v1.1.0: Enhanced Security and UX
- Improved PKCE implementation and state parameter security
- Better error handling and logging
v1.0.0: Initial Release
- Backend-for-Frontend authentication pattern
- OAuth 2.0/OIDC with PKCE support
- Keycloak integration and multi-tenancy
- Redis and in-memory session storage