@carecard/jwt-read 3.1.15 → 3.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: carecard-workspace-standards
3
- description: Follow the shared SO_CareCardCa/CareCard workspace coding, testing, repository, dependency, shared package, frontend, database, API response, and security standards. Use before modifying, testing, reviewing, or debugging any api-*, pkg-*, app-*, website, dashboard, or other CareCard repository in this workspace, especially when choosing validation commands, package boundaries, TypeScript types, dependencies, API contracts, database logic, service patterns, or frontend architecture.
3
+ description: Follow the shared SO_CareCardCa/CareCard workspace coding, testing, repository, dependency, shared package, frontend, database, API response, and security standards. Use before modifying, testing, reviewing, or debugging any ms-*, pkg-*, app-*, website, dashboard, or other CareCard repository in this workspace, especially when choosing validation commands, package boundaries, TypeScript types, dependencies, API contracts, database logic, service patterns, or frontend architecture.
4
4
  ---
5
5
 
6
6
  # CareCard Workspace Standards
@@ -23,7 +23,7 @@ Use before modifying, testing, reviewing, or debugging any CareCard workspace re
23
23
 
24
24
  - `.agents/config.toml`
25
25
  - `.agents/skills`
26
- - `api-*` services
26
+ - `ms-*` services
27
27
  - `pkg-*` packages
28
28
  - `app-*` frontends
29
29
  - `.husky` and `.junie` validation guidance
@@ -52,7 +52,7 @@ Use before modifying, testing, reviewing, or debugging any CareCard workspace re
52
52
 
53
53
  Treat `/Users/pankajpriscilla/SO_CareCardCa` as a collection of independent Git
54
54
  repositories, not as one monorepo. Work from the specific child repository
55
- being changed. Each `api-*`, `pkg-*`, and `app-*` directory has its own package
55
+ being changed. Each `ms-*`, `pkg-*`, and `app-*` directory has its own package
56
56
  scripts, Git status, test commands, style, naming, structure, test framework,
57
57
  and Husky hooks.
58
58
 
@@ -183,11 +183,11 @@ build artifacts, logs, or `.DS_Store`.
183
183
  `notFound404`, `appErrorHandler`, error throw helpers, case converters, and
184
184
  `ApiErrorType`.
185
185
  - Do not create or maintain duplicated common response or error helpers inside
186
- each `api-*` service.
186
+ each `ms-*` service.
187
187
  - Keep service-local response code limited to service-specific mapping or
188
188
  wiring.
189
189
  - Add broadly useful shared functionality to the relevant `pkg-*` package
190
- instead of duplicating it locally in an `api-*` or `app-*` project.
190
+ instead of duplicating it locally in an `ms-*` or `app-*` project.
191
191
  - When changing a `pkg-*` package, write package tests first, increase that
192
192
  package's minor version in `package.json`, run `npm install` in the package,
193
193
  update consuming projects to the new package version, run `npm install` in
@@ -206,10 +206,10 @@ build artifacts, logs, or `.DS_Store`.
206
206
 
207
207
  ## Backend Microservices
208
208
 
209
- Most JavaScript `api-*` services use CommonJS, Mocha, Supertest, Docker Compose
209
+ Most JavaScript `ms-*` services use CommonJS, Mocha, Supertest, Docker Compose
210
210
  database tests, `@carecard/*` packages, and `sub-apps`
211
- controller/router/model patterns. TypeScript services such as `api-contact-us`
212
- and `api-template-ts` use Jest or TypeScript tooling and should keep their
211
+ controller/router/model patterns. TypeScript services such as `ms-contact-us`
212
+ and `ms-template-ts` use Jest or TypeScript tooling and should keep their
213
213
  existing TypeScript style.
214
214
 
215
215
  - Keep environment-specific files explicit: `.env.development`, `.env.test`,
@@ -283,9 +283,9 @@ existing TypeScript style.
283
283
  - Keep tests readable and domain-specific.
284
284
  - Tests must cover desired or happy paths and prevention or rejection of
285
285
  undesired behavior.
286
- - JavaScript `api-*` services usually use Mocha, Supertest,
286
+ - JavaScript `ms-*` services usually use Mocha, Supertest,
287
287
  `test/index.test.js`, and Docker-backed PostgreSQL scripts.
