@nauth-toolkit/core 0.3.0 → 0.3.2

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 (87) hide show
  1. package/dist/bootstrap.d.ts.map +1 -1
  2. package/dist/bootstrap.js +53 -0
  3. package/dist/bootstrap.js.map +1 -1
  4. package/dist/dto/admin-api-key.dto.d.ts +69 -0
  5. package/dist/dto/admin-api-key.dto.d.ts.map +1 -0
  6. package/dist/dto/admin-api-key.dto.js +144 -0
  7. package/dist/dto/admin-api-key.dto.js.map +1 -0
  8. package/dist/dto/admin-signup-social.dto.d.ts +1 -1
  9. package/dist/dto/admin-signup-social.dto.js +1 -1
  10. package/dist/dto/admin-signup.dto.d.ts +1 -1
  11. package/dist/dto/admin-signup.dto.js +1 -1
  12. package/dist/dto/api-key.dto.d.ts +153 -0
  13. package/dist/dto/api-key.dto.d.ts.map +1 -0
  14. package/dist/dto/api-key.dto.js +222 -0
  15. package/dist/dto/api-key.dto.js.map +1 -0
  16. package/dist/dto/change-password.dto.d.ts +2 -2
  17. package/dist/dto/change-password.dto.js +2 -2
  18. package/dist/dto/index.d.ts +2 -0
  19. package/dist/dto/index.d.ts.map +1 -1
  20. package/dist/dto/index.js +3 -0
  21. package/dist/dto/index.js.map +1 -1
  22. package/dist/dto/reset-password.dto.d.ts +1 -1
  23. package/dist/dto/reset-password.dto.js +1 -1
  24. package/dist/dto/respond-challenge.dto.d.ts +1 -1
  25. package/dist/dto/respond-challenge.dto.js +1 -1
  26. package/dist/dto/signup.dto.d.ts +1 -1
  27. package/dist/dto/signup.dto.js +1 -1
  28. package/dist/entities/api-key.entity.d.ts +129 -0
  29. package/dist/entities/api-key.entity.d.ts.map +1 -0
  30. package/dist/entities/api-key.entity.js +143 -0
  31. package/dist/entities/api-key.entity.js.map +1 -0
  32. package/dist/entities/index.d.ts +1 -0
  33. package/dist/entities/index.d.ts.map +1 -1
  34. package/dist/entities/index.js +3 -1
  35. package/dist/entities/index.js.map +1 -1
  36. package/dist/enums/auth-audit-event-type.enum.d.ts +25 -1
  37. package/dist/enums/auth-audit-event-type.enum.d.ts.map +1 -1
  38. package/dist/enums/auth-audit-event-type.enum.js +27 -0
  39. package/dist/enums/auth-audit-event-type.enum.js.map +1 -1
  40. package/dist/enums/error-codes.enum.d.ts +56 -1
  41. package/dist/enums/error-codes.enum.d.ts.map +1 -1
  42. package/dist/enums/error-codes.enum.js +58 -0
  43. package/dist/enums/error-codes.enum.js.map +1 -1
  44. package/dist/exceptions/nauth.exception.d.ts.map +1 -1
  45. package/dist/exceptions/nauth.exception.js +13 -0
  46. package/dist/exceptions/nauth.exception.js.map +1 -1
  47. package/dist/handlers/api-key.handler.d.ts +45 -0
  48. package/dist/handlers/api-key.handler.d.ts.map +1 -0
  49. package/dist/handlers/api-key.handler.js +99 -0
  50. package/dist/handlers/api-key.handler.js.map +1 -0
  51. package/dist/handlers/auth.handler.d.ts.map +1 -1
  52. package/dist/handlers/auth.handler.js +6 -0
  53. package/dist/handlers/auth.handler.js.map +1 -1
  54. package/dist/index.d.ts +7 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +8 -1
  57. package/dist/index.js.map +1 -1
  58. package/dist/interfaces/config.interface.d.ts +118 -0
  59. package/dist/interfaces/config.interface.d.ts.map +1 -1
  60. package/dist/openapi/components.schemas.json +329 -7
  61. package/dist/platform/interfaces.d.ts +8 -0
  62. package/dist/platform/interfaces.d.ts.map +1 -1
  63. package/dist/schemas/auth-config.schema.d.ts +175 -0
  64. package/dist/schemas/auth-config.schema.d.ts.map +1 -1
  65. package/dist/schemas/auth-config.schema.js +26 -1
  66. package/dist/schemas/auth-config.schema.js.map +1 -1
  67. package/dist/services/api-key.service.d.ts +178 -0
  68. package/dist/services/api-key.service.d.ts.map +1 -0
  69. package/dist/services/api-key.service.js +475 -0
  70. package/dist/services/api-key.service.js.map +1 -0
  71. package/dist/utils/index.d.ts +1 -0
  72. package/dist/utils/index.d.ts.map +1 -1
  73. package/dist/utils/index.js +1 -0
  74. package/dist/utils/index.js.map +1 -1
  75. package/dist/utils/ip-match.d.ts +44 -0
  76. package/dist/utils/ip-match.d.ts.map +1 -0
  77. package/dist/utils/ip-match.js +135 -0
  78. package/dist/utils/ip-match.js.map +1 -0
  79. package/dist/utils/setup/get-repositories.d.ts +2 -1
  80. package/dist/utils/setup/get-repositories.d.ts.map +1 -1
  81. package/dist/utils/setup/get-repositories.js +2 -0
  82. package/dist/utils/setup/get-repositories.js.map +1 -1
  83. package/dist/utils/setup/init-services.d.ts +4 -2
  84. package/dist/utils/setup/init-services.d.ts.map +1 -1
  85. package/dist/utils/setup/init-services.js +7 -0
  86. package/dist/utils/setup/init-services.js.map +1 -1
  87. package/package.json +2 -2
