Neue Skills, Referenzen & OpenWiki-Doku integriert
Umfangreiche Erweiterung der Skill-Bibliothek: Neue Skills für Humanisierung (Englisch/PT-BR), Design-Validierung, AI-SEO und Coolify-Deployment inkl. Regelwerke, Presets, Pattern-Referenzen, Testfälle und Automatisierungsskripte. Zusätzliche Skills für Revenue-Centric Design, Pier Cloud, OKF, Lebenslauf- und LinkedIn-Optimierung sowie zahlreiche Referenzdateien, Checklisten und YAML/JSON/Markdown-Templates. Einführung einer vollständigen OpenWiki-Dokumentation mit Architektur-, Domain- und Workflow-Beschreibungen, zentralem Index und automatisierten Updates. Modularer Aufbau, restriktive Lizenzen und umfassende Qualitäts- und Evaluationsmechanismen für alle neuen Inhalte.
This commit is contained in:
3
openwiki/architecture/index.md
Normal file
3
openwiki/architecture/index.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Files
|
||||
|
||||
- [System Architecture & Security Overview](overview.md) - Technical architecture of SlipItIn, covering .NET Aspire orchestration, ASP.NET Core SignalR hub design, JWT authentication, and IDbContextFactory thread safety.
|
||||
129
openwiki/architecture/overview.md
Normal file
129
openwiki/architecture/overview.md
Normal file
@@ -0,0 +1,129 @@
|
||||
---
|
||||
type: Architecture
|
||||
title: System Architecture & Security Overview
|
||||
description: Technical architecture of SlipItIn, covering .NET Aspire orchestration, ASP.NET Core SignalR hub design, JWT authentication, and IDbContextFactory thread safety.
|
||||
tags: [architecture, spire, signalr, jwt, efcore, security]
|
||||
---
|
||||
|
||||
# System Architecture & Security Overview
|
||||
|
||||
**SlipItIn** is designed as a distributed, real-time application using modern .NET 10 architecture. This document details the orchestration model, backend service structure, security model, and concurrency safeguards.
|
||||
|
||||
The architectural foundation [orchestrates services with](/openwiki/operations/runbook.md) .NET Aspire, while [enforcing security & privacy on](/openwiki/domain/game-mechanics.md) domain entities and [serving real-time hub endpoints for](/openwiki/workflows/slip-and-challenge.md) all active game sessions. Source code structure for all architectural components can be found in the [Source Code Map](/openwiki/source-map.md).
|
||||
|
||||
---
|
||||
|
||||
## 1. .NET Aspire Orchestration Model
|
||||
|
||||
The solution uses **.NET Aspire** to orchestrate application resources, services, and infrastructure dependencies:
|
||||
|
||||
- **AppHost (`SlipItIn.AppHost/AppHost.cs`)**:
|
||||
- Provisions a PostgreSQL database container (`AddPostgres("pgsql").AddDatabase("postgresdb")`).
|
||||
- Registers the backend project `SlipItIn.Server` with a direct reference to the PostgreSQL resource.
|
||||
- Registers the client project `SlipItIn` with service discovery references to `SlipItIn.Server`.
|
||||
- **Service Defaults (`SlipItIn.ServiceDefaults/Extensions.cs`)**:
|
||||
- Configures **OpenTelemetry** logging, metrics (AspNetCore, HttpClient, Runtime), and tracing.
|
||||
- Exposes standardized health check endpoints (`/health` and `/alive`).
|
||||
- Enforces automatic HTTP resilience (`AddStandardResilienceHandler()`) and service discovery.
|
||||
|
||||
---
|
||||
|
||||
## 2. ASP.NET Core Backend Architecture (`SlipItIn.Server`)
|
||||
|
||||
The backend is an ASP.NET Core Web API & SignalR application providing REST endpoints for user management and real-time WebSockets for game state synchronization.
|
||||
|
||||
### Key Components
|
||||
|
||||
1. **`Program.cs`**:
|
||||
- Registers `AddServiceDefaults()`, `AddNpgsqlDataSource("postgresdb")`, and `AddDbContextFactory<SlipItInDbContext>()`.
|
||||
- Configures JWT Bearer authentication with custom `OnMessageReceived` token resolution for SignalR.
|
||||
- Automatically executes database migrations (`db.Database.Migrate()`) at startup.
|
||||
2. **`AuthController.cs`**:
|
||||
- Manages user registration (`/api/auth/register`) and authentication (`/api/auth/login`).
|
||||
- Uses `BCrypt.Net` for secure password hashing.
|
||||
- Issues JWT tokens signed with `Jwt:Key`, containing `ClaimTypes.NameIdentifier`, `ClaimTypes.Name`, and `ClaimTypes.Email`.
|
||||
3. **`GameHub.cs`**:
|
||||
- Real-time SignalR hub mapped to `/hubs/game`.
|
||||
- Annotated with `[Authorize]` to reject unauthenticated WebSocket connections.
|
||||
- Extracts and verifies user claims, delegating state mutations to `IGameService`.
|
||||
4. **`GameService.cs`**:
|
||||
- Implements core business logic: game creation, player joins, card dealing, slip submission, challenge creation, and resolution.
|
||||
|
||||
---
|
||||
|
||||
## 3. JWT Authentication & SignalR Security
|
||||
|
||||
In real-time SignalR applications, clients can attempt to spoof player identifiers. SlipItIn eliminates this vulnerability through strict claim validation and authorization checks.
|
||||
|
||||
### SignalR Token Extraction
|
||||
Since WebSockets cannot pass custom HTTP headers during connection handshakes, `Program.cs` configures JWT query parameter extraction:
|
||||
|
||||
```csharp
|
||||
options.Events = new JwtBearerEvents
|
||||
{
|
||||
OnMessageReceived = context =>
|
||||
{
|
||||
var accessToken = context.Request.Query["access_token"];
|
||||
var path = context.HttpContext.Request.Path;
|
||||
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
|
||||
{
|
||||
context.Token = accessToken;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Claims Validation & Access Control in `GameHub.cs`
|
||||
Inside `GameHub.cs`, claims are extracted directly from the authenticated SignalR context:
|
||||
|
||||
* `GetAuthenticatedUserId()`: Extracts `Context.User?.FindFirst(ClaimTypes.NameIdentifier)`. Throws `UnauthorizedAccessException` if missing or invalid.
|
||||
* `ValidatePlayerAccessAsync(playerId, authUserId)`: Queries the database to verify that `Player.UserId` matches the authenticated user ID.
|
||||
* `ValidateHostAccessAsync(gameId, authUserId)`: Verifies that `Game.HostId` matches the authenticated user ID before allowing lobby configuration or game start.
|
||||
* `ValidateChallengeTargetAccessAsync(challengeId, authUserId)`: Ensures only the accused player can resolve a challenge against them.
|
||||
|
||||
### Real-Time Auth Flow Diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor Client as MAUI Client
|
||||
participant Auth as AuthController
|
||||
participant Hub as GameHub
|
||||
participant Service as GameService
|
||||
participant DB as SlipItInDbContext
|
||||
|
||||
Client->>Auth: POST /api/auth/login
|
||||
Auth->>DB: Query User & Verify BCrypt Hash
|
||||
Auth-->>Client: 200 OK (JWT Access Token)
|
||||
Client->>Hub: Connect WebSocket /hubs/game?access_token=JWT
|
||||
Hub->>Hub: Validate JWT Signature & Claims
|
||||
Client->>Hub: JoinLobby(lobbyCode)
|
||||
Hub->>Hub: GetAuthenticatedUserId()
|
||||
Hub->>Service: JoinGameAsync(lobbyCode, userId, connectionId)
|
||||
Service->>DB: Create IDbContext Session & Save Player
|
||||
Hub-->>Client: Broadcast "PlayerJoined" (GameStateDto)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Concurrency Safety: `IDbContextFactory`
|
||||
|
||||
SignalR hubs process concurrent requests from multiple clients over persistent connections. Using a standard Scoped `DbContext` in SignalR leads to thread conflict exceptions (`InvalidOperationException: A second operation was started on this context instance before a previous operation completed`).
|
||||
|
||||
### Resolution via `IDbContextFactory`
|
||||
SlipItIn solves this by registering EF Core with `AddDbContextFactory<SlipItInDbContext>`:
|
||||
|
||||
* Every method call inside `GameHub.cs`, `AuthController.cs`, and `GameService.cs` creates a short-lived, isolated `DbContext` session using `using var context = _contextFactory.CreateDbContext()`.
|
||||
* **Benefit**: Thread-safe database operations across simultaneous challenges, card transfers, and lobby updates without race conditions.
|
||||
|
||||
---
|
||||
|
||||
## 5. Data Privacy Isolation Model
|
||||
|
||||
To prevent players from inspecting opponent cards via network trace analysis, SlipItIn strictly separates public and private data DTOs:
|
||||
|
||||
* **`GameStateDto` (Public)**: Broadcast to all players in a lobby. Contains public game information (`GameId`, `LobbyCode`, `Status`, `CurrentRound`, `RoundTimeRemaining`) and player summaries (`PlayerId`, `Username`, `Score`, `IsReady`, `CardCount`). **Crucially, no card text is included.**
|
||||
* **`PlayerHandDto` (Private)**: Direct unicast message sent *only* to the specific player's SignalR `ConnectionId`. Contains card text (`CardId`, `PhraseId`, `Text`, `IsUsed`).
|
||||
|
||||
This architecture guarantees that players cannot see opponent phrase cards, even if they inspect raw WebSocket packets.
|
||||
Reference in New Issue
Block a user