288
- - TypeScript `api-*` services usually use Jest and `tests/index.test.ts`.
288
+ - TypeScript `ms-*` services usually use Jest and `tests/index.test.ts`.
289
289
  - `pkg-*` packages usually use Mocha plus TypeScript type tests where present.
290
290
  - `app-dashboard` uses Vitest, React Testing Library, mock API tests, and
291
291
  Selenium for end-to-end flows.
@@ -306,8 +306,8 @@ existing TypeScript style.
306
306
  ## Dashboard Frontend
307
307
 
308
308
  `app-dashboard` is a Next.js App Router TypeScript app using MUI, React Query,
309
- `next-intl`, and shared CareCard utilities. It consumes `api-auth`,
310
- `api-institutions`, `api-contact-us`, and `api-user-profiles` through service
309
+ `next-intl`, and shared CareCard utilities. It consumes `ms-auth`,
310
+ `ms-institutions`, `ms-contact-us`, and `ms-user-profiles` through service
311
311
  modules.
312
312
 
313
313
  - Keep backend URL definitions centralized in `src/services/api.routes.ts`.
@@ -366,12 +366,34 @@ the authenticated dashboard.
366
366
  - Keep CareCard package usage consistent with the service or app being changed.
367
367
  - When standardizing response or error behavior, prefer `@carecard/common-util`
368
368
  `3.1.15` because it contains response and error functions aligned with
369
- `api-auth`.
369
+ `ms-auth`.
370
370
  - If package version changes are required, update lockfiles and verify affected
371
371
  services or apps.
372
372
  - Avoid broad dependency upgrades as part of feature or refactor work unless
373
373
  the task is specifically about dependencies.
374
374
 
375
+ ## Auth Service RLS Contract
376
+
377
+ - `ms-auth` follows the shared PostgreSQL/RLS pattern from `ms-template-js`: auth tables live in the `carecard` schema, RLS is enabled and forced on every auth table, and application runtime queries use the unprivileged database role.
378
+ - Auth table policies allow normal JWT or server-auth users to access only
379
+ self-owned rows. Do not add redundant `user_id = <jwt sub>` SQL predicates to
380
+ duplicate self-row checks when RLS owns the authorization decision.
381
+ - A JWT or server-auth payload containing `roles: ["ad"]` is the auth-service
382
+ super-admin signal and can perform any action on auth tables. Dashboard code
383
+ may map that role to `super_admin`, but backend auth RLS must not require a
384
+ separate database role row for that bypass.
385
+ - Public auth flows such as registration, login, server-auth session create and
386
+ introspection, confirmation, recovery, visitor creation, and service user
387
+ lookup must use narrow system contexts (`system_create`, `system_login`,
388
+ `system_confirm`, `system_recovery`, `system_visitor`, `system_service`)
389
+ instead of privileged runtime queries.
390
+
391
+ - `ms-auth` controller exports use concise action names such as `loginUser`,
392
+ `registerUser`, `getUserDetail`, and `renewJwt`; route middleware and router
393
+ placement express whether a flow is public, authenticated, admin-only, or
394
+ service-only, so avoid `public`, `protected`, `admin`, or `Handler` suffixes
395
+ in new controller names.
396
+
375
397
  ## Security Requirements
376
398
 
377
399
  - Treat authentication, authorization, JWT, password, email confirmation,
@@ -7,11 +7,13 @@ description: Use when changing pkg-jwt-read JWT parsing, middleware, visitor tok
7
7
 
8
8
  ## Purpose
9
9
 
10
- CareCard JWT read package for parsing, request attachment, visitor tokens, role mapping, authorization middleware, exports, and tests.
10
+ CareCard JWT read package for parsing, request attachment, visitor tokens, role mapping, JWT-or-server-auth authorization middleware, exports, and tests.
11
11
 
12
12
  ## When To Use
13
13
 
14
- - Use when changing pkg-jwt-read JWT parsing, middleware, visitor tokens, role checks, auth context, package exports, or tests.
14
+ - Use when changing pkg-jwt-read JWT parsing, server-auth introspection
15
+ middleware, visitor tokens, role checks, auth context, package exports, or
16
+ tests.
15
17
  - Pair with `carecard-workspace-standards` when the task affects shared CareCard conventions or cross-repository contracts.
16
18
 
17
19
  ## When Not To Use
@@ -53,7 +55,9 @@ CareCard JWT read package for parsing, request attachment, visitor tokens, role
53
55
  Use this skill when working inside `pkg-jwt-read`, the `@carecard/jwt-read`
