fix(security): first-party JwtBearer with HS256 pinned; /auth/token mints for a cookie session only

The hand-rolled bearer handler is replaced by AddJwtBearer (Microsoft.AspNetCore.Authentication.JwtBearer
10.0.7, pinned on the same servicing line as the other AspNetCore packages). Validation parameters come
from JwtTokenService.BuildValidationParameters, which now pins ValidAlgorithms = HS256 for both the
bearer scheme and TryValidate; MapInboundClaims is off so the minted Role/zb:username keys resolve.
/auth/token requires the cookie scheme, so a bearer can no longer mint a fresh token for itself.

Refs #733
Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
Joseph Doherty
2026-10-22 13:35:54 -04:00
parent 4d17b346c6
commit 3ddb6876db
10 changed files with 187 additions and 126 deletions
+8 -5
View File
@@ -6,11 +6,14 @@
> auth + role-grant pages live in `OtOpcUa.AdminUI`), and Admin auth policies
> register from `OtOpcUa.Host/Program.cs` via `AddOtOpcUaAuth`
> (`src/Server/ZB.MOM.WW.OtOpcUa.Security/ServiceCollectionExtensions.cs`) rather
> than in a separate Admin process. The Admin UI uses a **single Cookie
> authentication scheme** — there is no `AddJwtBearer` pipeline. The
> `Security:Jwt` section configures `JwtTokenService`, which mints a JWT at the
> `/auth/token` endpoint for **external** consumers (OPC UA clients / automation
> scripts); the cookie itself stores the `ClaimsPrincipal` directly. DataProtection
> than in a separate Admin process. The Admin UI session is a **Cookie**
> scheme; the cookie stores the `ClaimsPrincipal` directly. JWT is **opt-in by
> `Security:Jwt:SigningKey`** (validated at host start, ≥32 bytes): with a key,
> `/auth/token` mints an HS256 token for a **cookie session only** (a bearer
> cannot renew itself), and a first-party `AddJwtBearer` scheme (`Bearer`,
> `ValidAlgorithms = HS256`, parameters from `JwtTokenService.BuildValidationParameters`)
> accepts it on `/api/*` and every `RequireAuthorization` endpoint through the
> `OtOpcUa.CookieOrBearer` default policy scheme. Without a key neither exists. DataProtection
> keys persist to the shared Config DB (`PersistKeysToDbContext<OtOpcUaConfigDbContext>`)
> so cookies survive failover between admin-role nodes.
>