This commit is contained in:
2026-07-16 14:23:23 +05:30
parent 582782b0fe
commit 7c5faabc2d
26 changed files with 1225 additions and 18 deletions
+45 -10
View File
@@ -18,7 +18,7 @@ Path-based versioning. Breaking changes bump the major version.
```
Authorization: Bearer <access_token>
```
- Every endpoint requires a valid **Bearer JWT**; unauthenticated → `401`. Tokens are issued by the **external AuthHex IdP** (not ERPCore) — **RS256**, issuer `AuthHex`, audience `AuthHexClient`. ERPCore validates them against AuthHex's static RSA public key (no JWKS) and admits only holders of the configured ERP `UserType`/`Role` (door policy) → otherwise `403`.
- Every endpoint requires a valid **Bearer JWT**, sent either as an `Authorization: Bearer <token>` header or as the `erp_at` httpOnly cookie issued by `AuthController` (§2.0); unauthenticated → `401`. Tokens are issued by the **external AuthHex IdP** (not ERPCore) — **RS256**, issuer `AuthHex`, audience `AuthHexClient`. ERPCore validates them against AuthHex's static RSA public key (no JWKS) and admits only holders of the configured ERP `UserType`/`Role` (door policy) → otherwise `403`.
- **Per-endpoint RBAC is NOT enforced in Phase 1** (FR-X-01): any ERP-admitted user may call any endpoint.
- The **audit actor** is AuthHex's custom **`UserId` (GUID)** claim, mapped to a local shadow user (`int`). Clients never send `createdBy`; the server derives it (docs/10 A.4).
@@ -59,15 +59,50 @@ Domain errors add a stable `code` (catalog §7):
## 2. Master Data
### 2.0 Auth — **external (AuthHex IdP); not an ERPCore endpoint**
> **Superseded (2026-07-14).** ERPCore no longer exposes `/auth/login`. Login, registration and recovery are owned by the
> separate **AuthHex** service (e.g. `POST /api/loginUser` with `{ identifier, password }`), which returns an **RS256** JWT
> (issuer `AuthHex`, audience `AuthHexClient`; claims `UserId` (GUID), `UserTypeCode`, `RoleCode`, `NIC`, …). ERPCore only
> **validates** that Bearer token and provisions a local shadow user (docs/10 A.4). The old shape is retained here for history:
```json
POST {AuthHex}/api/loginUser { "identifier": "…", "password": "••••••••" }
// 200 → an RS256 access token (Bearer). Bad credentials → 401. Token → ERPCore Authorization: Bearer <token>.
```
### 2.0 Auth — **`AuthController`, proxying the external AuthHex IdP**
> **Superseding note (2026-07-16).** Un-superseded: the frontend no longer calls AuthHex directly. All of the endpoints
> below live on ERPCore under `/api/v1/auth/*` (`Controllers/AuthController.cs`), each forwarding to the matching AuthHex
> `functionName` (see the project-root `API_REFERENCE.md` for AuthHex's own contract) via `IAuthHexClient`. Session-issuing
> endpoints deliver AuthHex's access/refresh tokens as **httpOnly Secure cookies** (`erp_at`, `erp_rt`) plus a non-httpOnly
> `XSRF-TOKEN` cookie (docs/02-SECURITY.md §B.2) — response bodies never contain raw tokens. Mutating, cookie-authenticated
> requests must echo the CSRF cookie value in an `X-XSRF-TOKEN` header or receive `403 CSRF_TOKEN_MISMATCH`; Bearer-header
> callers (Swagger, service-to-service) are exempt. The JWT bearer handler also accepts the `erp_at` cookie in place of an
> `Authorization` header (docs/10 A.4 Auth proxy), so every other `/api/v1/*` controller keeps working unchanged either way.
| Route | AuthHex function | Auth |
|---|---|---|
| `POST /api/v1/auth/register` | registerUser | Anonymous |
| `POST /api/v1/auth/login` | loginUser | Anonymous |
| `POST /api/v1/auth/login/otp/verify` | VerifyOtpForLogin | Anonymous |
| `POST /api/v1/auth/refresh-token` | refreshToken | Anonymous (reads `erp_rt` cookie) |
| `GET /api/v1/auth/users/{userId}` | getUserDetails | Anonymous* |
| `GET /api/v1/auth/sessions` | getUserSessions | Required |
| `POST /api/v1/auth/status` | ChangeUserStatus | Required + CSRF |
| `POST /api/v1/auth/lock` | LockUserAccount | Required + CSRF |
| `POST /api/v1/auth/change-password` | ChangeUserPassword | Required + CSRF |
| `POST /api/v1/auth/verify-password` | VerifyPassword | Required |
| `POST /api/v1/auth/logout` | LogoutUser | Anonymous* |
| `PUT /api/v1/auth/me` | UpdateUser | Required + CSRF |
| `POST /api/v1/auth/2fa/initiate` | initiateTwoFASetup | Required + CSRF |
| `POST /api/v1/auth/2fa/complete` | completeTwoFASetup | Required + CSRF |
| `POST /api/v1/auth/2fa/verify` | verifyTwoFA | Required + CSRF |
| `POST /api/v1/auth/2fa/disable` | disableTwoFA | Required + CSRF |
| `GET /api/v1/auth/2fa/status` | getTwoFAStatus | Required |
| `POST /api/v1/auth/recovery/forgot-password` | forgotPassword | Anonymous |
| `POST /api/v1/auth/recovery/verify-otp` | verifyOTP | Anonymous |
| `POST /api/v1/auth/recovery/reset-password` | resetPassword | Anonymous |
| `POST /api/v1/auth/recovery/reset-password-token` | resetPasswordWithToken | Anonymous |
| `POST /api/v1/auth/availability` | IsAvailable | Anonymous |
| `POST /api/v1/auth/otp/send` | sendOtp | Anonymous |
| `POST /api/v1/auth/otp/verify` | VerifyOTP (Alt) | Anonymous |
\* `getUserDetails` and `LogoutUser` are anonymous because AuthHex itself resolves them from the request payload rather
than the bearer token — carried over from AuthHex's own design, not introduced by this proxy. Tracked as an accepted risk
in docs/02-SECURITY.md Part A.
Request/response field shapes match AuthHex's own payloads one-for-one (project-root `API_REFERENCE.md` §3–§5), except
session-issuing responses omit `AccessToken`/`RefreshToken` (cookie-delivered instead) and `refreshToken` is read from the
`erp_rt` cookie rather than the request body.
### 2.1 Items
#### `GET /items`