@zintrust/core 0.7.8 → 0.9.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.
Files changed (34) hide show
  1. package/package.json +1 -1
  2. package/src/auth/LoginFlow.d.ts.map +1 -1
  3. package/src/auth/LoginFlow.js +1 -3
  4. package/src/cli/OptionalCliExtensions.d.ts +1 -0
  5. package/src/cli/OptionalCliExtensions.d.ts.map +1 -1
  6. package/src/cli/OptionalCliExtensions.js +11 -1
  7. package/src/cli/commands/MySqlProxyCommand.d.ts.map +1 -1
  8. package/src/cli/commands/MySqlProxyCommand.js +1 -1
  9. package/src/cli/commands/ScheduleStartCommand.d.ts.map +1 -1
  10. package/src/cli/commands/ScheduleStartCommand.js +14 -9
  11. package/src/cli/scaffolding/GovernanceScaffolder.d.ts.map +1 -1
  12. package/src/cli/scaffolding/GovernanceScaffolder.js +7 -2
  13. package/src/cli/scaffolding/ProjectScaffolder.d.ts.map +1 -1
  14. package/src/cli/scaffolding/ProjectScaffolder.js +3 -10
  15. package/src/helper/index.d.ts +6 -0
  16. package/src/helper/index.d.ts.map +1 -1
  17. package/src/helper/index.js +19 -8
  18. package/src/index.d.ts +7 -4
  19. package/src/index.d.ts.map +1 -1
  20. package/src/index.js +7 -5
  21. package/src/middleware/BulletproofAuthMiddleware.d.ts +2 -1
  22. package/src/middleware/BulletproofAuthMiddleware.d.ts.map +1 -1
  23. package/src/middleware/BulletproofAuthMiddleware.js +103 -39
  24. package/src/security/BulletproofDeviceStore.d.ts.map +1 -1
  25. package/src/security/BulletproofDeviceStore.js +144 -33
  26. package/src/security/JwtVerifier.d.ts +75 -0
  27. package/src/security/JwtVerifier.d.ts.map +1 -0
  28. package/src/security/JwtVerifier.js +336 -0
  29. package/src/templates/project/basic/app/Controllers/AuthController.ts.tpl +1 -1
  30. package/src/templates/project/basic/config/trace.ts.tpl +73 -0
  31. package/src/templates/project/basic/package.json.tpl +1 -1
  32. package/src/zintrust.plugins.d.ts +3 -6
  33. package/src/zintrust.plugins.d.ts.map +1 -1
  34. package/src/zintrust.plugins.js +3 -6
@@ -93,6 +93,59 @@ const parseBulletproofHeaders = (params) => {
93
93
  };
94
94
  return { ok: true, deviceId, timezone, signingHeaders };
95
95
  };