54
56
  package. It provides utilities for reading, parsing, verifying, and attaching
55
57
  JWT data in the CareCard ecosystem. It depends on `@carecard/auth-util` for
56
- low-level cryptographic operations.
58
+ low-level cryptographic operations. It also exposes middleware helpers that
59
+ allow `ms-*` services to accept either an `ms-auth` JWT or an opaque server-auth
60
+ token introspected by `ms-auth`.
57
61
 
58
62
  Use `$carecard-workspace-standards` for shared workspace, dependency, package,
59
63
  testing, and security rules. Legacy `pkg-jwt-read/.codex` and
@@ -95,9 +99,14 @@ depend on those folders being present.
95
99
  - Middleware-like functions for Express, such as `verifyJwtAndRole`.
96
100
  - Service-to-service JWT verification and extraction helpers:
97
101
  `jwtValidateAndExtractService` and `jwtVerifyService`.
102
+ - JWT-or-server-auth helpers: `jwtValidateAndExtractOrServerAuth`,
103
+ `jwtVerifyOrServerAuth`, and `jwtVerifyOrServerAuthAndHasRole`.
98
104
  - Extraction of `sub`/clientId and other claims from JWT objects.
99
105
  - Expiration checks and TTL calculations.
100
106
  - Request attachment behavior for authenticated JWT objects and visitor tokens.
107
+ - Server-auth request attachment behavior that normalizes introspected claims
108
+ into `req.jwt.payload` with `authMode: "server-auth"` and
109
+ `auth_mode: "server-auth"`.
101
110
  - Integration with `@carecard/common-util` for standardized login and
102
111
  authorization errors.
103
112
 
@@ -108,6 +117,11 @@ JWT creation functions do not belong in this package. Service-to-service token
108
117
  creation belongs in `@carecard/auth-util` via `jwtCreateServiceToken` and
109
118
  `jwtCreateServiceAuthorizationHeader`.
110
119
 
120
+ Opaque server-auth token creation, hashing, persistence, and introspection
121
+ belong in `ms-auth`. This package only accepts a caller-provided introspector
122
+ function and normalizes valid introspection claims into the existing request
123
+ JWT context.
124
+
111
125
  Service JWTs must follow standard JWT claim semantics. They use `iss` for the
112
126
  sending service, `sub` for the sending service identity, `aud` for the
113
127
  receiving service, and NumericDate `iat`, `exp`, and optional `nbf` claims.
@@ -126,6 +140,10 @@ role names, such as `ad` and `admin`.
126
140
  by focused tests.
127
141
  - Preserve existing role semantics unless a task explicitly changes
128
142
  authorization behavior.
143
+ - Preserve the original JWT `roles` array on request context. `ms-auth` RLS
144
+ treats a payload containing `ad` as the auth-service super-admin signal;
145
+ dashboard code may map that to `super_admin`, but middleware must not hide,
146
+ rename, or drop the raw role payload needed by backend database contexts.
129
147
 
130
148
  ## NoThrow And Error Behavior
131
149
 
@@ -141,17 +159,20 @@ role names, such as `ad` and `admin`.
141
159
 
142
160
  ## Security Rules
143
161
 
144
- - Treat JWT parsing, signature verification, visitor tokens, authorization
145
- roles, and request context as security-sensitive.
162
+ - Treat JWT parsing, signature verification, server-auth introspection,
163
+ visitor tokens, authorization roles, and request context as
164
+ security-sensitive.
146
165
  - Do not log JWTs, token fragments, public/private keys, decoded payloads,
147
166
  authorization headers, visitor headers, or sensitive request data.
148
- - Keep missing headers, invalid signatures, expired tokens, role failures, and
149
- used-token errors behaviorally distinct where existing APIs do so.
167
+ - Keep missing headers, invalid signatures, expired tokens, revoked or invalid
168
+ server-auth tokens, role failures, and used-token errors behaviorally
169
+ distinct where existing APIs do so.
150
170
 
151
171
  ## Types And API Contracts
152
172
 
153
- - Model JWT header, payload, request attachment, visitor attachment, role, and
154
- context shapes explicitly in `index.d.ts`.
173
+ - Model JWT header, payload, server-auth introspection claims, request
174
+ attachment, visitor attachment, role, and context shapes explicitly in
175
+ `index.d.ts`.
155
176
  - Prefer `AuthenticatedRequest`, `JwtHeader`, `JwtPayload`, `JwtParts`,