@@ -0,0 +1,475 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiKeyService = void 0;
4
+ const crypto_1 = require("crypto");
5
+ const nauth_exception_1 = require("../exceptions/nauth.exception");
6
+ const error_codes_enum_1 = require("../enums/error-codes.enum");
7
+ const auth_audit_event_type_enum_1 = require("../enums/auth-audit-event-type.enum");
8
+ const context_storage_1 = require("../utils/context-storage");
9
+ const dto_validator_1 = require("../utils/dto-validator");
10
+ const ip_match_1 = require("../utils/ip-match");
11
+ const api_key_dto_1 = require("../dto/api-key.dto");
12
+ const admin_api_key_dto_1 = require("../dto/admin-api-key.dto");
13
+ /**
14
+ * API Key Service
15
+ *
16
+ * Owns the full API key lifecycle and validation. Keys authenticate **as their owning user**.
17
+ *
18
+ * Identity convention (matches the rest of the toolkit):
19
+ * - **Self-service** methods (`createKey`, `listKeys`, `updateKey`, `revokeKey`, `deleteKey`)
20
+ * act on the *currently authenticated* user, resolved from request context. They take no
21
+ * user identifier.
22
+ * - **Admin** methods (`adminCreateKey`, `adminListKeys`, `adminUpdateKey`, `adminRevokeKey`,
23
+ * `adminDeleteKey`) act on a target user identified by `sub`. Protect them with admin auth.
24
+ *
25
+ * Every public method takes a request DTO and returns a response DTO, and validates the DTO at
26
+ * runtime (via {@link ensureValidatedDto}) so non-NestJS callers are protected from invalid input.
27
+ *
28
+ * Security:
29
+ * - Only a SHA-256 hash of the key is stored; the plaintext is returned once at creation.
30
+ * - Presented keys are hashed and looked up by that hash (indexed, unique) — the plaintext never
31
+ * leaves the caller and is never compared field-by-field.
32
+ * - Per-key IP allowlists (optional) restrict which source IPs may use a key.
33
+ */
34
+ class ApiKeyService {
35
+ apiKeyRepository;
36
+ userRepository;
37
+ config;
38
+ logger;
39
+ auditService;
40
+ constructor(apiKeyRepository, userRepository, config, logger, auditService) {
41
+ this.apiKeyRepository = apiKeyRepository;
42
+ this.userRepository = userRepository;
43
+ this.config = config;
44
+ this.logger = logger;
45
+ this.auditService = auditService;
46
+ this.logger?.log?.('ApiKeyService initialized');
47
+ }
48
+ // ==========================================================================
49
+ // Self-service (acting user resolved from auth context)
50
+ // ==========================================================================
51
+ /**
52
+ * Create an API key for the currently authenticated user.
53
+ *
54
+ * @throws {NAuthException} API_KEY_CREATION_DISABLED, API_KEY_LIMIT_REACHED,
55
+ * API_KEY_EXPIRY_REQUIRED, API_KEY_INDEFINITE_NOT_ALLOWED, API_KEY_EXPIRY_TOO_LONG,
56
+ * VALIDATION_FAILED, FORBIDDEN (not authenticated)
57
+ */
58
+ async createKey(dto) {
59
+ dto = await (0, dto_validator_1.ensureValidatedDto)(api_key_dto_1.CreateApiKeyDTO, dto);
60
+ const user = this.getCurrentUserOrThrow();
61
+ return this.createForUser(user.id, dto, false);
62
+ }
63
+ /**
64
+ * List the current user's API keys (sanitized; never includes secrets).
65
+ */
66
+ async listKeys() {
67
+ const user = this.getCurrentUserOrThrow();
68
+ return this.listForUser(user.id);
69
+ }
70
+ /**
71
+ * Update the current user's key (label and/or IP allowlist). Secret and expiry are immutable.
72
+ *
73
+ * @throws {NAuthException} API_KEY_NOT_FOUND, VALIDATION_FAILED, FORBIDDEN
74
+ */
75
+ async updateKey(dto) {
76
+ dto = await (0, dto_validator_1.ensureValidatedDto)(api_key_dto_1.UpdateApiKeyDTO, dto);
77
+ const user = this.getCurrentUserOrThrow();
78
+ return this.updateForUser(user.id, dto.keyId, dto.name, dto.allowedIps);
79
+ }
80
+ /**
81
+ * Revoke (soft-delete) one of the current user's keys.
82
+ *
83
+ * @throws {NAuthException} API_KEY_NOT_FOUND, FORBIDDEN
84
+ */
85
+ async revokeKey(dto) {
86
+ dto = await (0, dto_validator_1.ensureValidatedDto)(api_key_dto_1.RevokeApiKeyDTO, dto);
87
+ const user = this.getCurrentUserOrThrow();
88
+ return this.revokeForUser(user.id, dto.keyId, 'user_revoked');
89
+ }
90
+ /**
91
+ * Permanently delete one of the current user's keys.
92
+ *
93
+ * @throws {NAuthException} API_KEY_NOT_FOUND, FORBIDDEN
94
+ */
95
+ async deleteKey(dto) {
96
+ dto = await (0, dto_validator_1.ensureValidatedDto)(api_key_dto_1.DeleteApiKeyDTO, dto);
97
+ const user = this.getCurrentUserOrThrow();
98
+ return this.deleteForUser(user.id, dto.keyId);
99
+ }
100
+ // ==========================================================================
101
+ // Admin (target user identified by sub) — protect with admin authentication
102
+ // ==========================================================================
103
+ /**
104
+ * Create an API key on behalf of a user. Bypasses `allowUserCreation`, but still enforces
105
+ * `maxKeysPerUser`, expiry, and IP-restriction rules.
106
+ *
107
+ * @throws {NAuthException} USER_NOT_FOUND, or any API_KEY_* creation error
108
+ */
109
+ async adminCreateKey(dto) {
110
+ dto = await (0, dto_validator_1.ensureValidatedDto)(admin_api_key_dto_1.AdminCreateApiKeyDTO, dto);
111
+ const userId = await this.resolveUserIdBySub(dto.sub);
112
+ return this.createForUser(userId, dto, true);
113
+ }
114
+ /**
115
+ * List a user's API keys (admin).
116
+ *
117
+ * @throws {NAuthException} USER_NOT_FOUND
118
+ */
119
+ async adminListKeys(dto) {
120
+ dto = await (0, dto_validator_1.ensureValidatedDto)(admin_api_key_dto_1.AdminManageApiKeyDTO, dto);
121
+ const userId = await this.resolveUserIdBySub(dto.sub);
122
+ return this.listForUser(userId);
123
+ }
124
+ /**
125
+ * Update a user's key (admin).
126
+ *
127
+ * @throws {NAuthException} USER_NOT_FOUND, API_KEY_NOT_FOUND, VALIDATION_FAILED
128
+ */
129
+ async adminUpdateKey(dto) {
130
+ dto = await (0, dto_validator_1.ensureValidatedDto)(admin_api_key_dto_1.AdminUpdateApiKeyDTO, dto);
131
+ const userId = await this.resolveUserIdBySub(dto.sub);
132
+ return this.updateForUser(userId, dto.keyId, dto.name, dto.allowedIps);
133
+ }
134
+ /**
135
+ * Revoke (soft-delete) a user's key (admin).
136
+ *
137
+ * @throws {NAuthException} USER_NOT_FOUND, API_KEY_NOT_FOUND, VALIDATION_FAILED
138
+ */
139
+ async adminRevokeKey(dto) {
140
+ dto = await (0, dto_validator_1.ensureValidatedDto)(admin_api_key_dto_1.AdminManageApiKeyDTO, dto);
141
+ const keyId = this.requireKeyId(dto.keyId);
142
+ const userId = await this.resolveUserIdBySub(dto.sub);
143
+ return this.revokeForUser(userId, keyId, 'admin_revoked');
144
+ }
145
+ /**
146
+ * Permanently delete a user's key (admin).
147
+ *
148
+ * @throws {NAuthException} USER_NOT_FOUND, API_KEY_NOT_FOUND, VALIDATION_FAILED
149
+ */
150
+ async adminDeleteKey(dto) {
151
+ dto = await (0, dto_validator_1.ensureValidatedDto)(admin_api_key_dto_1.AdminManageApiKeyDTO, dto);
152
+ const keyId = this.requireKeyId(dto.keyId);
153
+ const userId = await this.resolveUserIdBySub(dto.sub);
154
+ return this.deleteForUser(userId, keyId);
155
+ }
156
+ // ==========================================================================
157
+ // Validation (internal auth path — called by the handler / guard)
158
+ // ==========================================================================
159
+ /**
160
+ * Validate a presented API key and resolve its owner.
161
+ *
162
+ * On any failure this throws a precise {@link NAuthException} (access denied) — callers
163
+ * MUST NOT fall back to other credentials.
164
+ *
165
+ * @param rawKey - The full plaintext key from the request header
166
+ * @param callerIp - Source IP of the request (for IP-allowlist enforcement + usage tracking)
167
+ * @throws {NAuthException} API_KEY_INVALID, API_KEY_EXPIRED, API_KEY_IP_NOT_ALLOWED
168
+ */
169
+ async validateKey(rawKey, callerIp) {
170
+ if (typeof rawKey !== 'string' || rawKey.length === 0) {
171
+ await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_AUTH_FAILED, null, 'FAILURE', { reason: 'malformed' });
172
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_INVALID, 'Invalid API key');
173
+ }
174
+ // Look up by the hash of the presented key (indexed). A miss means unknown/invalid.
175
+ const key = await this.apiKeyRepository.findOne({ where: { keyHash: this.hashKey(rawKey) } });
176
+ if (!key || !key.isActive) {
177
+ await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_AUTH_FAILED, key?.userId ?? null, 'FAILURE', { reason: 'invalid' });
178
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_INVALID, 'Invalid API key');
179
+ }
180
+ if (key.isExpired()) {
181
+ await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_AUTH_FAILED, key.userId, 'FAILURE', {
182
+ keyId: key.keyId,
183
+ reason: 'expired',
184
+ });
185
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_EXPIRED, 'API key has expired');
186
+ }
187
+ if (!key.isIpAllowed(callerIp, ip_match_1.ipMatchesEntry)) {
188
+ await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_AUTH_FAILED, key.userId, 'SUSPICIOUS', {
189
+ keyId: key.keyId,
190
+ reason: 'ip_not_allowed',
191
+ });
192
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_IP_NOT_ALLOWED, 'API key not permitted from this IP address');
193
+ }
194
+ const owner = await this.userRepository.findOne({
195
+ where: { id: key.userId },
196
+ select: { id: true, sub: true, isActive: true },
197
+ });
198
+ if (!owner || !owner.isActive) {
199
+ await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_AUTH_FAILED, key.userId, 'FAILURE', {
200
+ keyId: key.keyId,
201
+ reason: 'owner_inactive',
202
+ });
203
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_INVALID, 'Invalid API key');
204
+ }
205
+ await this.recordUsage(key, callerIp ?? null);
206
+ return { keyId: key.keyId, sub: owner.sub };
207
+ }
208
+ // ==========================================================================
209
+ // Internal primitives (keyed by internal userId)
210
+ // ==========================================================================
211
+ /**
212
+ * Core key creation for a resolved user.
213
+ */
214
+ async createForUser(userId, params, createdByAdmin) {
215
+ const cfg = this.config.apiKeys ?? {};
216
+ // Creation-rights gate: only admins may create keys unless user creation is enabled.
217
+ if (!createdByAdmin && cfg.allowUserCreation !== true) {
218
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_CREATION_DISABLED, 'API key creation is disabled for users. Contact an administrator.');
219
+ }
220
+ const maxKeys = cfg.maxKeysPerUser ?? 10;
221
+ const activeCount = await this.apiKeyRepository.count({ where: { userId, isActive: true } });
222
+ if (activeCount >= maxKeys) {
223
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_LIMIT_REACHED, `Maximum of ${maxKeys} active API keys reached.`, {
224
+ maxKeysPerUser: maxKeys,
225
+ });
226
+ }
227
+ const expiresAt = this.resolveExpiry(params.expiresInDays);
228
+ const allowedIps = this.normalizeAllowedIps(params.allowedIps);
229
+ // Server-generated 256-bit secret. The plaintext is returned once; only its hash is stored.
230
+ const keyId = (0, crypto_1.randomUUID)();
231
+ const rawKey = (0, crypto_1.randomBytes)(32).toString('base64url');
232
+ const keyHash = this.hashKey(rawKey);
233
+ const entity = this.apiKeyRepository.create({
234
+ keyId,
235
+ userId,
236
+ keyHash,
237
+ name: params.name ?? null,
238
+ lastFour: rawKey.slice(-4),
239
+ allowedIps: allowedIps.length > 0 ? allowedIps : null,
240
+ expiresAt,
241
+ isActive: true,
242
+ createdByAdmin,
243
+ usageCount: 0,
244
+ });
245
+ const saved = await this.apiKeyRepository.save(entity);
246
+ await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_CREATED, userId, 'SUCCESS', {
247
+ keyId,
248
+ createdByAdmin,
249
+ hasExpiry: expiresAt !== null,
250
+ ipRestricted: allowedIps.length > 0,
251
+ });
252
+ return { key: rawKey, apiKey: this.toResponse(saved) };
253
+ }
254
+ /**
255
+ * List a resolved user's keys.
256
+ */
257
+ async listForUser(userId) {
258
+ const keys = await this.apiKeyRepository.find({ where: { userId }, order: { createdAt: 'DESC' } });
259
+ return { apiKeys: keys.map((k) => this.toResponse(k)) };
260
+ }
261
+ /**
262
+ * Update a resolved user's key (label / IP allowlist).
263
+ */
264
+ async updateForUser(userId, keyId, name, allowedIps) {
265
+ const key = await this.apiKeyRepository.findOne({ where: { keyId, userId } });
266
+ if (!key) {
267
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_NOT_FOUND, 'API key not found');
268
+ }
269
+ if (name !== undefined) {
270
+ key.name = name ?? null;
271
+ }
272
+ if (allowedIps !== undefined) {
273
+ const normalized = this.normalizeAllowedIps(allowedIps);
274
+ key.allowedIps = normalized.length > 0 ? normalized : null;
275
+ }
276
+ const saved = await this.apiKeyRepository.save(key);
277
+ await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_UPDATED, userId, 'INFO', { keyId });
278
+ return this.toResponse(saved);
279
+ }
280
+ /**
281
+ * Revoke (soft-delete) a resolved user's key.
282
+ */
283
+ async revokeForUser(userId, keyId, reason) {
284
+ const key = await this.apiKeyRepository.findOne({ where: { keyId, userId } });
285
+ if (!key) {
286
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_NOT_FOUND, 'API key not found');
287
+ }
288
+ if (key.isActive) {
289
+ key.isActive = false;
290
+ key.revokedAt = new Date();
291
+ key.revokeReason = reason;
292
+ await this.apiKeyRepository.save(key);
293
+ }
294
+ await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_REVOKED, userId, 'INFO', { keyId });
295
+ return { success: true };
296
+ }
297
+ /**
298
+ * Permanently delete a resolved user's key.
299
+ */
300
+ async deleteForUser(userId, keyId) {
301
+ const key = await this.apiKeyRepository.findOne({ where: { keyId, userId } });
302
+ if (!key) {
303
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_NOT_FOUND, 'API key not found');
304
+ }
305
+ await this.apiKeyRepository.remove(key);
306
+ await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_DELETED, userId, 'INFO', { keyId });
307
+ return { success: true };
308
+ }
309
+ // ==========================================================================
310
+ // Helpers
311
+ // ==========================================================================
312
+ /**
313
+ * Resolve the currently authenticated user from request context.
314
+ *
315
+ * @throws {NAuthException} FORBIDDEN when no authenticated user is present
316
+ */
317
+ getCurrentUserOrThrow() {
318
+ const currentUser = context_storage_1.ContextStorage.get('CURRENT_USER');
319
+ if (!currentUser) {
320
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.FORBIDDEN, 'Authentication required');
321
+ }
322
+ return currentUser;
323
+ }
324
+ /**
325
+ * Resolve an internal user id from an external sub (UUID).
326
+ *
327
+ * @throws {NAuthException} USER_NOT_FOUND
328
+ */
329
+ async resolveUserIdBySub(sub) {
330
+ const user = await this.userRepository.findOne({ where: { sub }, select: { id: true } });
331
+ if (!user) {
332
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.USER_NOT_FOUND, 'User not found');
333
+ }
334
+ return user.id;
335
+ }
336
+ /**
337
+ * Ensure a keyId is present for admin revoke/delete operations.
338
+ */
339
+ requireKeyId(keyId) {
340
+ if (!keyId) {
341
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, 'keyId is required for this operation');
342
+ }
343
+ return keyId;
344
+ }
345
+ /**
346
+ * Resolve the mandatory, config-bounded expiry.
347
+ */
348
+ resolveExpiry(expiresInDays) {
349
+ const cfg = this.config.apiKeys ?? {};
350
+ if (expiresInDays === undefined) {
351
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_EXPIRY_REQUIRED, 'An expiry must be specified when creating an API key (a number of days, or null for never).');
352
+ }
353
+ if (expiresInDays === null) {
354
+ if (cfg.allowIndefinite === false) {
355
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_INDEFINITE_NOT_ALLOWED, 'Non-expiring API keys are not allowed. Specify an expiry in days.');
356
+ }
357
+ return null;
358
+ }
359
+ if (!Number.isInteger(expiresInDays) || expiresInDays < 1) {
360
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, 'expiresInDays must be a positive integer or null.');
361
+ }
362
+ if (typeof cfg.maxExpiryDays === 'number' && expiresInDays > cfg.maxExpiryDays) {
363
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_EXPIRY_TOO_LONG, `Expiry exceeds the maximum of ${cfg.maxExpiryDays} days.`, { maxExpiryDays: cfg.maxExpiryDays });
364
+ }
365
+ const expiresAt = new Date();
366
+ expiresAt.setDate(expiresAt.getDate() + expiresInDays);
367
+ return expiresAt;
368
+ }
369
+ /**
370
+ * Validate and normalize an IP allowlist per configuration.
371
+ */
372
+ normalizeAllowedIps(allowedIps) {
373
+ const cfg = this.config.apiKeys ?? {};
374
+ const ipCfg = cfg.ipRestrictions ?? {};
375
+ if (ipCfg.enabled === false) {
376
+ if (allowedIps && allowedIps.length > 0) {
377
+ this.logger?.debug?.('[ApiKey] IP restrictions disabled - ignoring provided allowedIps');
378
+ }
379
+ return [];
380
+ }
381
+ const normalized = (allowedIps ?? []).map((e) => e.trim()).filter((e) => e.length > 0);
382
+ if (ipCfg.requireForNewKeys === true && normalized.length === 0) {
383
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, 'An IP allowlist is required for new API keys (apiKeys.ipRestrictions.requireForNewKeys).');
384
+ }
385
+ const maxIps = ipCfg.maxIpsPerKey ?? 20;
386
+ if (normalized.length > maxIps) {
387
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, `An API key may have at most ${maxIps} IP entries.`, {
388
+ maxIpsPerKey: maxIps,
389
+ });
390
+ }
391
+ const invalid = normalized.filter((e) => !(0, ip_match_1.isValidIpOrCidr)(e));
392
+ if (invalid.length > 0) {
393
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, 'allowedIps contains invalid IP or CIDR entries.', {
394
+ invalid,
395
+ });
396
+ }
397
+ return normalized;
398
+ }
399
+ /**
400
+ * Compute the SHA-256 hex hash of a key string (used for storage + indexed lookup).
401
+ */
402
+ hashKey(rawKey) {
403
+ return (0, crypto_1.createHash)('sha256').update(rawKey).digest('hex');
404
+ }
405
+ /**
406
+ * Update last-used metadata, throttled to avoid a write on every request.
407
+ * Never throws — usage tracking must not block authentication.
408
+ */
409
+ async recordUsage(key, callerIp) {
410
+ const cfg = this.config.apiKeys ?? {};
411
+ const throttleMs = (cfg.lastUsedThrottleSeconds ?? 60) * 1000;
412
+ const now = Date.now();
413
+ const last = key.lastUsedAt ? key.lastUsedAt.getTime() : 0;
414
+ if (last !== 0 && now - last < throttleMs) {
415
+ return;
416
+ }
417
+ try {
418
+ const update = {
419
+ lastUsedAt: new Date(now),
420
+ };
421
+ if (cfg.trackUsageIp !== false) {
422
+ update.lastUsedIp = callerIp;
423
+ }
424
+ await this.apiKeyRepository.update({ id: key.id }, update);
425
+ // Atomic increment avoids lost updates under concurrent use (read-modify-write race).
426
+ await this.apiKeyRepository.increment({ id: key.id }, 'usageCount', 1);
427
+ await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_USED, key.userId, 'SUCCESS', { keyId: key.keyId });
428
+ }
429
+ catch (error) {
430
+ const message = error instanceof Error ? error.message : 'Unknown error';
431
+ this.logger?.debug?.(`[ApiKey] Failed to record usage for key ${key.keyId}: ${message}`);
432
+ }
433
+ }
434
+ /**
435
+ * Build a sanitized response DTO from an entity.
436
+ */
437
+ toResponse(key) {
438
+ return {
439
+ keyId: key.keyId,
440
+ name: key.name ?? null,
441
+ lastFour: key.lastFour ?? null,
442
+ allowedIps: key.allowedIps ?? null,
443
+ expiresAt: key.expiresAt ?? null,
444
+ isActive: key.isActive,
445
+ createdByAdmin: key.createdByAdmin,
446
+ lastUsedAt: key.lastUsedAt ?? null,
447
+ lastUsedIp: key.lastUsedIp ?? null,
448
+ usageCount: key.usageCount ?? 0,
449
+ createdAt: key.createdAt,
450
+ };
451
+ }
452
+ /**
453
+ * Record an audit event (no-op when audit logging is disabled). Never throws.
454
+ */
455
+ async audit(eventType, userId, eventStatus, metadata) {
456
+ if (!this.auditService) {
457
+ return;
458
+ }
459
+ try {
460
+ await this.auditService.recordEvent({
461
+ userId: userId ?? undefined,
462
+ eventType,
463
+ eventStatus,
464
+ authMethod: 'api-key',
465
+ metadata,
466
+ });
467
+ }
468
+ catch (error) {
469
+ const message = error instanceof Error ? error.message : 'Unknown error';
470
+ this.logger?.debug?.(`[ApiKey] Failed to record audit event ${eventType}: ${message}`);
471
+ }
472
+ }
473
+ }
474
+ exports.ApiKeyService = ApiKeyService;
475
+ //# sourceMappingURL=api-key.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-key.service.js","sourceRoot":"","sources":["../../src/services/api-key.service.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@
3
3
  */
