Skip to content
bitzorcas
中EN

Reference

Modern Multi-Tenant Authentication: JWT, API Keys, and Context Injection

Explore the BitzOrcas.Modern authentication core. Learn ClaimsPrincipal resolution, high-throughput API Key validation, and immutable ICurrentUser / ICurrentTenant scoped injection.

Last updated

In enterprise multi-tenant B2B SaaS platforms, authentication is significantly more complex than standard consumer apps:

  • Hybrid Multi-Tenant Contexts: A single user may belong to multiple tenant organizations; the system must accurately resolve the active TenantId;
  • Dual-Channel Ingestion: Supporting both browser-based interactive JWT authentication and external machine-to-machine (M2M) API Key integration;
  • Context Tampering Defense: Callers must never be trusted to pass raw UserId or TenantId parameters in request payloads.

BitzOrcas.Modern implements a unified authentication foundation: Extracting verified claims in ASP.NET Core middleware and injecting immutable, scoped ICurrentUser and ICurrentTenant contracts.

Authentication and Context Injection Pipeline

Bearer JWTX-API-Key

1. Inbound HTTP Request

2. Authentication Middleware

3. JWT Signature & Expiry Verification

4. High-Throughput API Key Verification

5. Build ClaimsPrincipal & Security Context

6. ICurrentUser (UserId, Roles) + ICurrentTenant (TenantId)

7. Execute Handler (Reads Identity Exclusively from Context)


Step 1: Safely Accessing Identity from Scoped Contexts

Never read UserId from request bodies. Inject ICurrentUser and ICurrentTenant directly into handlers:

SubmitOrderCommandHandler.cs: Secure Identity Context Consumption
using System;
using System.Threading;
using System.Threading.Tasks;
using BitzOrcas.Application.Abstractions.Security;
using BitzOrcas.Domain.Abstractions;
using BitzOrcas.Domain.Results;
public static class AuthErrors
{
public static readonly Error Unauthenticated =
Error.Unauthorized("Auth.Unauthenticated", "User must be authenticated.");
}
public sealed class SubmitOrderCommandHandler(
ICurrentUser currentUser,
ICurrentTenant currentTenant,
ICommandRepository<Order, string> orderRepository)
{
public async ValueTask<Result<string>> Handle(SubmitOrderCommand command, CancellationToken ct)
{
// 1. Extract verified UserId and TenantId from signed server context
var userId = currentUser.UserId;
var tenantId = currentTenant.Tenant.EffectiveTenantId;
// 2. Validate active session state
if (string.IsNullOrEmpty(userId))
{
return Result<string>.Failure(AuthErrors.Unauthenticated);
}
// 3. Persist order with immutable server-resolved identities
var order = Order.Create(
id: Guid.NewGuid().ToString("N"),
orderCode: command.OrderCode,
totalAmount: command.TotalAmount,
userId: userId,
tenantId: tenantId).GetValueOrThrow();
var saveResult = await orderRepository.SaveAsync(order, ct);
if (saveResult.IsFailure)
{
return Result<string>.Failure(saveResult.Error);
}
return Result<string>.Success(order.Id);
}
}

Step 2: Configuring API Key Machine Authentication

For external machine integrations, register high-throughput API Key validation handlers:

ApiKeyAuthenticationHandler.cs: API Key Handler
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
public sealed class ApiKeyAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
System.Text.Encodings.Web.UrlEncoder encoder,
IApiKeyValidator apiKeyValidator) : AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
{
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
// 1. Extract X-API-Key header
if (!Request.Headers.TryGetValue("X-API-Key", out var apiKeyValue))
{
return AuthenticateResult.NoResult();
}
// 2. Validate key against cached hashed store
var validationResult = await apiKeyValidator.ValidateKeyAsync(apiKeyValue.ToString());
if (validationResult.IsFailure)
{
return AuthenticateResult.Fail("API Key is invalid or expired.");
}
// 3. Construct ClaimsPrincipal representing machine identity
var identity = validationResult.Value!;
var ticket = new AuthenticationTicket(identity, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}

Summary

BitzOrcas authentication unifies security across channels:

  • Dual-Channel Parity: JWT and API Keys share identical authorization pipelines;
  • Tamper-Proof Contexts: Identities are strictly generated by server verifications;
  • Native Tenant Isolation: Auto-resolves effective tenant IDs, eliminating tenant leakage.

100%

Scroll or use controls to zoom · drag when enlarged · double-click for 100% / 200%