156
177
  `JwtRequestObject`, `VisitorRequestObject`, and `JwtContext` over loose
157
178
  request objects.
@@ -171,7 +192,8 @@ role names, such as `ad` and `admin`.
171
192
  - `test/types.test.ts` verifies TypeScript declarations with `tsc`.
172
193
  - Add focused tests for valid JWTs, invalid JWTs, missing headers, role checks,
173
194
  visitor token extraction, expiration behavior, request attachment behavior,
174
- NoThrow behavior, and context-bound helpers when those areas change.
195
+ server-auth introspection success/failure, NoThrow behavior, and
196
+ context-bound helpers when those areas change.
175
197
  - Set `NODE_ENV=test` where tests or scripts require it.
176
198
  - Keep tests deterministic and avoid real external services.
177
199
 
package/index.d.ts CHANGED
@@ -32,6 +32,10 @@ export interface JwtPayload {
32
32
  sub?: string;
33
33
  /** Roles assigned to the user. */
34
34
  roles?: string[];
35
+ /** Authentication mode used by app-facing services. */
36
+ authMode?: 'jwt' | 'server-auth' | string;
37
+ /** Server-auth session identifier when an opaque server-auth token was used. */
38
+ sessionId?: string;
35
39
  /** Any other custom payload fields. */
36
40
  [key: string]: any;
37
41
  }
@@ -76,6 +80,30 @@ export interface AuthenticatedRequest extends Request {
76
80
  visitor?: VisitorRequestObject | null;
77
81
  }
78
82
 
83
+ export interface ServerAuthIntrospectionClaims {
84
+ valid?: boolean;
85
+ sub?: string;
86
+ userId?: string;
87
+ user_id?: string;
88
+ email?: string;
89
+ emailVerified?: boolean;
90
+ email_verified?: boolean;
91
+ emailConfirmed?: boolean;
92
+ email_confirmed?: boolean;
93
+ roles?: string[];
94
+ sessionId?: string;
95
+ session_id?: string;
96
+ exp?: number | string;
97
+ expiresAt?: string;
98
+ expires_at?: string;
99
+ [key: string]: any;
100
+ }
101
+
102
+ export type ServerAuthIntrospector = (
103
+ token: string,
104
+ req: AuthenticatedRequest,
105
+ ) => Promise<ServerAuthIntrospectionClaims> | ServerAuthIntrospectionClaims;
106
+
79
107
  /**
80
108
  * Returns a middleware that verifies a JWT from the 'Authorization: Bearer <token>' header
81
109
  * and extracts it into req.jwt. Throws an error if invalid.
@@ -147,6 +175,27 @@ export function jwtVerifyAndHasRole(
147
175
  customErrorFunction?: () => void,
148
176
  ): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
149
177
 
178
+ /**
179
+ * Returns middleware that accepts either an ms-auth JWT or an opaque server-auth token.
180
+ * Server-auth tokens are validated by the supplied introspector on every request.
181
+ */
182
+ export function jwtVerifyOrServerAuth(
183
+ publicKey: string,
184
+ serverAuthIntrospector: ServerAuthIntrospector,
185
+ customErrorFunction?: () => void,
186
+ ): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
187
+
188
+ /**
189
+ * Returns middleware that accepts either an ms-auth JWT or server-auth token and
190
+ * then checks that the authenticated user has the required role.
191
+ */
192
+ export function jwtVerifyOrServerAuthAndHasRole(
193
+ userRole: string,
194
+ publicKey: string,
195
+ serverAuthIntrospector: ServerAuthIntrospector,
196
+ customErrorFunction?: () => void,
197
+ ): (req: AuthenticatedRequest, res: Response, next: NextFunction) => void;
198
+
150
199
  /**
151
200
  * Gets the full name of a role from its code (e.g., 'ad' -> 'admin').
152
201
  */
@@ -189,6 +238,17 @@ export function jwtValidateAndExtractService(
189
238
  customErrorFunction?: () => void,
190
239
  ): void;
191
240
 
241
+ /**
242
+ * Validates the Authorization header as either an ms-auth JWT or an opaque
243
+ * server-auth token and extracts the result into req.jwt.
244
+ */
245
+ export function jwtValidateAndExtractOrServerAuth(
246
+ req: AuthenticatedRequest,
247
+ publicKey: string,
248
+ serverAuthIntrospector: ServerAuthIntrospector,
249
+ customErrorFunction?: () => void,
250
+ ): Promise<void>;
251
+
192
252
  /**
193
253
  * Validates the JWT from a custom header and extracts it into req.jwt.
194
254
  */