96
+ const isMissingDeviceStoreRegistration = (error) => {
97
+ if (!(error instanceof Error))
98
+ return false;
99
+ return (error.message.includes("Database connection '") && error.message.includes('is not registered'));
100
+ };
101
+ const normalizeStaticSecrets = (staticSecrets) => {
102
+ return (staticSecrets?.map((s) => (typeof s === 'string' ? s.trim() : '')).filter((s) => s !== '') ?? []);
103
+ };
104
+ const createDynamicSignedRequestAttempt = (params) => {
105
+ return async () => {
106
+ try {
107
+ return await SignedRequest.verify({
108
+ ...params.baseParams,
109
+ getSecretForKeyId: async (keyId) => {
110
+ const secretForKey = await params.getSecretForKeyId(keyId, params.req);
111
+ const normalized = typeof secretForKey === 'string' ? secretForKey.trim() : '';
112
+ return normalized === '' ? undefined : normalized;
113
+ },
114
+ });
115
+ }
116
+ catch (error) {
117
+ if (params.hasStaticFallback && isMissingDeviceStoreRegistration(error)) {
118
+ return { ok: false, code: 'UNKNOWN_KEY', message: 'Unknown key' };
119
+ }
120
+ throw error;
121
+ }
122
+ };
123
+ };
124
+ const collectSignedRequestAttempts = async (params) => {
125
+ const staticSecrets = normalizeStaticSecrets(params.staticSecrets);
126
+ const attemptResolvers = [
127
+ createDynamicSignedRequestAttempt({
128
+ req: params.req,
129
+ baseParams: params.baseParams,
130
+ getSecretForKeyId: params.getSecretForKeyId,
131
+ hasStaticFallback: staticSecrets.length > 0,
132
+ }),
133
+ ...staticSecrets.map((secret) => async () => {
134
+ return SignedRequest.verify({
135
+ ...params.baseParams,
136
+ getSecretForKeyId: () => secret,
137
+ });
138
+ }),
139
+ ];
140
+ return Promise.all(attemptResolvers.map(async (attemptResolver) => {
141
+ try {
142
+ return { result: await attemptResolver(), error: undefined };
143
+ }
144
+ catch (error) {
145
+ return { result: undefined, error };
146
+ }
147
+ }));
148
+ };
96
149
  const verifySignedRequest = async (params) => {
97
150
  const baseParams = {
98
151
  method: params.req.getMethod?.() ?? 'GET',
@@ -108,35 +161,35 @@ const verifySignedRequest = async (params) => {
108
161
  return params.verifyNonce(keyId, nonce, ttlMs);
109
162
  },
110
163
  };
111
- const staticSecrets = params.staticSecrets
112
- ?.map((s) => (typeof s === 'string' ? s.trim() : ''))
113
- .filter((s) => s !== '');
114
- const signed = staticSecrets !== undefined && staticSecrets.length > 0
115
- ? await (async () => {
116
- const attempts = await Promise.all(staticSecrets.map(async (secret) => SignedRequest.verify({
117
- ...baseParams,
118
- getSecretForKeyId: () => secret,
119
- })));
120
- // Preserve preference order based on provided secret list.
121
- for (const attempt of attempts) {
122
- if (attempt.ok === true)
123
- return attempt;
124
- }
125
- // If any attempt failed for a reason other than signature mismatch, return that.
126
- for (const attempt of attempts) {
127
- if (attempt.ok === false && attempt.code !== 'INVALID_SIGNATURE')
128
- return attempt;
129
- }
130
- return { ok: false, code: 'INVALID_SIGNATURE', message: 'Invalid signature' };
131
- })()
132
- : await SignedRequest.verify({
133
- ...baseParams,
134
- getSecretForKeyId: async (keyId) => {
135
- const secretForKey = await params.getSecretForKeyId(keyId, params.req);
136
- const normalized = typeof secretForKey === 'string' ? secretForKey.trim() : '';
137
- return normalized === '' ? undefined : normalized;
138
- },
139
- });
164
+ const attemptOutcomes = await collectSignedRequestAttempts({
165
+ req: params.req,
166
+ baseParams,
167
+ getSecretForKeyId: params.getSecretForKeyId,
168
+ staticSecrets: params.staticSecrets,
169
+ });
170
+ const attempts = attemptOutcomes.flatMap((attemptOutcome) => {
171
+ return attemptOutcome.result === undefined ? [] : [attemptOutcome.result];
172
+ });
173
+ if (attempts.length === 0) {
174
+ const firstError = attemptOutcomes.find((attemptOutcome) => attemptOutcome.error !== undefined)?.error;
175
+ if (firstError !== undefined) {
176
+ throw firstError;
177
+ }
178
+ return { ok: false, message: 'Unauthorized' };
179
+ }
180
+ let signed = attempts[0];
181
+ for (const attempt of attempts) {
182
+ if (attempt.ok === true) {
183
+ signed = attempt;
184
+ break;
185
+ }
186
+ }
187
+ if (signed.ok !== true) {
188
+ const nonSignatureFailure = attempts.find((attempt) => attempt.ok === false && attempt.code !== 'INVALID_SIGNATURE');
189
+ if (nonSignatureFailure !== undefined) {
190
+ signed = nonSignatureFailure;
191
+ }
192
+ }
140
193
  if (!signed.ok) {
141
194
  Logger.debug('Bulletproof auth signed-request verification failed', {
142
195
  code: signed.code,
@@ -296,20 +349,31 @@ const resolveSigningConfig = (options) => {
296
349
  const signingSecretFromEnv = Env.get('BULLETPROOF_SIGNING_SECRET', appKeyFallback).trim();
297
350
  const signingSecret = (options.signingSecret ?? signingSecretFromEnv).trim();
298
351
  const backupSecrets = parseBackupSecrets(Env.get('BULLETPROOF_SIGNING_SECRET_BK', ''));
352
+ const staticSigningSecrets = dedupeSecrets(backupSecrets.filter((secret) => secret.trim() !== '' && secret.trim() !== signingSecret));
299
353
  const hasCustomResolver = typeof options.getSecretForKeyId === 'function';
300
- const getSecretForKeyId = hasCustomResolver
301
- ? options.getSecretForKeyId
302
- : async (keyId) => {
303
- const device = await BulletproofDeviceStore.findByDeviceId(keyId).catch(() => null);
304
- if (device?.signingSecret)
354
+ let getSecretForKeyId;
355
+ if (hasCustomResolver) {
356
+ getSecretForKeyId = options.getSecretForKeyId;
357
+ }
358
+ else if (isNonEmptyString(signingSecret)) {
359
+ getSecretForKeyId = () => signingSecret;
360
+ }
361
+ else {
362
+ getSecretForKeyId = async (keyId) => {
363
+ const device = await BulletproofDeviceStore.findByDeviceId(keyId);
364
+ if (device && typeof device.signingSecret === 'string' && device.signingSecret !== '') {
305
365
  return device.signingSecret;
306
- return signingSecret === '' ? undefined : signingSecret;
366
+ }
367
+ return undefined;
307
368
  };
369
+ }
308
370
  const verifyNonce = options.verifyNonce ?? NonceReplay.createMemoryVerifier();
309
- const staticSigningSecrets = hasCustomResolver
310
- ? undefined
311
- : dedupeSecrets([signingSecret, ...backupSecrets]);
312
- return { getSecretForKeyId, verifyNonce, staticSigningSecrets };
371
+ const resolvedStaticSigningSecrets = hasCustomResolver ? undefined : staticSigningSecrets;
372
+ return {
373
+ getSecretForKeyId,
374
+ verifyNonce,
375
+ staticSigningSecrets: resolvedStaticSigningSecrets,
376
+ };
313
377
  };
314
378
  const resolveBulletproof = (options) => {
315
379
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"BulletproofDeviceStore.d.ts","sourceRoot":"","sources":["../../../src/security/BulletproofDeviceStore.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,MAAM,6BAA6B,GAAG,uBAAuB,GACjE,QAAQ,CAAC;IACP,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB,CAAC,CAAC;AAkFL,eAAO,MAAM,sBAAsB;6BACF,MAAM,GAAG,OAAO,CAAC,6BAA6B,GAAG,IAAI,CAAC;mBAiBhE,uBAAuB,GAAG,OAAO,CAAC,6BAA6B,CAAC;+BAsCpD,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;EAYvD,CAAC;AAEH,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"BulletproofDeviceStore.d.ts","sourceRoot":"","sources":["../../../src/security/BulletproofDeviceStore.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;CAClB,CAAC,CAAC;AAEH,MAAM,MAAM,6BAA6B,GAAG,uBAAuB,GACjE,QAAQ,CAAC;IACP,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB,CAAC,CAAC;AA0ML,eAAO,MAAM,sBAAsB;6BACF,MAAM,GAAG,OAAO,CAAC,6BAA6B,GAAG,IAAI,CAAC;mBA4BhE,uBAAuB,GAAG,OAAO,CAAC,6BAA6B,CAAC;+BAuDpD,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;EAavD,CAAC;AAEH,eAAe,sBAAsB,CAAC"}
@@ -17,6 +17,18 @@ const getTable = () => {
17
17
  const normalizeString = (value) => {
18
18
  return typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined;
19
19
  };
20
+ const normalizeRequiredDeviceId = (value) => {
21
+ const normalized = normalizeString(value);
22
+ if (normalized !== undefined)
23
+ return normalized;
24
+ throw ErrorFactory.createValidationError('Bulletproof device store requires a non-empty deviceId');
25
+ };
26
+ const normalizeRequiredSigningSecret = (value) => {
27
+ const normalized = normalizeString(value);
28
+ if (normalized !== undefined)
29
+ return normalized;
30
+ throw ErrorFactory.createValidationError('Bulletproof device store requires a non-empty signingSecret');
31
+ };
20
32
  const normalizeDate = (value) => {
21
33
  if (value instanceof Date && Number.isFinite(value.getTime()))
22
34
  return value;
@@ -26,10 +38,36 @@ const normalizeDate = (value) => {
26
38
  }
27
39
  return undefined;
28
40
  };
29
- const createMissingTableError = (table, error) => {
30
- return ErrorFactory.createConfigError(`Bulletproof device store table '${table}' is missing required columns (run migrations)`, {
41
+ const getErrorMessage = (error) => {
42
+ return error instanceof Error ? error.message : String(error);
43
+ };
44
+ const isSchemaError = (error) => {
45
+ const message = getErrorMessage(error).toLowerCase();
46
+ return (message.includes('no such table') ||
47
+ message.includes('no such column') ||
48
+ message.includes('unknown column') ||
49
+ message.includes('does not exist') ||
50
+ message.includes('undefined column') ||
51
+ message.includes('missing column'));
52
+ };
53
+ const createStoreError = (table, operation, error, details) => {
54
+ if (typeof error === 'object' && error !== null) {
55
+ const code = error.code;
56
+ if (typeof code === 'string' && code === 'CONFIG_ERROR') {
57
+ return error;
58
+ }
59
+ }
60
+ const message = getErrorMessage(error);
61
+ const detailPayload = { table, operation, error: message, ...details };
62
+ if (isSchemaError(error)) {
63
+ return ErrorFactory.createConfigError(`Bulletproof device store table '${table}' is missing required columns (run migrations)`, detailPayload);
64
+ }
65
+ return ErrorFactory.createDatabaseError(`Bulletproof device store ${operation} failed`, detailPayload);
66
+ };
67
+ const createInvalidStoredRecordError = (table, deviceId) => {
68
+ return ErrorFactory.createConfigError('Bulletproof device store returned an invalid record', {
31
69
  table,
32
- error: error instanceof Error ? error.message : String(error),
70
+ ...(deviceId === undefined ? {} : { deviceId }),
33
71
  });
34
72
  };
35
73
  const toStoredRecord = (row) => {
@@ -57,63 +95,135 @@ const toStoredRecord = (row) => {
57
95
  : { updatedAt: normalizeDate(row['updated_at']) }),
58
96
  };
59
97
  };
60
- const toPayload = (record) => {
98
+ const buildInsertPayload = (record) => {
61
99
  const lastSeenAt = record.lastSeenAt.toISOString();
100
+ const deviceId = normalizeRequiredDeviceId(record.deviceId);
101
+ const signingSecret = normalizeRequiredSigningSecret(record.signingSecret);
62
102
  return {
63
103
  user_id: normalizeString(record.userId) ?? null,
64
- device_id: record.deviceId,
65
- signing_secret: record.signingSecret,
104
+ device_id: deviceId,
105
+ signing_secret: signingSecret,
66
106
  user_agent: normalizeString(record.userAgent) ?? null,
67
107
  last_seen_at: lastSeenAt,
68
108
  created_at: lastSeenAt,
69
109
  updated_at: lastSeenAt,
70
110
  };
71
111
  };
112
+ const buildUpdatePayload = (record) => {
113
+ const lastSeenAt = record.lastSeenAt.toISOString();
114
+ const signingSecret = normalizeRequiredSigningSecret(record.signingSecret);
115
+ return {
116
+ user_id: normalizeString(record.userId) ?? null,
117
+ signing_secret: signingSecret,
118
+ user_agent: normalizeString(record.userAgent) ?? null,
119
+ last_seen_at: lastSeenAt,
120
+ updated_at: lastSeenAt,
121
+ };
122
+ };
123
+ const buildIgnoreInsert = (db, table, columns, conflictColumns) => {
124
+ const columnList = columns.join(', ');
125
+ const placeholders = columns.map(() => '?').join(', ');
126
+ const driver = db.getType();
127
+ if (driver === 'sqlite' || driver === 'd1' || driver === 'd1-remote') {
128
+ return `INSERT OR IGNORE INTO ${table} (${columnList}) VALUES (${placeholders})`;
129
+ }
130
+ if (driver === 'mysql') {
131
+ return `INSERT IGNORE INTO ${table} (${columnList}) VALUES (${placeholders})`;
132
+ }
133
+ if (driver === 'postgresql') {
134
+ return `INSERT INTO ${table} (${columnList}) VALUES (${placeholders}) ON CONFLICT (${conflictColumns.join(', ')}) DO NOTHING`;
135
+ }
136
+ if (driver === 'sqlserver') {
137
+ const sourceColumns = columns.map((_, index) => `v${index + 1}`);
138
+ const selectClause = sourceColumns.map((name) => `? AS ${name}`).join(', ');
139
+ const conflictClause = conflictColumns
140
+ .map((column) => `target.${column} = source.${column}`)
141
+ .join(' AND ');
142
+ const insertValues = columns.map((column) => `source.${column}`).join(', ');
143
+ const sourceProjection = columns
144
+ .map((column, index) => `${sourceColumns[index]} AS ${column}`)
145
+ .join(', ');
146
+ return [
147
+ `MERGE INTO ${table} WITH (HOLDLOCK) AS target`,
148
+ `USING (SELECT ${sourceProjection} FROM (SELECT ${selectClause}) seed) AS source`,
149
+ `ON ${conflictClause}`,
150
+ `WHEN NOT MATCHED THEN INSERT (${columnList}) VALUES (${insertValues});`,
151
+ ].join(' ');
152
+ }
153
+ return `INSERT INTO ${table} (${columnList}) VALUES (${placeholders})`;
154
+ };
72
155
  export const BulletproofDeviceStore = Object.freeze({
73
156
  async findByDeviceId(deviceId) {
74
157
  if (!isNonEmptyString(deviceId))
75
158
  return null;
76
159
  const db = useDatabase(undefined, getConnection());
77
160
  const table = getTable();
161
+ const normalizedDeviceId = deviceId.trim();
78
162
  try {
79
163
  const row = await db
80
164
  .table(table)
81
- .where('device_id', '=', deviceId.trim())
165
+ .where('device_id', '=', normalizedDeviceId)
82
166
  .first();
83
- return row === null ? null : toStoredRecord(row);
167
+ if (row === null) {
168
+ return null;
169
+ }
170
+ const normalized = toStoredRecord(row);
171
+ if (normalized === null) {
172
+ throw createInvalidStoredRecordError(table, normalizedDeviceId);
173
+ }
174
+ return normalized;
84
175
  }
85
176
  catch (error) {
86
- throw createMissingTableError(table, error);
177
+ throw createStoreError(table, 'lookup', error, { deviceId: normalizedDeviceId });
87
178
  }
88
179
  },
89
180
  async upsert(record) {
90
181
  const db = useDatabase(undefined, getConnection());
91
182
  const table = getTable();
92
- const payload = toPayload(record);
183
+ const deviceId = normalizeRequiredDeviceId(record.deviceId);
184
+ const normalizedUserId = normalizeString(record.userId);
185
+ const normalizedUserAgent = normalizeString(record.userAgent);
186
+ const normalizedRecord = {
187
+ ...record,
188
+ deviceId,
189
+ signingSecret: normalizeRequiredSigningSecret(record.signingSecret),
190
+ ...(normalizedUserId === undefined ? {} : { userId: normalizedUserId }),
191
+ ...(normalizedUserAgent === undefined ? {} : { userAgent: normalizedUserAgent }),
192
+ };
193
+ const insertPayload = buildInsertPayload(normalizedRecord);
194
+ const updatePayload = buildUpdatePayload(normalizedRecord);
195
+ const insertColumns = Object.keys(insertPayload);
196
+ const insertValues = insertColumns.map((column) => insertPayload[column]);
197
+ const insertSql = buildIgnoreInsert(db, table, insertColumns, ['device_id']);
93
198
  try {
94
- await db.table(table).where('device_id', '=', record.deviceId).update(payload);
95
- const existing = await db
96
- .table(table)
97
- .where('device_id', '=', record.deviceId)
98
- .first();
99
- if (existing === null) {
100
- await db.table(table).insert(payload);
101
- }
102
- const stored = await db
103
- .table(table)
104
- .where('device_id', '=', record.deviceId)
105
- .first();
106
- const normalized = stored === null ? null : toStoredRecord(stored);
107
- if (normalized === null) {
108
- throw ErrorFactory.createConfigError('Bulletproof device store returned an invalid record', {
109
- table,
110
- deviceId: record.deviceId,
111
- });
112
- }
113
- return normalized;
199
+ return await db.transaction(async (transactionDb) => {
200
+ await transactionDb.table(table).where('device_id', '=', deviceId).update(updatePayload);
201
+ try {
202
+ await transactionDb.execute(insertSql, insertValues);
203
+ }
204
+ catch (error) {
205
+ const duplicateMessage = getErrorMessage(error).toLowerCase();
206
+ const isDuplicateKeyError = duplicateMessage.includes('duplicate') ||
207
+ duplicateMessage.includes('unique constraint') ||
208
+ duplicateMessage.includes('unique failed') ||
209
+ duplicateMessage.includes('duplicate key');
210
+ if (!isDuplicateKeyError) {
211
+ throw error;
212
+ }
213
+ }
214
+ const stored = await transactionDb
215
+ .table(table)
216
+ .where('device_id', '=', deviceId)
217
+ .first();
218
+ const normalized = stored === null ? null : toStoredRecord(stored);
219
+ if (normalized === null) {
220
+ throw createInvalidStoredRecordError(table, deviceId);
221
+ }
222
+ return normalized;
223
+ });
114
224
  }
115
225
  catch (error) {
116
- throw createMissingTableError(table, error);
226
+ throw createStoreError(table, 'upsert', error, { deviceId });
117
227
  }
118
228
  },
119
229
  async removeByDeviceId(deviceId) {
@@ -121,11 +231,12 @@ export const BulletproofDeviceStore = Object.freeze({
121
231
  return;
122
232
  const db = useDatabase(undefined, getConnection());
123
233
  const table = getTable();
234
+ const normalizedDeviceId = deviceId.trim();
124
235
  try {
125
- await db.table(table).where('device_id', '=', deviceId.trim()).delete();
236
+ await db.table(table).where('device_id', '=', normalizedDeviceId).delete();
126
237
  }
127
238
  catch (error) {
128
- throw createMissingTableError(table, error);
239
+ throw createStoreError(table, 'delete', error, { deviceId: normalizedDeviceId });
129
240
  }
130
241
  },
131
242
  });
@@ -0,0 +1,75 @@
1
+ import type { JwtPayload } from './JwtManager';
2
+ export type JwtVerifierAlgorithm = 'RS256';
3
+ type JwtVerifierJsonWebKey = Readonly<{
4
+ alg?: string;
5
+ crv?: string;
6
+ d?: string;
7
+ dp?: string;
8
+ dq?: string;
9
+ e?: string;
10
+ ext?: boolean;
11
+ k?: string;
12
+ key_ops?: string[];
13
+ kid?: string;
14
+ kty?: string;
15
+ n?: string;
16
+ oth?: unknown[];
17
+ p?: string;
18
+ q?: string;
19
+ qi?: string;
20
+ use?: string;
21
+ x?: string;
22
+ x5c?: string[];
23
+ x5t?: string;
24
+ 'x5t#S256'?: string;
25
+ x5u?: string;
26
+ y?: string;
27
+ }>;
28
+ export type JwtVerifierJwk = JwtVerifierJsonWebKey & Readonly<{
29
+ kid?: string;
30
+ alg?: string;
31
+ use?: string;
32
+ }>;
33
+ export type JwtVerifierJwksDocument = Readonly<{
34
+ keys: readonly JwtVerifierJwk[];
35
+ }>;
36
+ export type JwtVerifierFailureReason = 'invalid_token_format' | 'invalid_header' | 'invalid_payload' | 'missing_kid' | 'key_not_found' | 'jwks_fetch_failed' | 'invalid_jwks' | 'unsupported_algorithm' | 'invalid_jwk' | 'invalid_signature' | 'issuer_mismatch' | 'audience_mismatch' | 'token_expired' | 'token_not_yet_valid';
37
+ export type JwtVerifierFailure = Readonly<{
38
+ ok: false;
39
+ reason: JwtVerifierFailureReason;
40
+ message: string;
41
+ details?: unknown;
42
+ }>;
43
+ export type JwtVerifierSuccess = Readonly<{
44
+ ok: true;
45
+ payload: JwtPayload;
46
+ header: Readonly<Record<string, unknown>>;
47
+ jwk: JwtVerifierJwk;
48
+ cacheHit?: boolean;
49
+ }>;
50
+ export type JwtVerifierResult = JwtVerifierSuccess | JwtVerifierFailure;
51
+ export type JwtVerifierCommonInput = Readonly<{
52
+ token: string;
53
+ algorithm?: JwtVerifierAlgorithm;
54
+ issuer?: string | readonly string[];
55
+ audience?: string | readonly string[];
56
+ nowMs?: number;
57
+ }>;
58
+ export type JwtVerifierWithJwkInput = JwtVerifierCommonInput & Readonly<{
59
+ jwk: JwtVerifierJwk;
60
+ }>;
61
+ export type JwtVerifierWithJwksInput = JwtVerifierCommonInput & Readonly<{
62
+ jwksUrl: string;
63
+ cacheKey?: string;
64
+ cacheTtlSeconds?: number;
65
+ fetcher?: typeof fetch;
66
+ }>;
67
+ export declare const JwtVerifier: Readonly<{
68
+ clearCache: (cacheKey?: string) => void;
69
+ verifyWithJwk: (input: JwtVerifierWithJwkInput) => Promise<JwtPayload>;
70
+ verifyWithJwkResult: (input: JwtVerifierWithJwkInput) => Promise<JwtVerifierResult>;
71
+ verifyWithJwks: (input: JwtVerifierWithJwksInput) => Promise<JwtPayload>;
72
+ verifyWithJwksResult: (input: JwtVerifierWithJwksInput) => Promise<JwtVerifierResult>;
73
+ }>;
74
+ export default JwtVerifier;
75
+ //# sourceMappingURL=JwtVerifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JwtVerifier.d.ts","sourceRoot":"","sources":["../../../src/security/JwtVerifier.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAC;AAE3C,KAAK,qBAAqB,GAAG,QAAQ,CAAC;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC;IAChB,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,CAAC,EAAE,MAAM,CAAC;CACZ,CAAC,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,qBAAqB,GAChD,QAAQ,CAAC;IACP,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC,CAAC;AAEL,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,IAAI,EAAE,SAAS,cAAc,EAAE,CAAC;CACjC,CAAC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAChC,sBAAsB,GACtB,gBAAgB,GAChB,iBAAiB,GACjB,aAAa,GACb,eAAe,GACf,mBAAmB,GACnB,cAAc,GACd,uBAAuB,GACvB,aAAa,GACb,mBAAmB,GACnB,iBAAiB,GACjB,mBAAmB,GACnB,eAAe,GACf,qBAAqB,CAAC;AAE1B,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,wBAAwB,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1C,GAAG,EAAE,cAAc,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAExE,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAAC;AAEH,MAAM,MAAM,uBAAuB,GAAG,sBAAsB,GAC1D,QAAQ,CAAC;IACP,GAAG,EAAE,cAAc,CAAC;CACrB,CAAC,CAAC;AAEL,MAAM,MAAM,wBAAwB,GAAG,sBAAsB,GAC3D,QAAQ,CAAC;IACP,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB,CAAC,CAAC;AA+aL,eAAO,MAAM,WAAW;4BATO,MAAM,KAAG,IAAI;2BAZR,uBAAuB,KAAG,OAAO,CAAC,UAAU,CAAC;iCA/BvC,uBAAuB,KAAG,OAAO,CAAC,iBAAiB,CAAC;4BAqCzD,wBAAwB,KAAG,OAAO,CAAC,UAAU,CAAC;kCAhC1E,wBAAwB,KAC9B,OAAO,CAAC,iBAAiB,CAAC;EAoD3B,CAAC;AAEH,eAAe,WAAW,CAAC"}