4
4
  export * from './pii-redactor';
5
5
  export * from './ip-extractor';
6
+ export * from './ip-match';
6
7
  export * from './nauth-logger';
7
8
  export * from './cookies.util';
8
9
  export * from './cookie-names.util';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC"}
@@ -19,6 +19,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
20
  __exportStar(require("./pii-redactor"), exports);
21
21
  __exportStar(require("./ip-extractor"), exports);
22
+ __exportStar(require("./ip-match"), exports);
22
23
  __exportStar(require("./nauth-logger"), exports);
23
24
  __exportStar(require("./cookies.util"), exports);
24
25
  __exportStar(require("./cookie-names.util"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;AAEH,iDAA+B;AAC/B,iDAA+B;AAC/B,iDAA+B;AAC/B,iDAA+B;AAC/B,sDAAoC;AACpC,oDAAkC;AAClC,0DAAwC;AACxC,uDAAqC;AACrC,kDAAgC;AAChC,wFAAwF"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";AAAA;;GAEG;;;;;;;;;;;;;;;;AAEH,iDAA+B;AAC/B,iDAA+B;AAC/B,6CAA2B;AAC3B,iDAA+B;AAC/B,iDAA+B;AAC/B,sDAAoC;AACpC,oDAAkC;AAClC,0DAAwC;AACxC,uDAAqC;AACrC,kDAAgC;AAChC,wFAAwF"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * IP Allowlist Matching
3
+ *
4
+ * Utilities for matching a source IP against an allowlist entry that may be an
5
+ * exact IPv4/IPv6 address or an IPv4 CIDR range. Used to enforce per-API-key IP
6
+ * restrictions.
7
+ *
8
+ * @remarks
9
+ * IPv4 CIDR ranges (e.g. `10.0.0.0/8`) and exact IPv4/IPv6 addresses are supported.
10
+ * IPv6 CIDR ranges are not expanded; provide exact IPv6 addresses instead.
11
+ */
12
+ /**
13
+ * Validate whether a string is a valid IP address or IPv4 CIDR range.
14
+ *
15
+ * @param entry - Candidate allowlist entry
16
+ * @returns true if the entry is a usable IP or IPv4 CIDR
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * isValidIpOrCidr('203.0.113.4'); // true
21
+ * isValidIpOrCidr('10.0.0.0/8'); // true
22
+ * isValidIpOrCidr('not-an-ip'); // false
23
+ * ```
24
+ */
25
+ export declare function isValidIpOrCidr(entry: string): boolean;
26
+ /**
27
+ * Determine whether a source IP matches a single allowlist entry.
28
+ *
29
+ * Supports:
30
+ * - Exact IPv4/IPv6 match (case-insensitive for IPv6)
31
+ * - IPv4 CIDR range match (e.g. `10.0.0.0/8`)
32
+ *
33
+ * @param ip - Source IP of the request
34
+ * @param entry - Allowlist entry (IP or IPv4 CIDR)
35
+ * @returns true if the IP is covered by the entry
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * ipMatchesEntry('10.1.2.3', '10.0.0.0/8'); // true
40
+ * ipMatchesEntry('203.0.113.4', '203.0.113.4'); // true
41
+ * ```
42
+ */
43
+ export declare function ipMatchesEntry(ip: string, entry: string): boolean;
44
+ //# sourceMappingURL=ip-match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ip-match.d.ts","sourceRoot":"","sources":["../../src/utils/ip-match.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAuCH;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAetD;AAkBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAwBjE"}