package/index.js CHANGED
@@ -17,11 +17,14 @@ module.exports = {
17
17
  jwtGetContext: jwtRoles.getContext,
18
18
  jwtValidateAndExtract: jwtLib.validateAndExtractJwtObject,
19
19
  jwtValidateAndExtractService: jwtLib.validateAndExtractServiceJwtObject,
20
+ jwtValidateAndExtractOrServerAuth: jwtLib.validateAndExtractJwtOrServerAuthObject,
20
21
  jwtValidateAndExtractWebToken: jwtLib.validateAndExtractWebToken,
21
22
  jwtValidateAndExtractNoThrow: jwtLib.validateAndExtractJwtObjectNoThrow,
22
23
  jwtValidateAndExtractWebTokenNoThrow: jwtLib.validateAndExtractWebTokenObjectNoThrow,
23
24
  jwtValidateAndExtractVisitorNoThrow: jwtLib.validateAndExtractVisitorObjectNoThrow,
24
25
  jwtVerifyService: jwtLib.verifyServiceJwt,
26
+ jwtVerifyOrServerAuth: jwtLib.verifyJwtOrServerAuth,
27
+ jwtVerifyOrServerAuthAndHasRole: jwtLib.verifyJwtOrServerAuthAndHasRole,
25
28
 
26
29
  /** @deprecated use jwtVerify */
27
30
  verifyJwt: jwtLib.verifyJwt,
package/lib/jwtLib.js CHANGED
@@ -122,6 +122,15 @@ function validateAndExtractServiceJwtObject(req, publicKey, expectedIssuer, expe
122
122
  return req;
123
123
  }
124
124
 
125
+ async function validateAndExtractJwtOrServerAuthObject(req, publicKey, serverAuthIntrospector, customErrorFunction) {
126
+ if (tryValidateAndExtractJwtObject(req, publicKey)) return req;
127
+
128
+ const serverAuthToken = _validateServerAuthToken(req, customErrorFunction);
129
+ const claims = await introspectServerAuthToken(serverAuthIntrospector, serverAuthToken, req, customErrorFunction);
130
+ attachServerAuthClaims(req, claims, customErrorFunction);
131
+ return req;
132
+ }
133
+
125
134
  function validateAndExtractWebTokenObjectNoThrow(req, publicKey, headerName) {
126
135
  return _validateAndExtractGenericNoThrow(req, publicKey, r => _validateWebTokenNoThrow(r, headerName), _extractJwtObjectNoThrow, 'jwt');
127
136
  }
@@ -165,6 +174,30 @@ function verifyServiceJwt(publicKey, expectedIssuer, expectedAudience, customErr
165
174
  };
166
175
  }
167
176
 
