@lucaapp/service-utils 5.21.0 → 5.22.0

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.
package/dist/index.d.ts CHANGED
@@ -14,6 +14,7 @@ export * from './lib/logger';
14
14
  export * from './lib/datetime';
15
15
  export * from './lib/proxy';
16
16
  export * from './lib/random';
17
+ export * from './lib/sessionRevocation';
17
18
  export * from './lib/crypto';
18
19
  export * from './lib/phone';
19
20
  export * from './lib/http';
package/dist/index.js CHANGED
@@ -30,6 +30,7 @@ __exportStar(require("./lib/logger"), exports);
30
30
  __exportStar(require("./lib/datetime"), exports);
31
31
  __exportStar(require("./lib/proxy"), exports);
32
32
  __exportStar(require("./lib/random"), exports);
33
+ __exportStar(require("./lib/sessionRevocation"), exports);
33
34
  __exportStar(require("./lib/crypto"), exports);
34
35
  __exportStar(require("./lib/phone"), exports);
35
36
  __exportStar(require("./lib/http"), exports);
@@ -0,0 +1,2 @@
1
+ export * from './sessionRevocation';
2
+ export * from './types';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./sessionRevocation"), exports);
18
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,7 @@
1
+ import { IsOperatorTokenRevokedParams, IsRevokedOperatorJwtParams, RevokeOperatorTokensParams } from './types';
2
+ export declare const OPERATOR_REVOCATION_KEY_PREFIX = "revoked:operator:";
3
+ export declare const OPERATOR_REVOCATION_TTL_SECONDS: number;
4
+ export declare const buildOperatorRevocationKey: (operatorUuid: string) => string;
5
+ export declare const revokeOperatorTokens: ({ redis, logger, operatorUuid, }: RevokeOperatorTokensParams) => Promise<void>;
6
+ export declare const isOperatorTokenRevoked: ({ redis, logger, operatorUuid, issuedAtSeconds, }: IsOperatorTokenRevokedParams) => Promise<boolean>;
7
+ export declare const isRevokedOperatorJwt: ({ redis, logger, payload, operatorRole, }: IsRevokedOperatorJwtParams) => Promise<boolean>;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isRevokedOperatorJwt = exports.isOperatorTokenRevoked = exports.revokeOperatorTokens = exports.buildOperatorRevocationKey = exports.OPERATOR_REVOCATION_TTL_SECONDS = exports.OPERATOR_REVOCATION_KEY_PREFIX = void 0;
4
+ exports.OPERATOR_REVOCATION_KEY_PREFIX = 'revoked:operator:';
5
+ exports.OPERATOR_REVOCATION_TTL_SECONDS = 24 * 60 * 60;
6
+ const REDIS_GET_TIMEOUT_MS = 50;
7
+ const buildOperatorRevocationKey = (operatorUuid) => `${exports.OPERATOR_REVOCATION_KEY_PREFIX}${operatorUuid}`;
8
+ exports.buildOperatorRevocationKey = buildOperatorRevocationKey;
9
+ const nowInSeconds = () => Math.floor(Date.now() / 1000);
10
+ const parseTimestamp = (raw) => {
11
+ if (raw === null)
12
+ return null;
13
+ const parsed = Number.parseInt(raw, 10);
14
+ return Number.isFinite(parsed) ? parsed : null;
15
+ };
16
+ const withTimeout = async (promise, timeoutMs) => {
17
+ let timer;
18
+ try {
19
+ return await Promise.race([
20
+ promise,
21
+ new Promise((_resolve, reject) => {
22
+ timer = setTimeout(() => reject(new Error('redis revocation lookup timed out')), timeoutMs);
23
+ }),
24
+ ]);
25
+ }
26
+ finally {
27
+ if (timer)
28
+ clearTimeout(timer);
29
+ }
30
+ };
31
+ const revokeOperatorTokens = async ({ redis, logger, operatorUuid, }) => {
32
+ try {
33
+ const key = (0, exports.buildOperatorRevocationKey)(operatorUuid);
34
+ const existing = parseTimestamp(await redis.get(key)) ?? 0;
35
+ const invalidatedAt = Math.max(existing, nowInSeconds());
36
+ await redis.setex(key, exports.OPERATOR_REVOCATION_TTL_SECONDS, String(invalidatedAt));
37
+ }
38
+ catch (error) {
39
+ logger.error(error, 'failed to write operator token revocation key');
40
+ }
41
+ };
42
+ exports.revokeOperatorTokens = revokeOperatorTokens;
43
+ const isOperatorTokenRevoked = async ({ redis, logger, operatorUuid, issuedAtSeconds, }) => {
44
+ try {
45
+ const key = (0, exports.buildOperatorRevocationKey)(operatorUuid);
46
+ const invalidatedAt = parseTimestamp(await withTimeout(redis.get(key), REDIS_GET_TIMEOUT_MS));
47
+ if (invalidatedAt === null)
48
+ return false;
49
+ return issuedAtSeconds <= invalidatedAt;
50
+ }
51
+ catch (error) {
52
+ logger.warn(error, 'operator token revocation check failed open');
53
+ return false;
54
+ }
55
+ };
56
+ exports.isOperatorTokenRevoked = isOperatorTokenRevoked;
57
+ const isRevokedOperatorJwt = async ({ redis, logger, payload, operatorRole, }) => {
58
+ if (payload.role !== operatorRole ||
59
+ typeof payload.sub !== 'string' ||
60
+ typeof payload.iat !== 'number') {
61
+ return false;
62
+ }
63
+ return (0, exports.isOperatorTokenRevoked)({
64
+ redis,
65
+ logger,
66
+ operatorUuid: payload.sub,
67
+ issuedAtSeconds: payload.iat,
68
+ });
69
+ };
70
+ exports.isRevokedOperatorJwt = isRevokedOperatorJwt;
@@ -0,0 +1,27 @@
1
+ import { Logger } from 'pino';
2
+ export interface RevocationRedis {
3
+ get(key: string): Promise<string | null>;
4
+ setex(key: string, ttlSeconds: number, value: string): Promise<unknown>;
5
+ }
6
+ export interface RevocableJwtPayload {
7
+ role?: string;
8
+ sub?: string;
9
+ iat?: number;
10
+ }
11
+ export interface RevokeOperatorTokensParams {
12
+ redis: RevocationRedis;
13
+ logger: Logger;
14
+ operatorUuid: string;
15
+ }
16
+ export interface IsOperatorTokenRevokedParams {
17
+ redis: RevocationRedis;
18
+ logger: Logger;
19
+ operatorUuid: string;
20
+ issuedAtSeconds: number;
21
+ }
22
+ export interface IsRevokedOperatorJwtParams {
23
+ redis: RevocationRedis;
24
+ logger: Logger;
25
+ payload: RevocableJwtPayload;
26
+ operatorRole: string;
27
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucaapp/service-utils",
3
- "version": "5.21.0",
3
+ "version": "5.22.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [