@nauth-toolkit/core 0.1.0 → 0.1.5
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/LICENSE +90 -0
- package/README.md +9 -0
- package/package.json +8 -3
- package/jest.config.js +0 -15
- package/jest.setup.ts +0 -6
- package/src/adapters/database-columns.ts +0 -165
- package/src/adapters/express.adapter.ts +0 -385
- package/src/adapters/fastify.adapter.ts +0 -416
- package/src/adapters/index.ts +0 -16
- package/src/adapters/storage.factory.ts +0 -143
- package/src/bootstrap.ts +0 -374
- package/src/dto/auth-challenge.dto.ts +0 -231
- package/src/dto/auth-response.dto.ts +0 -253
- package/src/dto/challenge-response.dto.ts +0 -234
- package/src/dto/change-password-request.dto.ts +0 -50
- package/src/dto/change-password-response.dto.ts +0 -29
- package/src/dto/change-password.dto.ts +0 -57
- package/src/dto/error-response.dto.ts +0 -136
- package/src/dto/get-available-methods.dto.ts +0 -55
- package/src/dto/get-challenge-data-response.dto.ts +0 -28
- package/src/dto/get-challenge-data.dto.ts +0 -69
- package/src/dto/get-client-info.dto.ts +0 -104
- package/src/dto/get-device-token-response.dto.ts +0 -25
- package/src/dto/get-events-by-type.dto.ts +0 -76
- package/src/dto/get-ip-address-response.dto.ts +0 -24
- package/src/dto/get-mfa-status.dto.ts +0 -94
- package/src/dto/get-risk-assessment-history.dto.ts +0 -39
- package/src/dto/get-session-id-response.dto.ts +0 -25
- package/src/dto/get-setup-data-response.dto.ts +0 -31
- package/src/dto/get-setup-data.dto.ts +0 -75
- package/src/dto/get-suspicious-activity.dto.ts +0 -42
- package/src/dto/get-user-agent-response.dto.ts +0 -23
- package/src/dto/get-user-auth-history.dto.ts +0 -95
- package/src/dto/get-user-by-email.dto.ts +0 -61
- package/src/dto/get-user-by-id.dto.ts +0 -46
- package/src/dto/get-user-devices.dto.ts +0 -53
- package/src/dto/get-user-response.dto.ts +0 -17
- package/src/dto/has-provider.dto.ts +0 -56
- package/src/dto/index.ts +0 -57
- package/src/dto/is-trusted-device-response.dto.ts +0 -34
- package/src/dto/list-providers-response.dto.ts +0 -23
- package/src/dto/login.dto.ts +0 -95
- package/src/dto/logout-all-response.dto.ts +0 -24
- package/src/dto/logout-all.dto.ts +0 -65
- package/src/dto/logout-response.dto.ts +0 -25
- package/src/dto/logout.dto.ts +0 -64
- package/src/dto/refresh-token.dto.ts +0 -36
- package/src/dto/remove-devices.dto.ts +0 -85
- package/src/dto/resend-code-response.dto.ts +0 -32
- package/src/dto/resend-code.dto.ts +0 -51
- package/src/dto/reset-password.dto.ts +0 -115
- package/src/dto/respond-challenge.dto.ts +0 -272
- package/src/dto/set-mfa-exemption.dto.ts +0 -112
- package/src/dto/set-must-change-password-response.dto.ts +0 -27
- package/src/dto/set-must-change-password.dto.ts +0 -46
- package/src/dto/set-preferred-method.dto.ts +0 -80
- package/src/dto/setup-mfa.dto.ts +0 -98
- package/src/dto/signup.dto.ts +0 -174
- package/src/dto/social-auth.dto.ts +0 -422
- package/src/dto/trust-device-response.dto.ts +0 -30
- package/src/dto/trust-device.dto.ts +0 -9
- package/src/dto/update-user-attributes-request.dto.ts +0 -51
- package/src/dto/user-response.dto.ts +0 -138
- package/src/dto/user-update.dto.ts +0 -222
- package/src/dto/verify-email.dto.ts +0 -313
- package/src/dto/verify-mfa-code.dto.ts +0 -103
- package/src/dto/verify-phone-by-sub.dto.ts +0 -78
- package/src/dto/verify-phone.dto.ts +0 -245
- package/src/entities/auth-audit.entity.ts +0 -232
- package/src/entities/challenge-session.entity.ts +0 -116
- package/src/entities/index.ts +0 -29
- package/src/entities/login-attempt.entity.ts +0 -64
- package/src/entities/mfa-device.entity.ts +0 -151
- package/src/entities/rate-limit.entity.ts +0 -44
- package/src/entities/session.entity.ts +0 -180
- package/src/entities/social-account.entity.ts +0 -96
- package/src/entities/storage-lock.entity.ts +0 -39
- package/src/entities/trusted-device.entity.ts +0 -112
- package/src/entities/user.entity.ts +0 -243
- package/src/entities/verification-token.entity.ts +0 -141
- package/src/enums/auth-audit-event-type.enum.ts +0 -360
- package/src/enums/error-codes.enum.ts +0 -420
- package/src/enums/mfa-method.enum.ts +0 -97
- package/src/enums/risk-factor.enum.ts +0 -111
- package/src/exceptions/nauth.exception.ts +0 -231
- package/src/handlers/auth.handler.ts +0 -260
- package/src/handlers/client-info.handler.ts +0 -101
- package/src/handlers/csrf.handler.ts +0 -156
- package/src/handlers/token-delivery.handler.ts +0 -118
- package/src/index.ts +0 -118
- package/src/interfaces/client-info.interface.ts +0 -85
- package/src/interfaces/config.interface.ts +0 -2135
- package/src/interfaces/entities.interface.ts +0 -226
- package/src/interfaces/index.ts +0 -15
- package/src/interfaces/logger.interface.ts +0 -283
- package/src/interfaces/mfa-provider.interface.ts +0 -154
- package/src/interfaces/oauth.interface.ts +0 -148
- package/src/interfaces/provider.interface.ts +0 -47
- package/src/interfaces/social-auth-provider.interface.ts +0 -131
- package/src/interfaces/storage-adapter.interface.ts +0 -82
- package/src/interfaces/template.interface.ts +0 -510
- package/src/interfaces/token-verifier.interface.ts +0 -110
- package/src/internal.ts +0 -178
- package/src/platform/interfaces.ts +0 -299
- package/src/schemas/auth-config.schema.ts +0 -646
- package/src/services/adaptive-mfa-decision.service.spec.ts +0 -1058
- package/src/services/adaptive-mfa-decision.service.ts +0 -457
- package/src/services/auth-audit.service.spec.ts +0 -675
- package/src/services/auth-audit.service.ts +0 -558
- package/src/services/auth-challenge-helper.service.spec.ts +0 -3227
- package/src/services/auth-challenge-helper.service.ts +0 -825
- package/src/services/auth-flow-context-builder.service.ts +0 -520
- package/src/services/auth-flow-rules.ts +0 -202
- package/src/services/auth-flow-state-definitions.ts +0 -190
- package/src/services/auth-flow-state-machine.service.ts +0 -207
- package/src/services/auth-flow-state-machine.types.ts +0 -316
- package/src/services/auth.service.spec.ts +0 -4195
- package/src/services/auth.service.ts +0 -3727
- package/src/services/challenge.service.spec.ts +0 -1363
- package/src/services/challenge.service.ts +0 -696
- package/src/services/client-info.service.spec.ts +0 -572
- package/src/services/client-info.service.ts +0 -374
- package/src/services/csrf.service.ts +0 -54
- package/src/services/email-verification.service.spec.ts +0 -1229
- package/src/services/email-verification.service.ts +0 -578
- package/src/services/geo-location.service.spec.ts +0 -603
- package/src/services/geo-location.service.ts +0 -599
- package/src/services/index.ts +0 -13
- package/src/services/jwt.service.spec.ts +0 -882
- package/src/services/jwt.service.ts +0 -621
- package/src/services/mfa-base.service.spec.ts +0 -246
- package/src/services/mfa-base.service.ts +0 -611
- package/src/services/mfa.service.spec.ts +0 -693
- package/src/services/mfa.service.ts +0 -960
- package/src/services/password.service.spec.ts +0 -166
- package/src/services/password.service.ts +0 -309
- package/src/services/phone-verification.service.spec.ts +0 -1120
- package/src/services/phone-verification.service.ts +0 -751
- package/src/services/risk-detection.service.spec.ts +0 -1292
- package/src/services/risk-detection.service.ts +0 -1012
- package/src/services/risk-scoring.service.spec.ts +0 -204
- package/src/services/risk-scoring.service.ts +0 -131
- package/src/services/session.service.spec.ts +0 -1293
- package/src/services/session.service.ts +0 -803
- package/src/services/social-account.service.spec.ts +0 -725
- package/src/services/social-auth-base.service.spec.ts +0 -418
- package/src/services/social-auth-base.service.ts +0 -581
- package/src/services/social-auth.service.spec.ts +0 -238
- package/src/services/social-auth.service.ts +0 -436
- package/src/services/social-provider-registry.service.spec.ts +0 -238
- package/src/services/social-provider-registry.service.ts +0 -122
- package/src/services/trusted-device.service.spec.ts +0 -505
- package/src/services/trusted-device.service.ts +0 -339
- package/src/storage/account-lockout-storage.service.spec.ts +0 -310
- package/src/storage/account-lockout-storage.service.ts +0 -89
- package/src/storage/index.ts +0 -3
- package/src/storage/memory-storage.adapter.ts +0 -443
- package/src/storage/rate-limit-storage.service.spec.ts +0 -247
- package/src/storage/rate-limit-storage.service.ts +0 -38
- package/src/templates/html-template.engine.spec.ts +0 -161
- package/src/templates/html-template.engine.ts +0 -688
- package/src/templates/index.ts +0 -7
- package/src/utils/common-passwords.spec.ts +0 -230
- package/src/utils/common-passwords.ts +0 -170
- package/src/utils/context-storage.ts +0 -188
- package/src/utils/cookie-names.util.ts +0 -67
- package/src/utils/cookies.util.ts +0 -94
- package/src/utils/index.ts +0 -12
- package/src/utils/ip-extractor.spec.ts +0 -330
- package/src/utils/ip-extractor.ts +0 -220
- package/src/utils/nauth-logger.spec.ts +0 -388
- package/src/utils/nauth-logger.ts +0 -215
- package/src/utils/pii-redactor.spec.ts +0 -130
- package/src/utils/pii-redactor.ts +0 -288
- package/src/utils/setup/get-repositories.ts +0 -140
- package/src/utils/setup/init-services.ts +0 -422
- package/src/utils/setup/init-social.ts +0 -189
- package/src/utils/setup/init-storage.ts +0 -94
- package/src/utils/setup/register-mfa.ts +0 -165
- package/src/utils/setup/run-nauth-migrations.ts +0 -61
- package/src/utils/token-delivery-policy.ts +0 -38
- package/src/validators/template.validator.ts +0 -219
- package/tsconfig.json +0 -37
- package/tsconfig.lint.json +0 -6
|
@@ -1,646 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Zod Schema Validation for NAuth Configuration
|
|
3
|
-
*
|
|
4
|
-
* Provides runtime validation for auth configuration with comprehensive
|
|
5
|
-
* cross-dependency checks. Ensures configuration is valid before module initialization.
|
|
6
|
-
*
|
|
7
|
-
* This schema validates:
|
|
8
|
-
* - Required fields
|
|
9
|
-
* - Type correctness
|
|
10
|
-
* - Cross-dependencies (e.g., email config requires emailProvider)
|
|
11
|
-
* - Algorithm-specific requirements (JWT symmetric vs asymmetric)
|
|
12
|
-
* - MFA enforcement modes and their requirements
|
|
13
|
-
* - Social provider requirements
|
|
14
|
-
* - GeoLocation MaxMind credentials
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```typescript
|
|
18
|
-
* import { authConfigSchema } from '@nauth-toolkit/core';
|
|
19
|
-
*
|
|
20
|
-
* const result = authConfigSchema.safeParse(config);
|
|
21
|
-
* if (!result.success) {
|
|
22
|
-
* console.error('Config validation failed:', result.error.errors);
|
|
23
|
-
* }
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
import { z } from 'zod';
|
|
28
|
-
|
|
29
|
-
// ============================================================================
|
|
30
|
-
// JWT Configuration Schemas
|
|
31
|
-
// ============================================================================
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Access token configuration schema
|
|
35
|
-
*/
|
|
36
|
-
const accessTokenConfigSchema = z.object({
|
|
37
|
-
secret: z.string().optional(),
|
|
38
|
-
privateKey: z.string().optional(),
|
|
39
|
-
publicKey: z.string().optional(),
|
|
40
|
-
expiresIn: z.union([z.string(), z.number()]),
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Refresh token configuration schema
|
|
45
|
-
*/
|
|
46
|
-
const refreshTokenConfigSchema = z.object({
|
|
47
|
-
secret: z.string().min(32, 'Refresh token secret must be at least 32 characters (256 bits) for security'),
|
|
48
|
-
expiresIn: z.union([z.string(), z.number()]),
|
|
49
|
-
rotation: z.boolean().optional(),
|
|
50
|
-
reuseDetection: z.boolean().optional(),
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* JWT configuration schema
|
|
55
|
-
*/
|
|
56
|
-
export const jwtConfigSchema = z
|
|
57
|
-
.object({
|
|
58
|
-
algorithm: z.enum(['HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512']).optional(),
|
|
59
|
-
accessToken: accessTokenConfigSchema,
|
|
60
|
-
refreshToken: refreshTokenConfigSchema,
|
|
61
|
-
issuer: z.string().optional(),
|
|
62
|
-
audience: z.union([z.string(), z.array(z.string())]).optional(),
|
|
63
|
-
})
|
|
64
|
-
.superRefine((data, ctx) => {
|
|
65
|
-
const algorithm = data.algorithm || 'HS256';
|
|
66
|
-
const symmetricAlgorithms = ['HS256', 'HS384', 'HS512'];
|
|
67
|
-
const asymmetricAlgorithms = ['RS256', 'RS384', 'RS512'];
|
|
68
|
-
|
|
69
|
-
if (symmetricAlgorithms.includes(algorithm)) {
|
|
70
|
-
if (!data.accessToken.secret) {
|
|
71
|
-
ctx.addIssue({
|
|
72
|
-
code: z.ZodIssueCode.custom,
|
|
73
|
-
message: `jwt.accessToken.secret is required for symmetric algorithm ${algorithm}`,
|
|
74
|
-
path: ['accessToken', 'secret'],
|
|
75
|
-
});
|
|
76
|
-
} else if (data.accessToken.secret.length < 32) {
|
|
77
|
-
ctx.addIssue({
|
|
78
|
-
code: z.ZodIssueCode.custom,
|
|
79
|
-
message: 'JWT access token secret must be at least 32 characters (256 bits) for security',
|
|
80
|
-
path: ['accessToken', 'secret'],
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
} else if (asymmetricAlgorithms.includes(algorithm)) {
|
|
84
|
-
if (!data.accessToken.privateKey) {
|
|
85
|
-
ctx.addIssue({
|
|
86
|
-
code: z.ZodIssueCode.custom,
|
|
87
|
-
message: `jwt.accessToken.privateKey is required for asymmetric algorithm ${algorithm}`,
|
|
88
|
-
path: ['accessToken', 'privateKey'],
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
if (!data.accessToken.publicKey) {
|
|
92
|
-
ctx.addIssue({
|
|
93
|
-
code: z.ZodIssueCode.custom,
|
|
94
|
-
message: `jwt.accessToken.publicKey is required for asymmetric algorithm ${algorithm}`,
|
|
95
|
-
path: ['accessToken', 'publicKey'],
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// ============================================================================
|
|
102
|
-
// Signup Configuration Schema
|
|
103
|
-
// ============================================================================
|
|
104
|
-
|
|
105
|
-
export const signupConfigSchema = z.object({
|
|
106
|
-
enabled: z.boolean().optional(),
|
|
107
|
-
verificationMethod: z.enum(['none', 'email', 'phone', 'both']).optional(),
|
|
108
|
-
allowDuplicatePhones: z.boolean().optional(),
|
|
109
|
-
emailVerification: z
|
|
110
|
-
.object({
|
|
111
|
-
expiresIn: z.number().optional(),
|
|
112
|
-
resendDelay: z.number().optional(),
|
|
113
|
-
rateLimitMax: z.number().optional(),
|
|
114
|
-
rateLimitWindow: z.number().optional(),
|
|
115
|
-
maxAttemptsPerUser: z.number().optional(),
|
|
116
|
-
maxAttemptsPerIP: z.number().optional(),
|
|
117
|
-
attemptWindow: z.number().optional(),
|
|
118
|
-
})
|
|
119
|
-
.optional(),
|
|
120
|
-
phoneVerification: z
|
|
121
|
-
.object({
|
|
122
|
-
codeLength: z.number().optional(),
|
|
123
|
-
expiresIn: z.number().optional(),
|
|
124
|
-
maxAttempts: z.number().optional(),
|
|
125
|
-
resendDelay: z.number().optional(),
|
|
126
|
-
rateLimitMax: z.number().optional(),
|
|
127
|
-
rateLimitWindow: z.number().optional(),
|
|
128
|
-
maxAttemptsPerUser: z.number().optional(),
|
|
129
|
-
maxAttemptsPerIP: z.number().optional(),
|
|
130
|
-
attemptWindow: z.number().optional(),
|
|
131
|
-
})
|
|
132
|
-
.optional(),
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// ============================================================================
|
|
136
|
-
// Login Configuration Schema
|
|
137
|
-
// ============================================================================
|
|
138
|
-
|
|
139
|
-
export const loginConfigSchema = z.object({
|
|
140
|
-
identifierType: z.enum(['email', 'username', 'phone', 'email_or_username']).optional(),
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
// ============================================================================
|
|
144
|
-
// Password Configuration Schema
|
|
145
|
-
// ============================================================================
|
|
146
|
-
|
|
147
|
-
export const passwordConfigSchema = z.object({
|
|
148
|
-
minLength: z.number().optional(),
|
|
149
|
-
maxLength: z.number().optional(),
|
|
150
|
-
requireUppercase: z.boolean().optional(),
|
|
151
|
-
requireLowercase: z.boolean().optional(),
|
|
152
|
-
requireNumbers: z.boolean().optional(),
|
|
153
|
-
requireSpecialChars: z.boolean().optional(),
|
|
154
|
-
specialChars: z.string().optional(),
|
|
155
|
-
preventCommon: z.boolean().optional(),
|
|
156
|
-
preventUserInfo: z.boolean().optional(),
|
|
157
|
-
historyCount: z.number().optional(),
|
|
158
|
-
expiryDays: z.number().optional(),
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
// ============================================================================
|
|
162
|
-
// Lockout Configuration Schema
|
|
163
|
-
// ============================================================================
|
|
164
|
-
|
|
165
|
-
export const lockoutConfigSchema = z.object({
|
|
166
|
-
enabled: z.boolean().optional(),
|
|
167
|
-
maxAttempts: z.number().optional(),
|
|
168
|
-
duration: z.number().optional(),
|
|
169
|
-
resetOnSuccess: z.boolean().optional(),
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
// ============================================================================
|
|
173
|
-
// Session Configuration Schema
|
|
174
|
-
// ============================================================================
|
|
175
|
-
|
|
176
|
-
export const sessionConfigSchema = z.object({
|
|
177
|
-
maxConcurrent: z.number().optional(),
|
|
178
|
-
disallowMultipleSessions: z.boolean().optional(),
|
|
179
|
-
maxLifetime: z.union([z.string(), z.number()]).optional(),
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// ============================================================================
|
|
183
|
-
// Security Configuration Schema
|
|
184
|
-
// ============================================================================
|
|
185
|
-
|
|
186
|
-
export const securityConfigSchema = z.object({
|
|
187
|
-
csrf: z
|
|
188
|
-
.object({
|
|
189
|
-
cookieName: z.string().optional(),
|
|
190
|
-
headerName: z.string().optional(),
|
|
191
|
-
tokenLength: z.number().optional(),
|
|
192
|
-
excludedPaths: z.array(z.string()).optional(),
|
|
193
|
-
cookieOptions: z
|
|
194
|
-
.object({
|
|
195
|
-
// httpOnly is always false (hardcoded) - CSRF token must be readable by JavaScript
|
|
196
|
-
secure: z.boolean().optional(),
|
|
197
|
-
sameSite: z.enum(['strict', 'lax', 'none']).optional(),
|
|
198
|
-
domain: z.string().optional(),
|
|
199
|
-
path: z.string().optional(),
|
|
200
|
-
})
|
|
201
|
-
.optional(),
|
|
202
|
-
})
|
|
203
|
-
.optional(),
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
// ============================================================================
|
|
207
|
-
// Lifecycle Hooks Schema
|
|
208
|
-
// ============================================================================
|
|
209
|
-
|
|
210
|
-
// Note: Functions cannot be validated by Zod, so we use z.any() for hooks
|
|
211
|
-
export const lifecycleHooksSchema = z.object({
|
|
212
|
-
beforeSignup: z.any().optional(),
|
|
213
|
-
afterSignup: z.any().optional(),
|
|
214
|
-
beforeLogin: z.any().optional(),
|
|
215
|
-
afterLogin: z.any().optional(),
|
|
216
|
-
afterLoginFailed: z.any().optional(),
|
|
217
|
-
beforePasswordChange: z.any().optional(),
|
|
218
|
-
afterPasswordChange: z.any().optional(),
|
|
219
|
-
beforeAccountLock: z.any().optional(),
|
|
220
|
-
afterAccountLock: z.any().optional(),
|
|
221
|
-
onAdaptiveMFATriggered: z.any().optional(),
|
|
222
|
-
onSignInBlocked: z.any().optional(),
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
// ============================================================================
|
|
226
|
-
// Email Configuration Schema
|
|
227
|
-
// ============================================================================
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Custom template definition schema
|
|
231
|
-
*
|
|
232
|
-
* Validates template definition structure:
|
|
233
|
-
* - Must have either htmlPath OR html content (not both)
|
|
234
|
-
* - Can have either textPath OR text content (not both)
|
|
235
|
-
* - Subject is optional (can be extracted from frontmatter)
|
|
236
|
-
*/
|
|
237
|
-
const customTemplateDefinitionSchema = z
|
|
238
|
-
.object({
|
|
239
|
-
htmlPath: z.string().optional(),
|
|
240
|
-
html: z.string().optional(),
|
|
241
|
-
textPath: z.string().optional(),
|
|
242
|
-
text: z.string().optional(),
|
|
243
|
-
subject: z.string().optional(),
|
|
244
|
-
})
|
|
245
|
-
.refine((data) => !!(data.htmlPath || data.html), {
|
|
246
|
-
message: 'Must provide either "htmlPath" or "html" content',
|
|
247
|
-
})
|
|
248
|
-
.refine((data) => !(data.htmlPath && data.html), {
|
|
249
|
-
message: 'Cannot provide both "htmlPath" and "html". Use one or the other.',
|
|
250
|
-
})
|
|
251
|
-
.refine((data) => !(data.textPath && data.text), {
|
|
252
|
-
message: 'Cannot provide both "textPath" and "text". Use one or the other.',
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Template configuration schema
|
|
257
|
-
*
|
|
258
|
-
* Validates template configuration:
|
|
259
|
-
* - Global variables for branding
|
|
260
|
-
* - Custom templates with required parameters
|
|
261
|
-
*/
|
|
262
|
-
const templateConfigSchema = z.object({
|
|
263
|
-
engine: z.any().optional(), // TemplateEngine instance - runtime validation
|
|
264
|
-
globalVariables: z.record(z.union([z.string(), z.number(), z.boolean()])).optional(),
|
|
265
|
-
customTemplates: z.record(customTemplateDefinitionSchema).optional(),
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Email configuration schema
|
|
270
|
-
*/
|
|
271
|
-
export const emailConfigSchema = z.object({
|
|
272
|
-
appName: z.string().optional(),
|
|
273
|
-
companyName: z.string().optional(),
|
|
274
|
-
logoUrl: z.string().optional(),
|
|
275
|
-
supportEmail: z.string().email().optional(),
|
|
276
|
-
dashboardUrl: z.string().url().optional(),
|
|
277
|
-
brandColor: z.string().optional(),
|
|
278
|
-
footerDisclaimer: z.string().optional(),
|
|
279
|
-
templates: templateConfigSchema.optional(),
|
|
280
|
-
});
|
|
281
|
-
|
|
282
|
-
// ============================================================================
|
|
283
|
-
// Phone Configuration Schema
|
|
284
|
-
// ============================================================================
|
|
285
|
-
|
|
286
|
-
export const phoneConfigSchema = z.object({
|
|
287
|
-
// verification section removed - moved to signup.phoneVerification
|
|
288
|
-
// Kept empty for backwards compatibility
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
// ============================================================================
|
|
292
|
-
// Social Provider Configuration Schema
|
|
293
|
-
// ============================================================================
|
|
294
|
-
|
|
295
|
-
export const socialProviderConfigSchema = z.object({
|
|
296
|
-
enabled: z.boolean().optional(),
|
|
297
|
-
clientId: z.union([z.string(), z.array(z.string())]).optional(),
|
|
298
|
-
clientSecret: z.string().optional(),
|
|
299
|
-
callbackUrl: z.string().optional(),
|
|
300
|
-
scopes: z.array(z.string()).optional(),
|
|
301
|
-
autoLink: z.boolean().optional(),
|
|
302
|
-
allowSignup: z.boolean().optional(),
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
export const socialConfigSchema = z.object({
|
|
306
|
-
google: socialProviderConfigSchema.optional(),
|
|
307
|
-
apple: socialProviderConfigSchema.optional(),
|
|
308
|
-
facebook: socialProviderConfigSchema.optional(),
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
// ============================================================================
|
|
312
|
-
// MFA Configuration Schemas
|
|
313
|
-
// ============================================================================
|
|
314
|
-
|
|
315
|
-
export const totpConfigSchema = z.object({
|
|
316
|
-
window: z.number().optional(),
|
|
317
|
-
stepSeconds: z.number().optional(),
|
|
318
|
-
digits: z.number().optional(),
|
|
319
|
-
algorithm: z.enum(['sha1', 'sha256', 'sha512']).optional(),
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
export const passkeyConfigSchema = z.object({
|
|
323
|
-
rpName: z.string(),
|
|
324
|
-
rpId: z.string(),
|
|
325
|
-
origin: z.union([z.string(), z.array(z.string())]).optional(),
|
|
326
|
-
timeout: z.number().optional(),
|
|
327
|
-
userVerification: z.enum(['required', 'preferred', 'discouraged']).optional(),
|
|
328
|
-
authenticatorAttachment: z.enum(['platform', 'cross-platform']).optional(),
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
export const backupCodesConfigSchema = z.object({
|
|
332
|
-
enabled: z.boolean().optional(),
|
|
333
|
-
codeCount: z.number().optional(),
|
|
334
|
-
codeLength: z.number().optional(),
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
export const riskLevelConfigSchema = z.object({
|
|
338
|
-
maxScore: z.number(),
|
|
339
|
-
action: z.enum(['allow', 'require_mfa', 'block_signin']).optional(),
|
|
340
|
-
notifyUser: z.boolean().optional(),
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
export const adaptiveMFAConfigSchema = z.object({
|
|
344
|
-
triggers: z
|
|
345
|
-
.array(z.enum(['new_device', 'new_ip', 'new_country', 'impossible_travel', 'suspicious_activity']))
|
|
346
|
-
.optional(),
|
|
347
|
-
riskThreshold: z.number().optional(), // Deprecated
|
|
348
|
-
riskWeights: z.record(z.string(), z.number()).optional(),
|
|
349
|
-
riskLevels: z
|
|
350
|
-
.object({
|
|
351
|
-
low: riskLevelConfigSchema.optional(),
|
|
352
|
-
medium: riskLevelConfigSchema.optional(),
|
|
353
|
-
high: riskLevelConfigSchema.optional(),
|
|
354
|
-
})
|
|
355
|
-
.optional(),
|
|
356
|
-
blockedSignIn: z
|
|
357
|
-
.object({
|
|
358
|
-
blockDuration: z.number().optional(),
|
|
359
|
-
message: z.string().optional(),
|
|
360
|
-
errorCode: z.string().optional(),
|
|
361
|
-
})
|
|
362
|
-
.optional(),
|
|
363
|
-
maxTravelSpeed: z.number().optional(),
|
|
364
|
-
countryChangeThreshold: z.number().optional(),
|
|
365
|
-
suspiciousActivityWindow: z.number().optional(),
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
export const mfaConfigSchema = z.object({
|
|
369
|
-
enabled: z.boolean().optional(),
|
|
370
|
-
enforcement: z.enum(['OPTIONAL', 'REQUIRED', 'ADAPTIVE']).optional(),
|
|
371
|
-
gracePeriod: z.number().optional(),
|
|
372
|
-
allowedMethods: z.array(z.enum(['totp', 'sms', 'email', 'passkey'])).optional(),
|
|
373
|
-
requireForSocialLogin: z.boolean().optional(),
|
|
374
|
-
rememberDevices: z.enum(['always', 'user_opt_in', 'never']).optional(),
|
|
375
|
-
rememberDeviceDays: z.number().optional(),
|
|
376
|
-
bypassMFAForTrustedDevices: z.boolean().optional(),
|
|
377
|
-
issuer: z.string().optional(),
|
|
378
|
-
totp: totpConfigSchema.optional(),
|
|
379
|
-
passkey: passkeyConfigSchema.optional(),
|
|
380
|
-
backup: backupCodesConfigSchema.optional(),
|
|
381
|
-
adaptive: adaptiveMFAConfigSchema.optional(),
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
// ============================================================================
|
|
385
|
-
// Token Delivery Configuration Schema
|
|
386
|
-
// ============================================================================
|
|
387
|
-
|
|
388
|
-
export const tokenDeliveryConfigSchema = z.object({
|
|
389
|
-
method: z.enum(['json', 'cookies', 'hybrid']).optional(),
|
|
390
|
-
cookieNamePrefix: z.string().optional(), // Prefix for all cookie names (default: 'nauth_')
|
|
391
|
-
cookieOptions: z
|
|
392
|
-
.object({
|
|
393
|
-
secure: z.boolean().optional(),
|
|
394
|
-
sameSite: z.enum(['strict', 'lax', 'none']).optional(),
|
|
395
|
-
path: z.string().optional(),
|
|
396
|
-
domain: z.string().optional(),
|
|
397
|
-
})
|
|
398
|
-
.optional(),
|
|
399
|
-
hybridPolicy: z
|
|
400
|
-
.object({
|
|
401
|
-
webOrigins: z.array(z.string()).optional(),
|
|
402
|
-
nativeOrigins: z.array(z.string()).optional(),
|
|
403
|
-
})
|
|
404
|
-
.optional(),
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
// ============================================================================
|
|
408
|
-
// Challenge Configuration Schema
|
|
409
|
-
// ============================================================================
|
|
410
|
-
|
|
411
|
-
export const challengeConfigSchema = z.object({
|
|
412
|
-
maxAttempts: z.number().int().positive().optional(),
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
// ============================================================================
|
|
416
|
-
// GeoLocation Configuration Schema
|
|
417
|
-
// ============================================================================
|
|
418
|
-
|
|
419
|
-
export const geoLocationConfigSchema = z.object({
|
|
420
|
-
maxMind: z
|
|
421
|
-
.object({
|
|
422
|
-
dbPath: z.string().optional(),
|
|
423
|
-
skipDownloads: z.boolean().optional(),
|
|
424
|
-
licenseKey: z.string().optional(),
|
|
425
|
-
accountId: z.number().optional(),
|
|
426
|
-
autoDownloadOnStartup: z.boolean().optional(),
|
|
427
|
-
editions: z.array(z.string()).optional(),
|
|
428
|
-
})
|
|
429
|
-
.optional(),
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
// ============================================================================
|
|
433
|
-
// Root Configuration Schema with Cross-Dependency Validation
|
|
434
|
-
// ============================================================================
|
|
435
|
-
|
|
436
|
-
/**
|
|
437
|
-
* Root authentication configuration schema
|
|
438
|
-
*
|
|
439
|
-
* Validates all configuration sections and enforces cross-dependencies:
|
|
440
|
-
* - Email/phone verification requires respective providers
|
|
441
|
-
* - MFA enforcement modes require specific configurations
|
|
442
|
-
* - Social providers require credentials when enabled
|
|
443
|
-
* - JWT algorithm requires appropriate keys
|
|
444
|
-
* - MaxMind geolocation requires credentials for downloads
|
|
445
|
-
*/
|
|
446
|
-
export const authConfigSchema = z
|
|
447
|
-
.object({
|
|
448
|
-
tablePrefix: z.string().optional(),
|
|
449
|
-
jwt: jwtConfigSchema,
|
|
450
|
-
signup: signupConfigSchema.optional(),
|
|
451
|
-
login: loginConfigSchema.optional(),
|
|
452
|
-
password: passwordConfigSchema.optional(),
|
|
453
|
-
lockout: lockoutConfigSchema.optional(),
|
|
454
|
-
session: sessionConfigSchema.optional(),
|
|
455
|
-
security: securityConfigSchema.optional(),
|
|
456
|
-
hooks: lifecycleHooksSchema.optional(),
|
|
457
|
-
auditLogs: z
|
|
458
|
-
.object({
|
|
459
|
-
enabled: z.boolean().optional(),
|
|
460
|
-
fireAndForget: z.boolean().optional(),
|
|
461
|
-
})
|
|
462
|
-
.optional(),
|
|
463
|
-
emailProvider: z.any().optional(), // Runtime instance - cannot validate type
|
|
464
|
-
email: emailConfigSchema.optional(),
|
|
465
|
-
smsProvider: z.any().optional(), // Runtime instance - cannot validate type
|
|
466
|
-
phone: phoneConfigSchema.optional(),
|
|
467
|
-
storageAdapter: z.any().optional(), // Runtime instance - cannot validate type
|
|
468
|
-
social: socialConfigSchema.optional(),
|
|
469
|
-
mfa: mfaConfigSchema.optional(),
|
|
470
|
-
logger: z.any().optional(), // LoggerService or NAuthLoggerConfig - runtime instance
|
|
471
|
-
tokenDelivery: tokenDeliveryConfigSchema.optional(),
|
|
472
|
-
challenge: challengeConfigSchema.optional(),
|
|
473
|
-
geoLocation: geoLocationConfigSchema.optional(),
|
|
474
|
-
})
|
|
475
|
-
.superRefine((data, ctx) => {
|
|
476
|
-
// ============================================================================
|
|
477
|
-
// 1. Email Verification Dependencies
|
|
478
|
-
// ============================================================================
|
|
479
|
-
const verificationMethod = data.signup?.verificationMethod || 'email';
|
|
480
|
-
if ((verificationMethod === 'email' || verificationMethod === 'both') && !data.emailProvider) {
|
|
481
|
-
ctx.addIssue({
|
|
482
|
-
code: z.ZodIssueCode.custom,
|
|
483
|
-
message: 'emailProvider is required when email verification is enabled',
|
|
484
|
-
path: ['emailProvider'],
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// ============================================================================
|
|
489
|
-
// 2. Phone Verification Dependencies
|
|
490
|
-
// ============================================================================
|
|
491
|
-
if ((verificationMethod === 'phone' || verificationMethod === 'both') && !data.smsProvider) {
|
|
492
|
-
ctx.addIssue({
|
|
493
|
-
code: z.ZodIssueCode.custom,
|
|
494
|
-
message: 'smsProvider is required when phone verification is enabled',
|
|
495
|
-
path: ['smsProvider'],
|
|
496
|
-
});
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
// ============================================================================
|
|
500
|
-
// 3. Email Config Requires Provider
|
|
501
|
-
// ============================================================================
|
|
502
|
-
if (data.email && !data.emailProvider) {
|
|
503
|
-
ctx.addIssue({
|
|
504
|
-
code: z.ZodIssueCode.custom,
|
|
505
|
-
message: 'emailProvider is required when email configuration is provided',
|
|
506
|
-
path: ['emailProvider'],
|
|
507
|
-
});
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// ============================================================================
|
|
511
|
-
// 4. Phone Config Requires Provider
|
|
512
|
-
// ============================================================================
|
|
513
|
-
if (data.phone && !data.smsProvider) {
|
|
514
|
-
ctx.addIssue({
|
|
515
|
-
code: z.ZodIssueCode.custom,
|
|
516
|
-
message: 'smsProvider is required when phone configuration is provided',
|
|
517
|
-
path: ['smsProvider'],
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// ============================================================================
|
|
522
|
-
// 5. MFA ADAPTIVE Enforcement Validation
|
|
523
|
-
// ============================================================================
|
|
524
|
-
if (data.mfa?.enforcement === 'ADAPTIVE') {
|
|
525
|
-
if (!data.mfa.enabled) {
|
|
526
|
-
ctx.addIssue({
|
|
527
|
-
code: z.ZodIssueCode.custom,
|
|
528
|
-
message: 'mfa.enabled must be true when enforcement is ADAPTIVE',
|
|
529
|
-
path: ['mfa', 'enabled'],
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
if (!data.mfa.adaptive) {
|
|
533
|
-
ctx.addIssue({
|
|
534
|
-
code: z.ZodIssueCode.custom,
|
|
535
|
-
message: 'mfa.adaptive configuration is required when enforcement is ADAPTIVE',
|
|
536
|
-
path: ['mfa', 'adaptive'],
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// ============================================================================
|
|
542
|
-
// 6. MFA REQUIRED Enforcement Validation
|
|
543
|
-
// ============================================================================
|
|
544
|
-
if (data.mfa?.enforcement === 'REQUIRED' && data.mfa.gracePeriod === undefined) {
|
|
545
|
-
// Grace period is optional but recommended - just warn, don't fail
|
|
546
|
-
// This is handled at runtime level, not validation level
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
// ============================================================================
|
|
550
|
-
// 7. MFA SMS Method Requires SMS Provider
|
|
551
|
-
// ============================================================================
|
|
552
|
-
if (data.mfa?.allowedMethods?.includes('sms') && !data.smsProvider) {
|
|
553
|
-
ctx.addIssue({
|
|
554
|
-
code: z.ZodIssueCode.custom,
|
|
555
|
-
message: 'smsProvider is required when MFA SMS method is enabled',
|
|
556
|
-
path: ['smsProvider'],
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// ============================================================================
|
|
561
|
-
// 8. MFA Email Method Requires Email Provider
|
|
562
|
-
// ============================================================================
|
|
563
|
-
if (data.mfa?.allowedMethods?.includes('email') && !data.emailProvider) {
|
|
564
|
-
ctx.addIssue({
|
|
565
|
-
code: z.ZodIssueCode.custom,
|
|
566
|
-
message: 'emailProvider is required when MFA Email method is enabled',
|
|
567
|
-
path: ['emailProvider'],
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
// ============================================================================
|
|
572
|
-
// 9. MFA Passkey Requires Passkey Config
|
|
573
|
-
// ============================================================================
|
|
574
|
-
if (data.mfa?.allowedMethods?.includes('passkey') && !data.mfa.passkey) {
|
|
575
|
-
ctx.addIssue({
|
|
576
|
-
code: z.ZodIssueCode.custom,
|
|
577
|
-
message: 'mfa.passkey configuration is required when passkey method is enabled',
|
|
578
|
-
path: ['mfa', 'passkey'],
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
// ============================================================================
|
|
583
|
-
// 10. MFA Remember Devices Validation
|
|
584
|
-
// ============================================================================
|
|
585
|
-
if (
|
|
586
|
-
data.mfa?.rememberDevices &&
|
|
587
|
-
data.mfa.rememberDevices !== 'never' &&
|
|
588
|
-
data.mfa.rememberDeviceDays === undefined
|
|
589
|
-
) {
|
|
590
|
-
// Remember device days has default - this is informational only
|
|
591
|
-
// Validation passes, but we could add a warning if needed
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// ============================================================================
|
|
595
|
-
// 11. Social Provider Validation
|
|
596
|
-
// ============================================================================
|
|
597
|
-
['google', 'apple', 'facebook'].forEach((provider) => {
|
|
598
|
-
const providerConfig = data.social?.[provider as 'google' | 'apple' | 'facebook'];
|
|
599
|
-
if (providerConfig?.enabled) {
|
|
600
|
-
if (!providerConfig.clientId) {
|
|
601
|
-
ctx.addIssue({
|
|
602
|
-
code: z.ZodIssueCode.custom,
|
|
603
|
-
message: `clientId is required when ${provider} provider is enabled`,
|
|
604
|
-
path: ['social', provider, 'clientId'],
|
|
605
|
-
});
|
|
606
|
-
}
|
|
607
|
-
if (!providerConfig.clientSecret) {
|
|
608
|
-
ctx.addIssue({
|
|
609
|
-
code: z.ZodIssueCode.custom,
|
|
610
|
-
message: `clientSecret is required when ${provider} provider is enabled`,
|
|
611
|
-
path: ['social', provider, 'clientSecret'],
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
// ============================================================================
|
|
618
|
-
// 12. MaxMind GeoLocation Validation
|
|
619
|
-
// ============================================================================
|
|
620
|
-
if (data.geoLocation?.maxMind) {
|
|
621
|
-
const maxMind = data.geoLocation.maxMind;
|
|
622
|
-
if (!maxMind.skipDownloads && maxMind.autoDownloadOnStartup) {
|
|
623
|
-
if (!maxMind.licenseKey || !maxMind.accountId) {
|
|
624
|
-
ctx.addIssue({
|
|
625
|
-
code: z.ZodIssueCode.custom,
|
|
626
|
-
message: 'MaxMind licenseKey and accountId are required when autoDownloadOnStartup is enabled',
|
|
627
|
-
path: ['geoLocation', 'maxMind', 'licenseKey'],
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
// ============================================================================
|
|
634
|
-
// 13. Token Delivery Hybrid Policy Validation
|
|
635
|
-
// ============================================================================
|
|
636
|
-
if (data.tokenDelivery?.method === 'hybrid' && !data.tokenDelivery.hybridPolicy) {
|
|
637
|
-
// Hybrid policy is optional but recommended - validation passes
|
|
638
|
-
// Runtime will handle defaults
|
|
639
|
-
}
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
/**
|
|
643
|
-
* Type inference from Zod schema
|
|
644
|
-
* This provides type safety while Zod provides runtime validation
|
|
645
|
-
*/
|
|
646
|
-
export type NAuthConfig = z.infer<typeof authConfigSchema>;
|