Files
ERP_Distribution/Distribution/docs/authentication_flow.md
2026-08-25 16:46:12 +05:30

3.8 KiB

Authentication — Own-User Auth via External Identity Service (AuthHex)

Status: Done Depends On: — (cross-cutting infrastructure, not a numbered feature step)


Goal

Own-user (staff/API) authentication is delegated to an external identity service ("AuthHex"), which owns credentials and signs RS256 JWTs. This app validates those tokens and maps them onto its own User/Role tables so the existing permission-policy authorization stays unchanged.


Background / Context

Previously this app verified passwords and minted its own HMAC-signed JWT locally. That's been replaced — AuthHex now owns credentials and signing; this app only holds AuthHex's public key to validate tokens, and never sees a plaintext password for this flow.

Authorization is untouched: permission policies, role levels, scope checks, and the seeded role set all still work exactly as before, because every validated token is mapped back onto a local User/Role row before any authorization check runs.


Configuration Changes

"Jwt": {
  "PublicKey": "<RSAKeyValue>...</RSAKeyValue>",
  "Audience": "AuthHexClient",
  "Issuer": "AuthHex"
},
"AuthHex": { "BaseUrl": "http://localhost:6102" }

Jwt:Key (old symmetric secret) is removed. Jwt:PublicKey is AuthHex's RSA public key in XML format (not PEM).


API Endpoints

Method Route Notes
POST /api/auth/login Proxies to AuthHex loginUser
POST /api/auth/register Proxies to AuthHex registerUser
GET /api/auth/me [Authorize], proxies to AuthHex getUserDetails
POST /api/auth/logout [Authorize], proxies to AuthHex LogoutUser

All four just forward to POST {AuthHex:BaseUrl}/api/user and pass AuthHex's response back as-is. No cookie — token returned in the response body, sent back as Authorization: Bearer.


Business Rules

  1. No local password check or token issuance — both delegated to AuthHex.
  2. AddJwtBearer validates issuer/audience/signature/lifetime against AuthHex's public key.
  3. OnTokenValidated maps the token's sub claim to a local User (IUserRepository); fails the token if no matching active user exists.
  4. On success, username/role_id/role_level claims are attached from the local User/ Role row — authorization checks downstream are unaffected by anything AuthHex's own token claims contain.
  5. Only one validation mechanism runs (AddJwtBearer) — two ported alternatives exist but are not registered (see Implementation Notes).
  6. No refresh tokens — once the AuthHex-issued token expires, the client must log in again.

Permissions to Add to Seed

None — this only changes authentication. Permission policies and role seed data are untouched.


Open Questions

  • Add a refresh-token flow if AuthHex doesn't offer one and re-login UX proves disruptive.
  • Keep or delete the ported AuthMiddleware/JwtMiddleware if never actually needed.
  • No retry/circuit-breaker if AuthHex is unreachable.

Implementation Notes

Features/Auth/            — AuthController, AuthService, AuthDTOs (thin AuthHex proxies)
Infra/Authorization/
  AuthHex/                 — IAuthHexClient, AuthHexClient, AuthHexResponse
  Jwt/RsaXmlKeyReader.cs    — portable XML→RSA key parser (RSA.FromXmlString is Windows-only)
  Middleware/               — AuthMiddleware, JwtMiddleware, JwtOptions, ApiResponse
                               (ported from OSMCore for reference — NOT registered)
Program.cs                 — AddHttpClient<IAuthHexClient>, AddJwtBearer + OnTokenValidated
                               (maps token → local User/Role → role_id/role_level claims)

Gotcha: don't run more than one of AddJwtBearer / AuthMiddleware / JwtMiddleware.CustomMiddlewareAttribute at once — each re-validates the same token independently.