177
+ function verifyJwtOrServerAuth(publicKey, serverAuthIntrospector, customErrorFunction) {
178
+ return async function (req, res, next) {
179
+ try {
180
+ await validateAndExtractJwtOrServerAuthObject(req, publicKey, serverAuthIntrospector, customErrorFunction);
181
+ next();
182
+ } catch (err) {
183
+ next(err);
184
+ }
185
+ };
186
+ }
187
+
188
+ function verifyJwtOrServerAuthAndHasRole(role, publicKey, serverAuthIntrospector, customErrorFunction) {
189
+ return async function (req, res, next) {
190
+ try {
191
+ await validateAndExtractJwtOrServerAuthObject(req, publicKey, serverAuthIntrospector, customErrorFunction);
192
+ const isRoleExist = doesJwtUserHasRole(req, role);
193
+ _isLoginRequired(isRoleExist, customErrorFunction);
194
+ next();
195
+ } catch (err) {
196
+ next(err);
197
+ }
198
+ };
199
+ }
200
+
168
201
  function verifyWebToken(publicKey, headerName, customErrorFunction) {
169
202
  return function (req, res, next) {
170
203
  try {
@@ -222,6 +255,80 @@ function _isLoginRequired(hasRequiredRole, customErrorFunction) {
222
255
  }
223
256
  }
224
257
 
258
+ function tryValidateAndExtractJwtObject(req, publicKey) {
259
+ const jwtString = _validateJwtNoThrow(req);
260
+ if (!jwtString || !isJwtString(jwtString)) return false;
261
+
262
+ const isJwtSignatureValid = jwtVerifySignedToken(jwtString, publicKey);
263
+ if (!isJwtSignatureValid) {
264
+ if (req) req.jwt = null;
265
+ return false;
266
+ }
267
+
268
+ _extractJwtObjectNoThrow(req, jwtString);
269
+ return Boolean(req?.jwt);
270
+ }
271
+
272
+ function _validateServerAuthToken(req, customErrorFunction) {
273
+ const authorizationHeader = req?.get?.('Authorization') || req?.get?.('authorization');
274
+ const token = _extractJwtNoThrow(authorizationHeader);
275
+ if (token) return token;
276
+
277
+ throwError(customErrorFunction);
278
+ return null;
279
+ }
280
+
281
+ async function introspectServerAuthToken(serverAuthIntrospector, token, req, customErrorFunction) {
282
+ if (typeof serverAuthIntrospector !== 'function') {
283
+ throwError(customErrorFunction);
284
+ }
285
+
286
+ const claims = await serverAuthIntrospector(token, req);
287
+ if (!claims || claims.valid === false) {
288
+ throwError(customErrorFunction);
289
+ }
290
+
291
+ return claims;
292
+ }
293
+
294
+ function attachServerAuthClaims(req, claims, customErrorFunction) {
295
+ const payload = createServerAuthPayload(claims);
296
+ if (!payload.sub) {
297
+ if (req) req.jwt = null;
298
+ throwError(customErrorFunction);
299
+ }
300
+
301
+ req.jwt = {
302
+ header: {
303
+ alg: 'opaque',
304
+ typ: 'ServerAuth',
305
+ },
306
+ payload,
307
+ };
308
+ _attachJwtMethods(req.jwt);
309
+ }
310
+
311
+ function createServerAuthPayload(claims) {
312
+ return {
313
+ sub: claims.sub || claims.userId || claims.user_id,
314
+ email: claims.email || '',
315
+ email_verified: claims.emailVerified || claims.email_verified || claims.emailConfirmed || claims.email_confirmed || false,
316
+ roles: Array.isArray(claims.roles) ? claims.roles : [],
317
+ authMode: 'server-auth',
318
+ auth_mode: 'server-auth',
319
+ sessionId: claims.sessionId || claims.session_id,
320
+ session_id: claims.session_id || claims.sessionId,
321
+ exp: readEpochSeconds(claims.exp || claims.expiresAt || claims.expires_at),
322
+ };
323
+ }
324
+
325
+ function readEpochSeconds(value) {
326
+ if (value === undefined || value === null || value === '') return undefined;
327
+ if (typeof value === 'number') return normalizeSeconds(value);
328
+ const millis = Date.parse(value);
329
+ return Number.isFinite(millis) ? Math.floor(millis / 1000) : undefined;
330
+ }
331
+
225
332
  function normalizeSeconds(value) {
226
333
  if (!Number.isFinite(value)) return null;
227
334
  return value > 1000000000000 ? Math.floor(value / 1000) : Math.floor(value);
@@ -434,6 +541,7 @@ module.exports = {
434
541
  _extractJwtObject,
435
542
  validateAndExtractJwtObject,
436
543
  validateAndExtractServiceJwtObject,
544
+ validateAndExtractJwtOrServerAuthObject,
437
545
  validateAndExtractWebToken,
438
546
  jwtAgeInSeconds,
439
547
  isJwtExpired,
@@ -442,6 +550,8 @@ module.exports = {
442
550
  visitorClientId,
443
551
  verifyJwtAndRole,
444
552
  verifyServiceJwt,
553
+ verifyJwtOrServerAuth,
554
+ verifyJwtOrServerAuthAndHasRole,
445
555
  verifyJwt,
446
556
  verifyWebTokenNoThrow,
447
557
  verifyWebToken,
@@ -461,6 +571,9 @@ module.exports = {
461
571
  _extractVisitorObjectNoThrow,
462
572
  _extractWebToken,
463
573
  _isLoginRequired,
574
+ tryValidateAndExtractJwtObject,
575
+ attachServerAuthClaims,
576
+ createServerAuthPayload,
464
577
  _isServiceJwtFor,
465
578
  _attachJwtMethods,
466
579
  _attachVisitorMethods,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carecard/jwt-read",
3
- "version": "3.1.15",
3
+ "version": "3.1.16",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/CareCard-ca/pkg-jwt-read.git"
package/readme.md CHANGED
@@ -3,7 +3,10 @@
3
3
  ![Tests Passing](https://github.com/CareCard-ca/pkg-jwt-read/actions/workflows/ci.yml/badge.svg)
4
4
  ![Coverage](https://img.shields.io/badge/Coverage-80%25-orange)
5
5
 
6
- Utility package for reading, parsing, and verifying JWTs in the CareCard ecosystem.
6
+ Utility package for reading, parsing, and verifying JWTs in the CareCard
7
+ ecosystem. It also provides the shared request middleware used by `ms-*`
8
+ services to accept either an `ms-auth` JWT or an opaque server-auth token
9
+ introspected by `ms-auth`.
7
10
 
8
11
  ## Features
9
12
 
@@ -13,6 +16,8 @@ Utility package for reading, parsing, and verifying JWTs in the CareCard ecosyst
13
16
  - **Claims Extraction**: Easy extraction of `sub` (clientId) and other JWT payload claims.
14
17
  - **Expiration Management**: Helpers to check if a JWT is expired and calculate its remaining TTL.
15
18
  - **Service JWTs**: Helpers for verifying and extracting microservice-to-microservice JWTs with standard `iss`, `sub`, `aud`, `iat`, and `exp` claims.
19
+ - **JWT or Server Auth**: Middleware helpers that verify normal JWTs locally and
20
+ call a service-provided introspector for opaque server-auth tokens.
16
21
 
17
22
  ## Installation
18
23
 
@@ -60,6 +65,19 @@ console.log(getNameOfRole('ad')); // Result: 'admin'
60
65
  console.log(getCodeOfRole('super_admin')); // Result: 'su'
61
66
  ```
62
67
 
68
+ ### Auth RLS Role Semantics
69
+
70
+ `ms-auth` treats a JWT or server-auth payload containing `roles: ["ad"]` as the
71
+ auth-service super-admin signal for its RLS policies. Consumers may map `ad` to
72
+ UI/domain names such as `super_admin`, but middleware should preserve the
73
+ original roles array on the request context so services can make
74
+ database-context decisions consistently.
75
+
76
+ Docs that mention `ms-auth` controller internals should use concise action
77
+ names such as `loginUser`, `registerUser`, `getUserDetail`, and `renewJwt`.
78
+ Access level is conveyed by route middleware and endpoint placement, not by
79
+ `public`/`protected`/`admin`/`Handler` suffixes.
80
+
63
81
  ### Service-To-Service JWTs
64
82
 
65
83
  Use service JWT verification helpers for backend service calls. The sending
@@ -88,6 +106,33 @@ Service JWT payloads follow standard JWT semantics:
88
106
  - `iat`: issued-at NumericDate
89
107
  - `exp`: expiration NumericDate
90
108
 
109
+ ### JWT Or Server-Auth Middleware
110
+
111
+ Use the `OrServerAuth` helpers on app-facing `ms-*` routes that should accept
112
+ both current authentication modes. The JWT path verifies locally with the
113
+ `ms-auth` public key. The server-auth path calls the provided introspector,
114
+ which should send the opaque token to
115
+ `POST /api/v1/ms-auth/server-auth/introspect` with the receiving service's
116
+ service JWT.
117
+
118
+ ```javascript
119
+ const { jwtGetRoleCode, jwtVerifyOrServerAuth, jwtVerifyOrServerAuthAndHasRole } = require('@carecard/jwt-read');
120
+
121
+ const verifyUser = jwtVerifyOrServerAuth(msAuthPublicKey, token => introspectServerAuthTokenWithMsAuth(token), throwNotAuthorizedError);
122
+
123
+ const verifyAdmin = jwtVerifyOrServerAuthAndHasRole(
124
+ jwtGetRoleCode('admin'),
125
+ msAuthPublicKey,
126
+ token => introspectServerAuthTokenWithMsAuth(token),
127
+ throwNotAuthorizedError,
128
+ );
129
+ ```
130
+
131
+ The introspector must return claims for valid tokens. This package normalizes
132
+ those claims onto `req.jwt.payload` with `authMode: "server-auth"` and
133
+ `auth_mode: "server-auth"` so services can keep their existing JWT-backed
134
+ database context and role checks.
135
+
91
136
  ## Testing
92
137
 
93
138
  Run tests using: