@nauth-toolkit/core 0.3.0 → 0.3.1
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/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +53 -0
- package/dist/bootstrap.js.map +1 -1
- package/dist/dto/admin-api-key.dto.d.ts +69 -0
- package/dist/dto/admin-api-key.dto.d.ts.map +1 -0
- package/dist/dto/admin-api-key.dto.js +144 -0
- package/dist/dto/admin-api-key.dto.js.map +1 -0
- package/dist/dto/admin-signup-social.dto.d.ts +1 -1
- package/dist/dto/admin-signup-social.dto.js +1 -1
- package/dist/dto/admin-signup.dto.d.ts +1 -1
- package/dist/dto/admin-signup.dto.js +1 -1
- package/dist/dto/api-key.dto.d.ts +132 -0
- package/dist/dto/api-key.dto.d.ts.map +1 -0
- package/dist/dto/api-key.dto.js +198 -0
- package/dist/dto/api-key.dto.js.map +1 -0
- package/dist/dto/change-password.dto.d.ts +2 -2
- package/dist/dto/change-password.dto.js +2 -2
- package/dist/dto/index.d.ts +2 -0
- package/dist/dto/index.d.ts.map +1 -1
- package/dist/dto/index.js +3 -0
- package/dist/dto/index.js.map +1 -1
- package/dist/dto/reset-password.dto.d.ts +1 -1
- package/dist/dto/reset-password.dto.js +1 -1
- package/dist/dto/respond-challenge.dto.d.ts +1 -1
- package/dist/dto/respond-challenge.dto.js +1 -1
- package/dist/dto/signup.dto.d.ts +1 -1
- package/dist/dto/signup.dto.js +1 -1
- package/dist/entities/api-key.entity.d.ts +135 -0
- package/dist/entities/api-key.entity.d.ts.map +1 -0
- package/dist/entities/api-key.entity.js +149 -0
- package/dist/entities/api-key.entity.js.map +1 -0
- package/dist/entities/index.d.ts +1 -0
- package/dist/entities/index.d.ts.map +1 -1
- package/dist/entities/index.js +3 -1
- package/dist/entities/index.js.map +1 -1
- package/dist/enums/auth-audit-event-type.enum.d.ts +25 -1
- package/dist/enums/auth-audit-event-type.enum.d.ts.map +1 -1
- package/dist/enums/auth-audit-event-type.enum.js +27 -0
- package/dist/enums/auth-audit-event-type.enum.js.map +1 -1
- package/dist/enums/error-codes.enum.d.ts +56 -1
- package/dist/enums/error-codes.enum.d.ts.map +1 -1
- package/dist/enums/error-codes.enum.js +58 -0
- package/dist/enums/error-codes.enum.js.map +1 -1
- package/dist/exceptions/nauth.exception.d.ts.map +1 -1
- package/dist/exceptions/nauth.exception.js +13 -0
- package/dist/exceptions/nauth.exception.js.map +1 -1
- package/dist/handlers/api-key.handler.d.ts +45 -0
- package/dist/handlers/api-key.handler.d.ts.map +1 -0
- package/dist/handlers/api-key.handler.js +99 -0
- package/dist/handlers/api-key.handler.js.map +1 -0
- package/dist/handlers/auth.handler.d.ts.map +1 -1
- package/dist/handlers/auth.handler.js +6 -0
- package/dist/handlers/auth.handler.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/config.interface.d.ts +124 -0
- package/dist/interfaces/config.interface.d.ts.map +1 -1
- package/dist/openapi/components.schemas.json +284 -7
- package/dist/platform/interfaces.d.ts +8 -0
- package/dist/platform/interfaces.d.ts.map +1 -1
- package/dist/schemas/auth-config.schema.d.ts +185 -0
- package/dist/schemas/auth-config.schema.d.ts.map +1 -1
- package/dist/schemas/auth-config.schema.js +27 -1
- package/dist/schemas/auth-config.schema.js.map +1 -1
- package/dist/services/admin-auth.service.d.ts +59 -1
- package/dist/services/admin-auth.service.d.ts.map +1 -1
- package/dist/services/admin-auth.service.js +99 -1
- package/dist/services/admin-auth.service.js.map +1 -1
- package/dist/services/api-key.service.d.ts +152 -0
- package/dist/services/api-key.service.d.ts.map +1 -0
- package/dist/services/api-key.service.js +378 -0
- package/dist/services/api-key.service.js.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/ip-match.d.ts +44 -0
- package/dist/utils/ip-match.d.ts.map +1 -0
- package/dist/utils/ip-match.js +135 -0
- package/dist/utils/ip-match.js.map +1 -0
- package/dist/utils/setup/get-repositories.d.ts +2 -1
- package/dist/utils/setup/get-repositories.d.ts.map +1 -1
- package/dist/utils/setup/get-repositories.js +2 -0
- package/dist/utils/setup/get-repositories.js.map +1 -1
- package/dist/utils/setup/init-services.d.ts +4 -2
- package/dist/utils/setup/init-services.d.ts.map +1 -1
- package/dist/utils/setup/init-services.js +8 -1
- package/dist/utils/setup/init-services.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,378 @@
|
|
|
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 ip_match_1 = require("../utils/ip-match");
|
|
9
|
+
/**
|
|
10
|
+
* API Key Service
|
|
11
|
+
*
|
|
12
|
+
* Manages the lifecycle of API keys (create/list/update/revoke/delete) and validates
|
|
13
|
+
* keys presented on requests. Keys authenticate as their owning user.
|
|
14
|
+
*
|
|
15
|
+
* Security:
|
|
16
|
+
* - Only a SHA-256 hash of the full key is stored; the plaintext is returned once at creation.
|
|
17
|
+
* - Lookup uses a non-secret, indexed `lookupId`; the secret is compared in constant time.
|
|
18
|
+
* - Per-key IP allowlists (optional) restrict which source IPs may use a key.
|
|
19
|
+
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* This service does NOT enforce endpoint authorization. Route protection (which endpoints
|
|
22
|
+
* accept API keys) is the responsibility of the framework adapter (guard/middleware).
|
|
23
|
+
*/
|
|
24
|
+
class ApiKeyService {
|
|
25
|
+
apiKeyRepository;
|
|
26
|
+
userRepository;
|
|
27
|
+
config;
|
|
28
|
+
logger;
|
|
29
|
+
auditService;
|
|
30
|
+
constructor(apiKeyRepository, userRepository, config, logger, auditService) {
|
|
31
|
+
this.apiKeyRepository = apiKeyRepository;
|
|
32
|
+
this.userRepository = userRepository;
|
|
33
|
+
this.config = config;
|
|
34
|
+
this.logger = logger;
|
|
35
|
+
this.auditService = auditService;
|
|
36
|
+
this.logger?.log?.('ApiKeyService initialized');
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Create a new API key.
|
|
40
|
+
*
|
|
41
|
+
* @param params - Creation parameters (owning userId is required)
|
|
42
|
+
* @returns The plaintext key (shown once) plus sanitized metadata
|
|
43
|
+
* @throws {NAuthException} API_KEY_CREATION_DISABLED, API_KEY_LIMIT_REACHED,
|
|
44
|
+
* API_KEY_EXPIRY_REQUIRED, API_KEY_INDEFINITE_NOT_ALLOWED, API_KEY_EXPIRY_TOO_LONG,
|
|
45
|
+
* VALIDATION_FAILED (invalid IP allowlist), USER_NOT_FOUND
|
|
46
|
+
*/
|
|
47
|
+
async createKey(params) {
|
|
48
|
+
const cfg = this.config.apiKeys ?? {};
|
|
49
|
+
// Creation-rights gate: only admins may create keys unless user creation is enabled.
|
|
50
|
+
if (!params.createdByAdmin && cfg.allowUserCreation !== true) {
|
|
51
|
+
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.');
|
|
52
|
+
}
|
|
53
|
+
const user = await this.userRepository.findOne({ where: { id: params.userId } });
|
|
54
|
+
if (!user) {
|
|
55
|
+
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.USER_NOT_FOUND, 'User not found');
|
|
56
|
+
}
|
|
57
|
+
// Enforce per-user active key limit.
|
|
58
|
+
const maxKeys = cfg.maxKeysPerUser ?? 10;
|
|
59
|
+
const activeCount = await this.apiKeyRepository.count({ where: { userId: params.userId, isActive: true } });
|
|
60
|
+
if (activeCount >= maxKeys) {
|
|
61
|
+
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_LIMIT_REACHED, `Maximum of ${maxKeys} active API keys reached.`, {
|
|
62
|
+
maxKeysPerUser: maxKeys,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const expiresAt = this.resolveExpiry(params.expiresInDays);
|
|
66
|
+
const allowedIps = this.normalizeAllowedIps(params.allowedIps);
|
|
67
|
+
// Generate the key material.
|
|
68
|
+
const keyId = (0, crypto_1.randomUUID)();
|
|
69
|
+
const lookupId = (0, crypto_1.randomBytes)(8).toString('hex'); // 16 non-secret hex chars
|
|
70
|
+
const secret = (0, crypto_1.randomBytes)(32).toString('base64url');
|
|
71
|
+
const prefix = cfg.keyPrefix ?? 'nauth';
|
|
72
|
+
const fullKey = `${prefix}_${lookupId}.${secret}`;
|
|
73
|
+
const keyHash = this.hashKey(fullKey);
|
|
74
|
+
const entity = this.apiKeyRepository.create({
|
|
75
|
+
keyId,
|
|
76
|
+
userId: params.userId,
|
|
77
|
+
lookupId,
|
|
78
|
+
keyHash,
|
|
79
|
+
name: params.name ?? null,
|
|
80
|
+
lastFour: secret.slice(-4),
|
|
81
|
+
allowedIps: allowedIps.length > 0 ? allowedIps : null,
|
|
82
|
+
expiresAt,
|
|
83
|
+
isActive: true,
|
|
84
|
+
createdByAdmin: params.createdByAdmin === true,
|
|
85
|
+
usageCount: 0,
|
|
86
|
+
});
|
|
87
|
+
const saved = await this.apiKeyRepository.save(entity);
|
|
88
|
+
await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_CREATED, params.userId, 'SUCCESS', {
|
|
89
|
+
keyId,
|
|
90
|
+
createdByAdmin: entity.createdByAdmin,
|
|
91
|
+
hasExpiry: expiresAt !== null,
|
|
92
|
+
ipRestricted: allowedIps.length > 0,
|
|
93
|
+
});
|
|
94
|
+
return { key: fullKey, apiKey: this.toResponse(saved) };
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Update a key's mutable fields (name and IP allowlist).
|
|
98
|
+
* The secret and expiry are immutable — rotate/extend by deleting and recreating.
|
|
99
|
+
*
|
|
100
|
+
* @throws {NAuthException} API_KEY_NOT_FOUND, VALIDATION_FAILED
|
|
101
|
+
*/
|
|
102
|
+
async updateKey(params) {
|
|
103
|
+
const key = await this.apiKeyRepository.findOne({ where: { keyId: params.keyId, userId: params.userId } });
|
|
104
|
+
if (!key) {
|
|
105
|
+
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_NOT_FOUND, 'API key not found');
|
|
106
|
+
}
|
|
107
|
+
if (params.name !== undefined) {
|
|
108
|
+
key.name = params.name ?? null;
|
|
109
|
+
}
|
|
110
|
+
if (params.allowedIps !== undefined) {
|
|
111
|
+
const allowedIps = this.normalizeAllowedIps(params.allowedIps);
|
|
112
|
+
key.allowedIps = allowedIps.length > 0 ? allowedIps : null;
|
|
113
|
+
}
|
|
114
|
+
const saved = await this.apiKeyRepository.save(key);
|
|
115
|
+
await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_UPDATED, params.userId, 'INFO', { keyId: params.keyId });
|
|
116
|
+
return this.toResponse(saved);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* List all keys owned by a user (sanitized; never includes secrets).
|
|
120
|
+
*/
|
|
121
|
+
async listKeys(userId) {
|
|
122
|
+
const keys = await this.apiKeyRepository.find({
|
|
123
|
+
where: { userId },
|
|
124
|
+
order: { createdAt: 'DESC' },
|
|
125
|
+
});
|
|
126
|
+
return keys.map((k) => this.toResponse(k));
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Revoke (soft-delete) a key. The record is retained for audit history.
|
|
130
|
+
*
|
|
131
|
+
* @throws {NAuthException} API_KEY_NOT_FOUND
|
|
132
|
+
*/
|
|
133
|
+
async revokeKey(params) {
|
|
134
|
+
const key = await this.apiKeyRepository.findOne({ where: { keyId: params.keyId, userId: params.userId } });
|
|
135
|
+
if (!key) {
|
|
136
|
+
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_NOT_FOUND, 'API key not found');
|
|
137
|
+
}
|
|
138
|
+
if (key.isActive) {
|
|
139
|
+
key.isActive = false;
|
|
140
|
+
key.revokedAt = new Date();
|
|
141
|
+
key.revokeReason = params.reason ?? 'user_revoked';
|
|
142
|
+
await this.apiKeyRepository.save(key);
|
|
143
|
+
}
|
|
144
|
+
await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_REVOKED, params.userId, 'INFO', { keyId: params.keyId });
|
|
145
|
+
return { success: true };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Permanently delete a key.
|
|
149
|
+
*
|
|
150
|
+
* @throws {NAuthException} API_KEY_NOT_FOUND
|
|
151
|
+
*/
|
|
152
|
+
async deleteKey(params) {
|
|
153
|
+
const key = await this.apiKeyRepository.findOne({ where: { keyId: params.keyId, userId: params.userId } });
|
|
154
|
+
if (!key) {
|
|
155
|
+
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_NOT_FOUND, 'API key not found');
|
|
156
|
+
}
|
|
157
|
+
await this.apiKeyRepository.remove(key);
|
|
158
|
+
await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_DELETED, params.userId, 'INFO', { keyId: params.keyId });
|
|
159
|
+
return { success: true };
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Validate a presented API key and resolve its owner.
|
|
163
|
+
*
|
|
164
|
+
* On any failure this throws a precise {@link NAuthException} (access denied) — callers
|
|
165
|
+
* MUST NOT fall back to other credentials.
|
|
166
|
+
*
|
|
167
|
+
* @param rawKey - The full plaintext key from the request header
|
|
168
|
+
* @param callerIp - Source IP of the request (for IP-allowlist enforcement + usage tracking)
|
|
169
|
+
* @returns The owning user's identifiers on success
|
|
170
|
+
* @throws {NAuthException} API_KEY_INVALID, API_KEY_EXPIRED, API_KEY_IP_NOT_ALLOWED
|
|
171
|
+
*/
|
|
172
|
+
async validateKey(rawKey, callerIp) {
|
|
173
|
+
const lookupId = this.parseLookupId(rawKey);
|
|
174
|
+
if (!lookupId) {
|
|
175
|
+
await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_AUTH_FAILED, null, 'FAILURE', { reason: 'malformed' });
|
|
176
|
+
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_INVALID, 'Invalid API key');
|
|
177
|
+
}
|
|
178
|
+
const key = await this.apiKeyRepository.findOne({ where: { lookupId } });
|
|
179
|
+
if (!key || !key.isActive || !this.hashesEqual(key.keyHash, this.hashKey(rawKey))) {
|
|
180
|
+
await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_AUTH_FAILED, key?.userId ?? null, 'FAILURE', { reason: 'invalid' });
|
|
181
|
+
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_INVALID, 'Invalid API key');
|
|
182
|
+
}
|
|
183
|
+
if (key.isExpired()) {
|
|
184
|
+
await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_AUTH_FAILED, key.userId, 'FAILURE', {
|
|
185
|
+
keyId: key.keyId,
|
|
186
|
+
reason: 'expired',
|
|
187
|
+
});
|
|
188
|
+
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_EXPIRED, 'API key has expired');
|
|
189
|
+
}
|
|
190
|
+
if (!key.isIpAllowed(callerIp, ip_match_1.ipMatchesEntry)) {
|
|
191
|
+
await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_AUTH_FAILED, key.userId, 'SUSPICIOUS', {
|
|
192
|
+
keyId: key.keyId,
|
|
193
|
+
reason: 'ip_not_allowed',
|
|
194
|
+
});
|
|
195
|
+
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_IP_NOT_ALLOWED, 'API key not permitted from this IP address');
|
|
196
|
+
}
|
|
197
|
+
// Resolve the owner's external id (sub) so callers can load the shared auth context.
|
|
198
|
+
const owner = await this.userRepository.findOne({
|
|
199
|
+
where: { id: key.userId },
|
|
200
|
+
select: { id: true, sub: true, isActive: true },
|
|
201
|
+
});
|
|
202
|
+
if (!owner || !owner.isActive) {
|
|
203
|
+
await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_AUTH_FAILED, key.userId, 'FAILURE', {
|
|
204
|
+
keyId: key.keyId,
|
|
205
|
+
reason: 'owner_inactive',
|
|
206
|
+
});
|
|
207
|
+
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.API_KEY_INVALID, 'Invalid API key');
|
|
208
|
+
}
|
|
209
|
+
await this.recordUsage(key, callerIp ?? null);
|
|
210
|
+
return { keyId: key.keyId, userId: key.userId, sub: owner.sub };
|
|
211
|
+
}
|
|
212
|
+
// ==========================================================================
|
|
213
|
+
// Internal helpers
|
|
214
|
+
// ==========================================================================
|
|
215
|
+
/**
|
|
216
|
+
* Resolve the mandatory, config-bounded expiry.
|
|
217
|
+
*/
|
|
218
|
+
resolveExpiry(expiresInDays) {
|
|
219
|
+
const cfg = this.config.apiKeys ?? {};
|
|
220
|
+
if (expiresInDays === undefined) {
|
|
221
|
+
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).');
|
|
222
|
+
}
|
|
223
|
+
if (expiresInDays === null) {
|
|
224
|
+
if (cfg.allowIndefinite === false) {
|
|
225
|
+
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.');
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
if (!Number.isInteger(expiresInDays) || expiresInDays < 1) {
|
|
230
|
+
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, 'expiresInDays must be a positive integer or null.');
|
|
231
|
+
}
|
|
232
|
+
if (typeof cfg.maxExpiryDays === 'number' && expiresInDays > cfg.maxExpiryDays) {
|
|
233
|
+
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 });
|
|
234
|
+
}
|
|
235
|
+
const expiresAt = new Date();
|
|
236
|
+
expiresAt.setDate(expiresAt.getDate() + expiresInDays);
|
|
237
|
+
return expiresAt;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Validate and normalize an IP allowlist per configuration.
|
|
241
|
+
*/
|
|
242
|
+
normalizeAllowedIps(allowedIps) {
|
|
243
|
+
const cfg = this.config.apiKeys ?? {};
|
|
244
|
+
const ipCfg = cfg.ipRestrictions ?? {};
|
|
245
|
+
// When IP restrictions are disabled, allowlists are ignored entirely.
|
|
246
|
+
if (ipCfg.enabled === false) {
|
|
247
|
+
if (allowedIps && allowedIps.length > 0) {
|
|
248
|
+
this.logger?.debug?.('[ApiKey] IP restrictions disabled - ignoring provided allowedIps');
|
|
249
|
+
}
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
const normalized = (allowedIps ?? []).map((e) => e.trim()).filter((e) => e.length > 0);
|
|
253
|
+
if (ipCfg.requireForNewKeys === true && normalized.length === 0) {
|
|
254
|
+
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).');
|
|
255
|
+
}
|
|
256
|
+
const maxIps = ipCfg.maxIpsPerKey ?? 20;
|
|
257
|
+
if (normalized.length > maxIps) {
|
|
258
|
+
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, `An API key may have at most ${maxIps} IP entries.`, {
|
|
259
|
+
maxIpsPerKey: maxIps,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
const invalid = normalized.filter((e) => !(0, ip_match_1.isValidIpOrCidr)(e));
|
|
263
|
+
if (invalid.length > 0) {
|
|
264
|
+
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, 'allowedIps contains invalid IP or CIDR entries.', {
|
|
265
|
+
invalid,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return normalized;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Compute the SHA-256 hex hash of a full key string.
|
|
272
|
+
*/
|
|
273
|
+
hashKey(rawKey) {
|
|
274
|
+
return (0, crypto_1.createHash)('sha256').update(rawKey).digest('hex');
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Constant-time comparison of two equal-length hex hashes.
|
|
278
|
+
*/
|
|
279
|
+
hashesEqual(a, b) {
|
|
280
|
+
if (a.length !== b.length) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
return (0, crypto_1.timingSafeEqual)(Buffer.from(a), Buffer.from(b));
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Extract the non-secret lookup id from a raw key of the form `prefix_lookupId.secret`.
|
|
292
|
+
*/
|
|
293
|
+
parseLookupId(rawKey) {
|
|
294
|
+
if (typeof rawKey !== 'string') {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
const dotIndex = rawKey.indexOf('.');
|
|
298
|
+
if (dotIndex <= 0) {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
const head = rawKey.slice(0, dotIndex); // prefix_lookupId
|
|
302
|
+
const underscoreIndex = head.lastIndexOf('_');
|
|
303
|
+
if (underscoreIndex < 0 || underscoreIndex === head.length - 1) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
return head.slice(underscoreIndex + 1);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Update last-used metadata, throttled to avoid a write on every request.
|
|
310
|
+
* Never throws — usage tracking must not block authentication.
|
|
311
|
+
*/
|
|
312
|
+
async recordUsage(key, callerIp) {
|
|
313
|
+
const cfg = this.config.apiKeys ?? {};
|
|
314
|
+
const throttleMs = (cfg.lastUsedThrottleSeconds ?? 60) * 1000;
|
|
315
|
+
const now = Date.now();
|
|
316
|
+
const last = key.lastUsedAt ? key.lastUsedAt.getTime() : 0;
|
|
317
|
+
if (last !== 0 && now - last < throttleMs) {
|
|
318
|
+
return; // Within throttle window - skip write and audit noise.
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
const update = {
|
|
322
|
+
lastUsedAt: new Date(now),
|
|
323
|
+
};
|
|
324
|
+
if (cfg.trackUsageIp !== false) {
|
|
325
|
+
update.lastUsedIp = callerIp;
|
|
326
|
+
}
|
|
327
|
+
await this.apiKeyRepository.update({ id: key.id }, update);
|
|
328
|
+
// Atomic increment avoids lost updates under concurrent use (read-modify-write race).
|
|
329
|
+
await this.apiKeyRepository.increment({ id: key.id }, 'usageCount', 1);
|
|
330
|
+
await this.audit(auth_audit_event_type_enum_1.AuthAuditEventType.API_KEY_USED, key.userId, 'SUCCESS', { keyId: key.keyId });
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
334
|
+
this.logger?.debug?.(`[ApiKey] Failed to record usage for key ${key.keyId}: ${message}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Build a sanitized response DTO from an entity.
|
|
339
|
+
*/
|
|
340
|
+
toResponse(key) {
|
|
341
|
+
return {
|
|
342
|
+
keyId: key.keyId,
|
|
343
|
+
name: key.name ?? null,
|
|
344
|
+
lastFour: key.lastFour ?? null,
|
|
345
|
+
allowedIps: key.allowedIps ?? null,
|
|
346
|
+
expiresAt: key.expiresAt ?? null,
|
|
347
|
+
isActive: key.isActive,
|
|
348
|
+
createdByAdmin: key.createdByAdmin,
|
|
349
|
+
lastUsedAt: key.lastUsedAt ?? null,
|
|
350
|
+
lastUsedIp: key.lastUsedIp ?? null,
|
|
351
|
+
usageCount: key.usageCount ?? 0,
|
|
352
|
+
createdAt: key.createdAt,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Record an audit event (no-op when audit logging is disabled). Never throws.
|
|
357
|
+
*/
|
|
358
|
+
async audit(eventType, userId, eventStatus, metadata) {
|
|
359
|
+
if (!this.auditService) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
try {
|
|
363
|
+
await this.auditService.recordEvent({
|
|
364
|
+
userId: userId ?? undefined,
|
|
365
|
+
eventType,
|
|
366
|
+
eventStatus,
|
|
367
|
+
authMethod: 'api-key',
|
|
368
|
+
metadata,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
373
|
+
this.logger?.debug?.(`[ApiKey] Failed to record audit event ${eventType}: ${message}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
exports.ApiKeyService = ApiKeyService;
|
|
378
|
+
//# 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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"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -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"}
|
package/dist/utils/index.js
CHANGED
|
@@ -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);
|
package/dist/utils/index.js.map
CHANGED
|
@@ -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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* IP Allowlist Matching
|
|
4
|
+
*
|
|
5
|
+
* Utilities for matching a source IP against an allowlist entry that may be an
|
|
6
|
+
* exact IPv4/IPv6 address or an IPv4 CIDR range. Used to enforce per-API-key IP
|
|
7
|
+
* restrictions.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* IPv4 CIDR ranges (e.g. `10.0.0.0/8`) and exact IPv4/IPv6 addresses are supported.
|
|
11
|
+
* IPv6 CIDR ranges are not expanded; provide exact IPv6 addresses instead.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.isValidIpOrCidr = isValidIpOrCidr;
|
|
15
|
+
exports.ipMatchesEntry = ipMatchesEntry;
|
|
16
|
+
/**
|
|
17
|
+
* Validate a dotted-quad IPv4 string (octet range enforced).
|
|
18
|
+
*/
|
|
19
|
+
function isIpv4(value) {
|
|
20
|
+
const ipv4 = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
21
|
+
if (!ipv4.test(value)) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
return value
|
|
25
|
+
.split('.')
|
|
26
|
+
.map(Number)
|
|
27
|
+
.every((part) => part >= 0 && part <= 255);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Validate whether a string is a plausible IPv4 or IPv6 address.
|
|
31
|
+
*
|
|
32
|
+
* Accepts IPv4, IPv4-mapped IPv6 (e.g. `::ffff:192.168.0.1`), and general IPv6.
|
|
33
|
+
* Rejects malformed IPv6 with more than one `::` compression.
|
|
34
|
+
*/
|
|
35
|
+
function isIpAddress(value) {
|
|
36
|
+
if (isIpv4(value)) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
// IPv4-mapped IPv6, e.g. ::ffff:203.0.113.4
|
|
40
|
+
const mapped = value.match(/^::ffff:(.+)$/i);
|
|
41
|
+
if (mapped) {
|
|
42
|
+
return isIpv4(mapped[1]);
|
|
43
|
+
}
|
|
44
|
+
// General IPv6: at most one "::" compression allowed.
|
|
45
|
+
if ((value.match(/::/g) ?? []).length > 1) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const ipv6 = /^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/;
|
|
49
|
+
return ipv6.test(value);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Validate whether a string is a valid IP address or IPv4 CIDR range.
|
|
53
|
+
*
|
|
54
|
+
* @param entry - Candidate allowlist entry
|
|
55
|
+
* @returns true if the entry is a usable IP or IPv4 CIDR
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* isValidIpOrCidr('203.0.113.4'); // true
|
|
60
|
+
* isValidIpOrCidr('10.0.0.0/8'); // true
|
|
61
|
+
* isValidIpOrCidr('not-an-ip'); // false
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
function isValidIpOrCidr(entry) {
|
|
65
|
+
if (typeof entry !== 'string' || entry.trim().length === 0) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
const trimmed = entry.trim();
|
|
69
|
+
if (trimmed.includes('/')) {
|
|
70
|
+
const [base, prefixStr] = trimmed.split('/');
|
|
71
|
+
const prefix = Number(prefixStr);
|
|
72
|
+
// Only IPv4 CIDR is supported for range matching.
|
|
73
|
+
if (!isIpv4(base)) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
return Number.isInteger(prefix) && prefix >= 0 && prefix <= 32;
|
|
77
|
+
}
|
|
78
|
+
return isIpAddress(trimmed);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Convert an IPv4 dotted-quad string to a 32-bit unsigned integer.
|
|
82
|
+
*/
|
|
83
|
+
function ipv4ToInt(ip) {
|
|
84
|
+
const ipv4 = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
85
|
+
if (!ipv4.test(ip)) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const parts = ip.split('.').map(Number);
|
|
89
|
+
if (!parts.every((p) => p >= 0 && p <= 255)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
// Unsigned 32-bit via >>> 0
|
|
93
|
+
return ((parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3]) >>> 0;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Determine whether a source IP matches a single allowlist entry.
|
|
97
|
+
*
|
|
98
|
+
* Supports:
|
|
99
|
+
* - Exact IPv4/IPv6 match (case-insensitive for IPv6)
|
|
100
|
+
* - IPv4 CIDR range match (e.g. `10.0.0.0/8`)
|
|
101
|
+
*
|
|
102
|
+
* @param ip - Source IP of the request
|
|
103
|
+
* @param entry - Allowlist entry (IP or IPv4 CIDR)
|
|
104
|
+
* @returns true if the IP is covered by the entry
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* ipMatchesEntry('10.1.2.3', '10.0.0.0/8'); // true
|
|
109
|
+
* ipMatchesEntry('203.0.113.4', '203.0.113.4'); // true
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
function ipMatchesEntry(ip, entry) {
|
|
113
|
+
if (!ip || !entry) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
const normalizedIp = ip.trim().replace(/^::ffff:/i, '');
|
|
117
|
+
const normalizedEntry = entry.trim();
|
|
118
|
+
if (normalizedEntry.includes('/')) {
|
|
119
|
+
const [base, prefixStr] = normalizedEntry.split('/');
|
|
120
|
+
const prefix = Number(prefixStr);
|
|
121
|
+
const ipInt = ipv4ToInt(normalizedIp);
|
|
122
|
+
const baseInt = ipv4ToInt(base);
|
|
123
|
+
if (ipInt === null || baseInt === null || !Number.isInteger(prefix) || prefix < 0 || prefix > 32) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
if (prefix === 0) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
const mask = (0xffffffff << (32 - prefix)) >>> 0;
|
|
130
|
+
return (ipInt & mask) === (baseInt & mask);
|
|
131
|
+
}
|
|
132
|
+
// Exact match (case-insensitive to accommodate IPv6 hex casing)
|
|
133
|
+
return normalizedIp.toLowerCase() === normalizedEntry.replace(/^::ffff:/i, '').toLowerCase();
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=ip-match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ip-match.js","sourceRoot":"","sources":["../../src/utils/ip-match.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Supports both explicit entity names and table name matching.
|
|
6
6
|
*/
|
|
7
7
|
import { DataSource, Repository } from 'typeorm';
|
|
8
|
-
import { BaseUser, BaseSession, BaseLoginAttempt, BaseVerificationToken, BaseSocialAccount, BaseChallengeSession, BaseMFADevice, BaseAuthAudit, BaseTrustedDevice, BaseRateLimit, BaseStorageLock, BaseSocialProviderSecret } from '../../entities';
|
|
8
|
+
import { BaseUser, BaseSession, BaseLoginAttempt, BaseVerificationToken, BaseSocialAccount, BaseChallengeSession, BaseMFADevice, BaseAuthAudit, BaseTrustedDevice, BaseRateLimit, BaseStorageLock, BaseSocialProviderSecret, BaseApiKey } from '../../entities';
|
|
9
9
|
/**
|
|
10
10
|
* Get all required repositories from DataSource
|
|
11
11
|
*
|
|
@@ -29,5 +29,6 @@ export declare function getRepositories(dataSource: DataSource): {
|
|
|
29
29
|
rateLimitRepository: Repository<BaseRateLimit> | null;
|
|
30
30
|
storageLockRepository: Repository<BaseStorageLock> | null;
|
|
31
31
|
socialProviderSecretRepository: Repository<BaseSocialProviderSecret>;
|
|
32
|
+
apiKeyRepository: Repository<BaseApiKey> | null;
|
|
32
33
|
};
|
|
33
34
|
//# sourceMappingURL=get-repositories.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-repositories.d.ts","sourceRoot":"","sources":["../../../src/utils/setup/get-repositories.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAkB,UAAU,EAAiB,MAAM,SAAS,CAAC;AAEhF,OAAO,EACL,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,wBAAwB,
|
|
1
|
+
{"version":3,"file":"get-repositories.d.ts","sourceRoot":"","sources":["../../../src/utils/setup/get-repositories.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAkB,UAAU,EAAiB,MAAM,SAAS,CAAC;AAEhF,OAAO,EACL,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,wBAAwB,EACxB,UAAU,EACX,MAAM,gBAAgB,CAAC;AAExB;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG;IACvD,cAAc,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IACrC,iBAAiB,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;IAC3C,sBAAsB,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;IACrD,2BAA2B,EAAE,UAAU,CAAC,qBAAqB,CAAC,CAAC;IAC/D,uBAAuB,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC;IACvD,0BAA0B,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAC;IAC7D,mBAAmB,EAAE,UAAU,CAAC,aAAa,CAAC,CAAC;IAC/C,mBAAmB,EAAE,UAAU,CAAC,aAAa,CAAC,CAAC;IAC/C,uBAAuB,EAAE,UAAU,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAC;IAC9D,mBAAmB,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC;IACtD,qBAAqB,EAAE,UAAU,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC;IAC1D,8BAA8B,EAAE,UAAU,CAAC,wBAAwB,CAAC,CAAC;IACrE,gBAAgB,EAAE,UAAU,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;CACjD,CA8DA"}
|
|
@@ -32,6 +32,8 @@ function getRepositories(dataSource) {
|
|
|
32
32
|
trustedDeviceRepository: getRepository(dataSource, 'TrustedDevice', 'nauth_trusted_devices', false),
|
|
33
33
|
rateLimitRepository: getRepository(dataSource, 'RateLimit', 'nauth_rate_limits', false),
|
|
34
34
|
storageLockRepository: getRepository(dataSource, 'StorageLock', 'nauth_storage_locks', false),
|
|
35
|
+
// Optional - only present when the API keys feature is enabled and the entity is registered
|
|
36
|
+
apiKeyRepository: getRepository(dataSource, 'ApiKey', 'nauth_api_keys', false),
|
|
35
37
|
socialProviderSecretRepository: (() => {
|
|
36
38
|
try {
|
|
37
39
|
return getRepository(dataSource, 'SocialProviderSecret', 'nauth_social_provider_secrets', true);
|