Service Architecture
- Web Service: Blazor Server UI with authentication (Port 8080)
- Application Service: REST API with JWT authentication (Port 8081)
- MongoDB: Document database (Port 27017)
- Redis: Cache and messaging (Port 6379)
The Application Service is a separate ASP.NET Core WebAPI process that handles data-oriented requests. The web application communicates with it via HTTP using JWT authentication, automatically forwarding the current organisation context.
API Documentation
- Health Checks: System Health Status
- Infrastructure: Infrastructure Monitor
Scoped Controller Pattern
API controllers are categorised into two scopes using attributes and base classes:
Organisation-Scoped
Inherit from OrganisationApiController and are marked with [OrganisationScoped]. These require an X-Organisation-Id header. The ApiTenantResolutionMiddleware resolves the organisation, sets the tenant context, and all IRepository<T> queries are automatically scoped to that org's database.
public class MyController : OrganisationApiController{ // CurrentOrganisationName, CurrentOrganisationId available // IRepository<T> queries scoped to org's database}Platform-Scoped
Inherit from PlatformApiController and are marked with [PlatformScoped]. No organisation context is needed. Used for authentication endpoints and platform-level operations.
[AllowAnonymous]public class TokenController : PlatformApiController{ // No tenant resolution, uses IPlatformRepository<T>}Authentication
Service-to-Service (Web → API)
The web application authenticates using client credentials. The JwtAuthenticationHandler handles this automatically on every request: it requests a token from IJwtTokenApiClient.GetServiceTokenAsync — which caches one token per organisation — and stamps the matching X-Organisation-Id header.
Each issued JWT is bound to a specific organisation via an org_id claim. The API rejects any request where the bearer token's org_id does not match the X-Organisation-Id header — a leaked token can only act on the organisation it was minted for. Tokens minted without an OrganisationId are usable only against [PlatformScoped] endpoints.
POST /api/token{ "ClientId": "blazorblueprint-web", "ClientSecret": "...", "OrganisationId": "<org-guid>" // optional; required for [OrganisationScoped] calls}# Subsequent requests:Authorization: Bearer <jwt-with-org_id-claim>X-Organisation-Id: <same-org-guid>Configuration: Client credentials are stored in appsettings.json under Services:ApplicationService. Both base controllers use [Authorize(Policy = "ServiceClient")], so any future co-tenant of the JWT secret can't reach the API without the ServiceClient role.
Current Endpoints
| Method | Route | Scope | Description |
|---|---|---|---|
POST |
/api/token |
Platform | Client credentials token exchange |
GET |
/api/organisations/current |
Organisation | Trimmed view of the organisation the JWT is bound to |
GET |
/api/weatherforecast |
Organisation | Weather demo (Redis cached, returns org name) |
GET |
/api/weatherforecast/page-cache |
Organisation | Weather demo (no server cache, returns org name) |
See the Caching Demos to see the tenant context in action — the API response includes the resolved organisation name.
Ready to Download?
Start building with Blazor Blueprint's service architecture and APIs. Download the full product free — use it, including commercially, while you're under £100k/year revenue; a commercial licence (from £499) applies above that.
🔓 Source-available on GitHub • Free under £100k/yr revenue • Commercial licence (from £499) above that • Full source code included