BFF Authentication & Authorization
Relevant source files
The following files were used as context for generating this wiki page:
- BuildingBlocks/BuildingBlocks.Infrastructure.Auth/AuthExtensions.cs
- BuildingBlocks/BuildingBlocks.Infrastructure.Auth/AuthController.cs
- BuildingBlocks/BuildingBlocks.Application/Validators/UserValidator.cs
- WebApplications/Identity.WebApi/Program.cs
- claude.md
- Aspire.AppHost/AppHost.cs
- Core/Scheduling/Scheduling.Application/Patients/Commands/CreatePatientCommand.cs
The BFF (Backend-for-Frontend) security pattern in this project ensures that the Single Page Application (SPA) never handles sensitive tokens directly. Instead, it relies on secure, HTTP-only cookies managed by the Web APIs. Authentication is handled via Duende IdentityServer using the OpenID Connect (OIDC) Authorization Code flow, while authorization is enforced through role-based validation within the application layer.
Sources: claude.md:104-120, BuildingBlocks.Infrastructure.Auth/AuthExtensions.cs:22-40
Architecture Overview
The system uses a centralized Identity provider and multiple resource servers (Web APIs) that share authentication state.
The diagram above shows the relationship between the Angular frontend, the Web API acting as a BFF, and the IdentityServer.
Key Components
| Component | Responsibility |
|---|---|
| Duende IdentityServer | The OIDC/OAuth2 authority managing users, roles, and token issuance. |
| BFF (Web APIs) | Acts as the OIDC client. It exchanges codes for tokens and issues secure cookies to the SPA. |
| Data Protection | Ensures that authentication cookies can be decrypted across different Web API instances (e.g., Scheduling and Billing). |
| UserValidator | Base class for enforcing role-based access control (RBAC) on Commands and Queries. |
Sources: claude.md:106-115, WebApplications/Identity.WebApi/Program.cs:60-70, BuildingBlocks.Infrastructure.Auth/AuthExtensions.cs:157-164
Authentication Flow
The project implements the Authorization Code flow. When a user logs in, the Web API redirects them to IdentityServer. Upon return, the API exchanges the code for tokens but only stores the id_token in the authentication properties to keep the cookie small.
Authentication Sequence
The sequence above illustrates the OIDC flow where the SPA remains decoupled from the token exchange process.
OIDC Configuration
The OIDC handler is configured with specific scopes and claim mappings to hydrate the user's identity:
- Scopes:
openid,profile,email,roles. - Claims:
sub,name,email,role(mapped viaMapJsonKey). - AJAX Handling: Returns
401 UnauthorizedforXMLHttpRequestinstead of redirecting to the login page.
Sources: BuildingBlocks.Infrastructure.Auth/AuthExtensions.cs:64-106, BuildingBlocks.Infrastructure.Auth/AuthController.cs:23-45
Authorization & Role Validation
Authorization is enforced at the Application layer using a custom UserValidator<T>. This ensures that business logic is protected regardless of the entry point.
Role Logic
The system supports complex role groups using AND/OR logic:
- OR Logic: Defined by the outer collection of role groups.
- AND Logic: Defined by the inner collection of roles within a group.
Sources: BuildingBlocks.Application/Validators/UserValidator.cs:20-40
Implementation Example: CreatePatientCommand
The CreatePatientCommandValidator restricts the creation of patients to specific roles:
internal class CreatePatientCommandValidator : UserValidator<CreatePatientCommand>
{
public CreatePatientCommandValidator(ICurrentUser currentUser, IValidator<CreatePatientRequest> createPatientRequestValidator)
: base(currentUser, new[] { AppRoles.Nurse }, new[] { AppRoles.Doctor }, new[] { AppRoles.Admin })
{
// ... rules ...
}
}
Sources: Scheduling.Application/Patients/Commands/CreatePatientCommand.cs:41-45
Data Protection and Session Management
To support a distributed environment where multiple APIs (Scheduling, Billing) must recognize the same session cookie, a shared Data Protection provider is used.
- Application Name: Must be consistent across all APIs (e.g.,
DDD.WebApis). - Key Storage: Keys are persisted to a shared file system path or a DbContext.
- Cookie Security: Cookies are configured as
HttpOnly,SecurePolicy.Always, andSameSite.Laxfor CSRF protection.
Sources: BuildingBlocks.Infrastructure.Auth/AuthExtensions.cs:41-52, BuildingBlocks.Infrastructure.Auth/AuthExtensions.cs:157-167, WebApplications/Identity.WebApi/Program.cs:55-58
Multi-Slot Isolation (Aspire)
In the .NET Aspire orchestrator, authentication settings are dynamically overridden to allow multiple developers or worktree slots to run simultaneously without session collisions.
- Cookie Name: Appended with the slot number (e.g.,
DDD.Auth.S2). - Authority: Pointed to the slot-specific Identity API URL.
- CORS: Allowed origins are updated to include the slot-specific SPA and Identity URLs.
Sources: Aspire.AppHost/AppHost.cs:175-195
The BFF pattern implemented here provides a secure foundation by centralizing token management in the backend and enforcing granular role-based access control in the application core.