@nauth-toolkit/core 0.1.0 → 0.1.3
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 +30 -0
- package/package.json +7 -2
- 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
package/src/bootstrap.ts
DELETED
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* NAuth Bootstrap
|
|
3
|
-
*
|
|
4
|
-
* Entry point for initializing NAuth with platform adapters.
|
|
5
|
-
*
|
|
6
|
-
* **Usage:**
|
|
7
|
-
* ```typescript
|
|
8
|
-
* import { NAuth, ExpressAdapter } from '@nauth-toolkit/core';
|
|
9
|
-
*
|
|
10
|
-
* const nauth = await NAuth.create({
|
|
11
|
-
* config: authConfig,
|
|
12
|
-
* dataSource: dataSource,
|
|
13
|
-
* adapter: new ExpressAdapter(),
|
|
14
|
-
* });
|
|
15
|
-
*
|
|
16
|
-
* // Mount middleware
|
|
17
|
-
* app.use(nauth.middleware.clientInfo);
|
|
18
|
-
* app.use(nauth.middleware.csrf);
|
|
19
|
-
* app.use(nauth.middleware.auth);
|
|
20
|
-
* app.use(nauth.middleware.tokenDelivery);
|
|
21
|
-
*
|
|
22
|
-
* // Protected routes
|
|
23
|
-
* app.get('/profile', nauth.helpers.requireAuth(), (req, res) => {
|
|
24
|
-
* const user = nauth.helpers.getCurrentUser();
|
|
25
|
-
* res.json({ user });
|
|
26
|
-
* });
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
import { DataSource } from 'typeorm';
|
|
31
|
-
import { NAuthConfig } from './interfaces/config.interface';
|
|
32
|
-
import { NAuthLogger } from './utils/nauth-logger';
|
|
33
|
-
import { NAuthException } from './exceptions/nauth.exception';
|
|
34
|
-
import { AuthErrorCode } from './enums/error-codes.enum';
|
|
35
|
-
import { NAuthAdapter, NAuthRequest, NAuthResponse } from './platform/interfaces';
|
|
36
|
-
import { ExpressAdapter } from './adapters/express.adapter';
|
|
37
|
-
import { ContextStorage } from './utils/context-storage';
|
|
38
|
-
|
|
39
|
-
// Handlers
|
|
40
|
-
import { ClientInfoHandler } from './handlers/client-info.handler';
|
|
41
|
-
import { AuthHandler } from './handlers/auth.handler';
|
|
42
|
-
import { TokenDeliveryHandler } from './handlers/token-delivery.handler';
|
|
43
|
-
import { CsrfHandler } from './handlers/csrf.handler';
|
|
44
|
-
import { CsrfService } from './services/csrf.service';
|
|
45
|
-
|
|
46
|
-
// Setup Helpers
|
|
47
|
-
import { getRepositories } from './utils/setup/get-repositories';
|
|
48
|
-
import { initStorage } from './utils/setup/init-storage';
|
|
49
|
-
import { initServices, NAuthServices } from './utils/setup/init-services';
|
|
50
|
-
import { registerMFAProviders } from './utils/setup/register-mfa';
|
|
51
|
-
import { initSocialAuth, NAuthSocialProviders } from './utils/setup/init-social';
|
|
52
|
-
import { runNAuthMigrationsOnStartup } from './utils/setup/run-nauth-migrations';
|
|
53
|
-
import { AuthFlowContextBuilder, AuthFlowStateMachineService } from './internal';
|
|
54
|
-
import { ClientInfo } from './interfaces/client-info.interface';
|
|
55
|
-
import { IUser } from './interfaces/entities.interface';
|
|
56
|
-
|
|
57
|
-
// ============================================================================
|
|
58
|
-
// Types
|
|
59
|
-
// ============================================================================
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Options for NAuth initialization
|
|
63
|
-
*/
|
|
64
|
-
export interface NAuthOptions {
|
|
65
|
-
/** NAuth configuration */
|
|
66
|
-
config: NAuthConfig;
|
|
67
|
-
|
|
68
|
-
/** TypeORM DataSource */
|
|
69
|
-
dataSource: DataSource;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Platform adapter (Express, Fastify, etc.)
|
|
73
|
-
* @default ExpressAdapter
|
|
74
|
-
*/
|
|
75
|
-
adapter?: NAuthAdapter;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* NAuth instance returned by NAuth.create()
|
|
80
|
-
*
|
|
81
|
-
* Contains services, middleware, and helpers for authentication.
|
|
82
|
-
*
|
|
83
|
-
* @typeParam TMiddleware - Type for middleware (framework-specific)
|
|
84
|
-
* @typeParam THelper - Type for route helpers (framework-specific)
|
|
85
|
-
*/
|
|
86
|
-
export interface NAuthInstance<TMiddleware = unknown, THelper = unknown>
|
|
87
|
-
extends Omit<NAuthServices, 'challengeService' | 'authChallengeHelperService'>, NAuthSocialProviders {
|
|
88
|
-
/** Framework-specific middleware/hooks */
|
|
89
|
-
middleware: {
|
|
90
|
-
/** Client info extraction (MUST BE FIRST) */
|
|
91
|
-
clientInfo: TMiddleware;
|
|
92
|
-
/** JWT authentication */
|
|
93
|
-
auth: TMiddleware;
|
|
94
|
-
/** CSRF protection */
|
|
95
|
-
csrf: TMiddleware;
|
|
96
|
-
/** Token delivery (response interceptor) */
|
|
97
|
-
tokenDelivery: TMiddleware;
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
/** Route helpers */
|
|
101
|
-
helpers: {
|
|
102
|
-
/** Mark route as public (bypasses CSRF) */
|
|
103
|
-
public: () => THelper;
|
|
104
|
-
/** Require authentication (with optional CSRF bypass) */
|
|
105
|
-
requireAuth: (options?: { csrf?: boolean }) => THelper;
|
|
106
|
-
/** Optional authentication marker */
|
|
107
|
-
optionalAuth: () => THelper;
|
|
108
|
-
/** Override token delivery mode */
|
|
109
|
-
tokenDelivery: (mode: 'json' | 'cookies') => THelper;
|
|
110
|
-
/** Get current authenticated user */
|
|
111
|
-
getCurrentUser: () => IUser | undefined;
|
|
112
|
-
/** Get current session ID */
|
|
113
|
-
getCurrentSession: () => string | number | undefined;
|
|
114
|
-
/** Get client info */
|
|
115
|
-
getClientInfo: () => ClientInfo | undefined;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
/** The adapter being used */
|
|
119
|
-
adapter: NAuthAdapter;
|
|
120
|
-
|
|
121
|
-
/** Configuration */
|
|
122
|
-
config: NAuthConfig;
|
|
123
|
-
|
|
124
|
-
/** Logger instance */
|
|
125
|
-
logger: NAuthLogger;
|
|
126
|
-
|
|
127
|
-
/** CSRF service (if enabled) */
|
|
128
|
-
csrfService?: CsrfService;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// ============================================================================
|
|
132
|
-
// NAuth Bootstrap Class
|
|
133
|
-
// ============================================================================
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* NAuth Bootstrap
|
|
137
|
-
*
|
|
138
|
-
* Main entry point for initializing NAuth.
|
|
139
|
-
*/
|
|
140
|
-
export class NAuth {
|
|
141
|
-
/**
|
|
142
|
-
* Create NAuth instance
|
|
143
|
-
*
|
|
144
|
-
* @param options - Configuration options
|
|
145
|
-
* @returns Initialized NAuth instance
|
|
146
|
-
*
|
|
147
|
-
* @example
|
|
148
|
-
* ```typescript
|
|
149
|
-
* const nauth = await NAuth.create({
|
|
150
|
-
* config: authConfig,
|
|
151
|
-
* dataSource: dataSource,
|
|
152
|
-
* adapter: new ExpressAdapter(),
|
|
153
|
-
* });
|
|
154
|
-
* ```
|
|
155
|
-
*/
|
|
156
|
-
static async create(options: NAuthOptions): Promise<NAuthInstance> {
|
|
157
|
-
const { config, dataSource } = options;
|
|
158
|
-
const adapter = options.adapter || new ExpressAdapter();
|
|
159
|
-
|
|
160
|
-
const logger = new NAuthLogger(config.logger);
|
|
161
|
-
logger.log(`Initializing NAuth with ${adapter.name}...`);
|
|
162
|
-
|
|
163
|
-
// ========================================================================
|
|
164
|
-
// 0. Run database migrations (adapter-owned, auto-run, no consumer burden)
|
|
165
|
-
// ========================================================================
|
|
166
|
-
await runNAuthMigrationsOnStartup(config, dataSource, logger);
|
|
167
|
-
|
|
168
|
-
// ========================================================================
|
|
169
|
-
// 1. Initialize Repositories & Storage
|
|
170
|
-
// ========================================================================
|
|
171
|
-
const repos = getRepositories(dataSource);
|
|
172
|
-
const storage = await initStorage(config, repos.rateLimitRepository, repos.storageLockRepository, logger);
|
|
173
|
-
|
|
174
|
-
// ========================================================================
|
|
175
|
-
// 2. Initialize Services
|
|
176
|
-
// ========================================================================
|
|
177
|
-
const emailProvider = config.emailProvider;
|
|
178
|
-
const smsProvider = config.smsProvider;
|
|
179
|
-
const services: NAuthServices = initServices(config, repos, storage, logger, emailProvider, smsProvider);
|
|
180
|
-
|
|
181
|
-
// ========================================================================
|
|
182
|
-
// 3. Initialize Auth Flow State Machine
|
|
183
|
-
// ========================================================================
|
|
184
|
-
const contextBuilder = new AuthFlowContextBuilder(
|
|
185
|
-
services.trustedDeviceService,
|
|
186
|
-
services.adaptiveMFADecisionService,
|
|
187
|
-
services.clientInfoService,
|
|
188
|
-
logger,
|
|
189
|
-
);
|
|
190
|
-
const stateMachine = new AuthFlowStateMachineService(contextBuilder, logger);
|
|
191
|
-
|
|
192
|
-
if (services.authChallengeHelperService) {
|
|
193
|
-
(services.authChallengeHelperService as unknown as Record<string, unknown>).stateMachine = stateMachine;
|
|
194
|
-
(services.authChallengeHelperService as unknown as Record<string, unknown>).contextBuilder = contextBuilder;
|
|
195
|
-
} else {
|
|
196
|
-
throw new NAuthException(AuthErrorCode.INTERNAL_ERROR, 'AuthChallengeHelperService not initialized.');
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// ========================================================================
|
|
200
|
-
// 4. Register MFA & Social Providers
|
|
201
|
-
// ========================================================================
|
|
202
|
-
const socialAuthStateStore = new Map<string, { timestamp: number; provider: string }>();
|
|
203
|
-
|
|
204
|
-
if (config.mfa?.enabled && services.mfaService) {
|
|
205
|
-
await registerMFAProviders(
|
|
206
|
-
config,
|
|
207
|
-
services.mfaService,
|
|
208
|
-
repos.mfaDeviceRepository!,
|
|
209
|
-
repos.userRepository,
|
|
210
|
-
logger,
|
|
211
|
-
services.passwordService,
|
|
212
|
-
services.emailVerificationService,
|
|
213
|
-
services.phoneVerificationService,
|
|
214
|
-
services.challengeService,
|
|
215
|
-
services.auditService,
|
|
216
|
-
services.clientInfoService,
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const socialProviders: NAuthSocialProviders = await initSocialAuth(
|
|
221
|
-
config,
|
|
222
|
-
services.socialProviderRegistry,
|
|
223
|
-
services.authService,
|
|
224
|
-
services.socialAuthService,
|
|
225
|
-
services.jwtService,
|
|
226
|
-
services.sessionService,
|
|
227
|
-
services.authChallengeHelperService,
|
|
228
|
-
services.clientInfoService,
|
|
229
|
-
logger,
|
|
230
|
-
socialAuthStateStore,
|
|
231
|
-
repos.userRepository,
|
|
232
|
-
services.phoneVerificationService,
|
|
233
|
-
services.auditService,
|
|
234
|
-
services.trustedDeviceService,
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
// ========================================================================
|
|
238
|
-
// 5. Create Handlers
|
|
239
|
-
// ========================================================================
|
|
240
|
-
const clientInfoHandler = new ClientInfoHandler(services.clientInfoService, services.geoLocationService, logger);
|
|
241
|
-
|
|
242
|
-
const authHandler = new AuthHandler(
|
|
243
|
-
services.jwtService,
|
|
244
|
-
services.sessionService,
|
|
245
|
-
repos.userRepository,
|
|
246
|
-
config,
|
|
247
|
-
logger,
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
const tokenDeliveryHandler = new TokenDeliveryHandler(config, logger);
|
|
251
|
-
|
|
252
|
-
// CSRF service (only for cookies/hybrid delivery)
|
|
253
|
-
const csrfService =
|
|
254
|
-
config.tokenDelivery?.method === 'cookies' || config.tokenDelivery?.method === 'hybrid'
|
|
255
|
-
? new CsrfService(config)
|
|
256
|
-
: undefined;
|
|
257
|
-
|
|
258
|
-
const csrfHandler = csrfService ? new CsrfHandler(csrfService, config, logger) : null;
|
|
259
|
-
|
|
260
|
-
// ========================================================================
|
|
261
|
-
// 6. Register Middleware with Adapter
|
|
262
|
-
// ========================================================================
|
|
263
|
-
const middleware = {
|
|
264
|
-
// ClientInfo MUST be first - initializes context
|
|
265
|
-
clientInfo: adapter.registerMiddleware('clientInfo', clientInfoHandler.handle.bind(clientInfoHandler), {
|
|
266
|
-
initializesContext: true,
|
|
267
|
-
}),
|
|
268
|
-
|
|
269
|
-
// Auth handler
|
|
270
|
-
auth: adapter.registerMiddleware('auth', authHandler.handle.bind(authHandler)),
|
|
271
|
-
|
|
272
|
-
// CSRF handler (no-op if disabled)
|
|
273
|
-
csrf: csrfHandler
|
|
274
|
-
? adapter.registerMiddleware('csrf', csrfHandler.handle.bind(csrfHandler))
|
|
275
|
-
: adapter.registerMiddleware('noop', async (_req: NAuthRequest, _res: NAuthResponse, next: () => void) => {
|
|
276
|
-
await next();
|
|
277
|
-
}),
|
|
278
|
-
|
|
279
|
-
// Token delivery (response interceptor)
|
|
280
|
-
tokenDelivery: adapter.registerResponseInterceptor(
|
|
281
|
-
tokenDeliveryHandler.handleResponse.bind(tokenDeliveryHandler),
|
|
282
|
-
),
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
// ========================================================================
|
|
286
|
-
// 7. Create Helpers
|
|
287
|
-
// ========================================================================
|
|
288
|
-
const helpers = {
|
|
289
|
-
/**
|
|
290
|
-
* Mark route as public - bypasses CSRF validation
|
|
291
|
-
*/
|
|
292
|
-
public: () =>
|
|
293
|
-
adapter.registerMiddleware('public', (req: NAuthRequest, _res: NAuthResponse, next: () => void) => {
|
|
294
|
-
req.attributes.nauthPublic = true;
|
|
295
|
-
return next();
|
|
296
|
-
}),
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Require authentication
|
|
300
|
-
*
|
|
301
|
-
* @param options.csrf - Set to false to bypass CSRF check (e.g., for logout)
|
|
302
|
-
*/
|
|
303
|
-
requireAuth: (options?: { csrf?: boolean }) =>
|
|
304
|
-
adapter.registerMiddleware('requireAuth', (req: NAuthRequest, res: NAuthResponse, next: () => void) => {
|
|
305
|
-
// Enforce deferred CSRF check (unless disabled)
|
|
306
|
-
if (options?.csrf !== false && req.attributes.nauthCsrfError) {
|
|
307
|
-
throw req.attributes.nauthCsrfError;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Enforce authentication
|
|
311
|
-
if (!req.attributes.user) {
|
|
312
|
-
res.status(401).json({
|
|
313
|
-
statusCode: 401,
|
|
314
|
-
error: 'Unauthorized',
|
|
315
|
-
message: 'Authentication required',
|
|
316
|
-
code: 'AUTH_REQUIRED',
|
|
317
|
-
});
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return next();
|
|
322
|
-
}),
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Optional authentication marker
|
|
326
|
-
*
|
|
327
|
-
* Auth middleware already does optional auth by default.
|
|
328
|
-
* This helper is a semantic marker for documentation purposes.
|
|
329
|
-
*/
|
|
330
|
-
optionalAuth: () =>
|
|
331
|
-
adapter.registerMiddleware('optionalAuth', (_req: NAuthRequest, _res: NAuthResponse, next: () => void) => {
|
|
332
|
-
return next();
|
|
333
|
-
}),
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Override token delivery mode for this route
|
|
337
|
-
*/
|
|
338
|
-
tokenDelivery: (mode: 'json' | 'cookies') =>
|
|
339
|
-
adapter.registerMiddleware(
|
|
340
|
-
'tokenDeliveryConfig',
|
|
341
|
-
(req: NAuthRequest, _res: NAuthResponse, next: () => void) => {
|
|
342
|
-
req.attributes.nauthTokenDelivery = mode;
|
|
343
|
-
return next();
|
|
344
|
-
},
|
|
345
|
-
),
|
|
346
|
-
|
|
347
|
-
// Context helpers (read from ContextStorage)
|
|
348
|
-
getCurrentUser: () => ContextStorage.get<IUser>('CURRENT_USER'),
|
|
349
|
-
getCurrentSession: () => ContextStorage.get<string | number>('CURRENT_SESSION'),
|
|
350
|
-
getClientInfo: () => ContextStorage.get<ClientInfo>('CLIENT_INFO'),
|
|
351
|
-
};
|
|
352
|
-
|
|
353
|
-
// ========================================================================
|
|
354
|
-
// 8. Build and Return Instance
|
|
355
|
-
// ========================================================================
|
|
356
|
-
|
|
357
|
-
// Exclude internal services from public API
|
|
358
|
-
const { challengeService, authChallengeHelperService, ...publicServices } = services;
|
|
359
|
-
|
|
360
|
-
logger.log(`NAuth initialized successfully with ${adapter.name}`);
|
|
361
|
-
|
|
362
|
-
return {
|
|
363
|
-
...publicServices,
|
|
364
|
-
...socialProviders,
|
|
365
|
-
middleware,
|
|
366
|
-
helpers,
|
|
367
|
-
adapter,
|
|
368
|
-
config,
|
|
369
|
-
logger,
|
|
370
|
-
socialAuthService: services.socialAuthService,
|
|
371
|
-
csrfService,
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
}
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
import { IsEnum, IsUUID, IsObject } from 'class-validator';
|
|
2
|
-
import { Transform } from 'class-transformer';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Authentication Challenge Types
|
|
6
|
-
*
|
|
7
|
-
* Represents different challenges that must be completed before
|
|
8
|
-
* a user can gain full access to the system. This is similar to
|
|
9
|
-
* AWS Cognito's challenge system.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```typescript
|
|
13
|
-
* // After login, check for challenges
|
|
14
|
-
* const result = await authService.login(credentials);
|
|
15
|
-
* if (result.challengeName) {
|
|
16
|
-
* // User must complete challenge before accessing system
|
|
17
|
-
* console.log('Challenge required:', result.challengeName);
|
|
18
|
-
* }
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
export enum AuthChallenge {
|
|
22
|
-
/**
|
|
23
|
-
* Email verification required
|
|
24
|
-
* User must verify their email address before proceeding
|
|
25
|
-
*/
|
|
26
|
-
VERIFY_EMAIL = 'VERIFY_EMAIL',
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Phone verification required
|
|
30
|
-
* User must verify their phone number before proceeding
|
|
31
|
-
*/
|
|
32
|
-
VERIFY_PHONE = 'VERIFY_PHONE',
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Multi-factor authentication required
|
|
36
|
-
* User must complete MFA verification (TOTP, SMS, etc.)
|
|
37
|
-
* This challenge is used when user already has MFA enabled and needs to verify
|
|
38
|
-
*/
|
|
39
|
-
MFA_REQUIRED = 'MFA_REQUIRED',
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* MFA setup required
|
|
43
|
-
* User must set up multi-factor authentication before being allowed to login.
|
|
44
|
-
* This occurs when enforcement is 'REQUIRED' and grace period has expired or is disabled.
|
|
45
|
-
*/
|
|
46
|
-
MFA_SETUP_REQUIRED = 'MFA_SETUP_REQUIRED',
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Password change required
|
|
50
|
-
* User must change their password before proceeding
|
|
51
|
-
* (e.g., admin-forced password reset, expired password)
|
|
52
|
-
*/
|
|
53
|
-
FORCE_CHANGE_PASSWORD = 'FORCE_CHANGE_PASSWORD',
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Challenge Response DTO
|
|
58
|
-
*
|
|
59
|
-
* Used when a user's authentication is incomplete due to pending challenges.
|
|
60
|
-
* Contains minimal information about the user and what challenges they must complete.
|
|
61
|
-
*
|
|
62
|
-
* Note: This is primarily a response DTO, but validation is included for completeness.
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* ```typescript
|
|
66
|
-
* // Login response with challenge
|
|
67
|
-
* {
|
|
68
|
-
* challengeName: 'VERIFY_EMAIL',
|
|
69
|
-
* session: 'a21b654c-2746-4168-acee-c175083a65cd',
|
|
70
|
-
* challengeParameters: {
|
|
71
|
-
* email: 'user@example.com',
|
|
72
|
-
* codeDeliveryDestination: 'u***@example.com'
|
|
73
|
-
* },
|
|
74
|
-
* userSub: 'a21b654c-2746-4168-acee-c175083a65cd'
|
|
75
|
-
* }
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
export class AuthChallengeResponseDTO {
|
|
79
|
-
/**
|
|
80
|
-
* The challenge that must be completed
|
|
81
|
-
*
|
|
82
|
-
* Validation:
|
|
83
|
-
* - Must be a valid AuthChallenge enum value
|
|
84
|
-
*/
|
|
85
|
-
@IsEnum(AuthChallenge, {
|
|
86
|
-
message: 'Challenge name must be a valid AuthChallenge enum value',
|
|
87
|
-
})
|
|
88
|
-
challengeName!: AuthChallenge;
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Temporary session identifier for challenge completion (UUID v4)
|
|
92
|
-
* This is NOT a full JWT token - only used for challenge verification
|
|
93
|
-
*
|
|
94
|
-
* Validation:
|
|
95
|
-
* - Must be a valid UUID v4 format
|
|
96
|
-
* - Generated using randomUUID() in challenge service
|
|
97
|
-
*
|
|
98
|
-
* @example "a21b654c-2746-4168-acee-c175083a65cd"
|
|
99
|
-
*/
|
|
100
|
-
@IsUUID('4', { message: 'Session token must be a valid UUID v4 format' })
|
|
101
|
-
@Transform(({ value }) => {
|
|
102
|
-
if (typeof value === 'string') {
|
|
103
|
-
return value.trim().toLowerCase();
|
|
104
|
-
}
|
|
105
|
-
return value;
|
|
106
|
-
})
|
|
107
|
-
session!: string;
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Challenge-specific parameters
|
|
111
|
-
* Contains information needed to complete the challenge
|
|
112
|
-
*
|
|
113
|
-
* Validation:
|
|
114
|
-
* - Must be an object
|
|
115
|
-
*
|
|
116
|
-
* @example
|
|
117
|
-
* ```typescript
|
|
118
|
-
* // For VERIFY_EMAIL
|
|
119
|
-
* {
|
|
120
|
-
* email: 'user@example.com',
|
|
121
|
-
* codeDeliveryDestination: 'u***@example.com'
|
|
122
|
-
* }
|
|
123
|
-
*
|
|
124
|
-
* // For VERIFY_PHONE
|
|
125
|
-
* {
|
|
126
|
-
* phone: '+1234567890',
|
|
127
|
-
* codeDeliveryDestination: '***-***-7890'
|
|
128
|
-
* }
|
|
129
|
-
* ```
|
|
130
|
-
*/
|
|
131
|
-
@IsObject({ message: 'Challenge parameters must be an object' })
|
|
132
|
-
challengeParameters!: Record<string, unknown>;
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* User's unique identifier (UUID v4)
|
|
136
|
-
* Provided so the client knows which user is completing challenges
|
|
137
|
-
*
|
|
138
|
-
* Validation:
|
|
139
|
-
* - Must be a valid UUID v4 format
|
|
140
|
-
* - Matches DB constraint: char(36) or uuid
|
|
141
|
-
*
|
|
142
|
-
* @example "a21b654c-2746-4168-acee-c175083a65cd"
|
|
143
|
-
*/
|
|
144
|
-
@IsUUID('4', { message: 'User sub must be a valid UUID v4 format' })
|
|
145
|
-
@Transform(({ value }) => {
|
|
146
|
-
if (typeof value === 'string') {
|
|
147
|
-
return value.trim().toLowerCase();
|
|
148
|
-
}
|
|
149
|
-
return value;
|
|
150
|
-
})
|
|
151
|
-
userSub!: string;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Challenge Completion Request DTO
|
|
156
|
-
*
|
|
157
|
-
* Used to submit a response to an authentication challenge.
|
|
158
|
-
*
|
|
159
|
-
* Note: This is a legacy DTO. The codebase now uses RespondChallengeDTO for the unified API.
|
|
160
|
-
* This DTO is kept for backwards compatibility.
|
|
161
|
-
*
|
|
162
|
-
* Security:
|
|
163
|
-
* - Session token validated as UUID v4 format
|
|
164
|
-
* - Challenge name validated against enum
|
|
165
|
-
* - Challenge responses validated as object
|
|
166
|
-
*
|
|
167
|
-
* @example
|
|
168
|
-
* ```typescript
|
|
169
|
-
* // Verify email challenge
|
|
170
|
-
* const request: ChallengeResponseRequestDTO = {
|
|
171
|
-
* session: 'a21b654c-2746-4168-acee-c175083a65cd',
|
|
172
|
-
* challengeName: 'VERIFY_EMAIL',
|
|
173
|
-
* challengeResponses: {
|
|
174
|
-
* code: '123456'
|
|
175
|
-
* }
|
|
176
|
-
* };
|
|
177
|
-
* ```
|
|
178
|
-
*/
|
|
179
|
-
export class ChallengeResponseRequestDTO {
|
|
180
|
-
/**
|
|
181
|
-
* Temporary session from initial auth response (UUID v4)
|
|
182
|
-
*
|
|
183
|
-
* Validation:
|
|
184
|
-
* - Must be a valid UUID v4 format
|
|
185
|
-
* - Generated using randomUUID() in challenge service
|
|
186
|
-
*
|
|
187
|
-
* Sanitization:
|
|
188
|
-
* - Trimmed
|
|
189
|
-
* - Lowercased for consistency
|
|
190
|
-
*
|
|
191
|
-
* @example "a21b654c-2746-4168-acee-c175083a65cd"
|
|
192
|
-
*/
|
|
193
|
-
@IsUUID('4', { message: 'Session token must be a valid UUID v4 format' })
|
|
194
|
-
@Transform(({ value }) => {
|
|
195
|
-
if (typeof value === 'string') {
|
|
196
|
-
return value.trim().toLowerCase();
|
|
197
|
-
}
|
|
198
|
-
return value;
|
|
199
|
-
})
|
|
200
|
-
session!: string;
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* The challenge being responded to
|
|
204
|
-
*
|
|
205
|
-
* Validation:
|
|
206
|
-
* - Must be a valid AuthChallenge enum value
|
|
207
|
-
*/
|
|
208
|
-
@IsEnum(AuthChallenge, {
|
|
209
|
-
message: 'Challenge name must be a valid AuthChallenge enum value',
|
|
210
|
-
})
|
|
211
|
-
challengeName!: AuthChallenge;
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Challenge-specific responses
|
|
215
|
-
*
|
|
216
|
-
* Validation:
|
|
217
|
-
* - Must be an object
|
|
218
|
-
* - Structure validated in service layer based on challenge type
|
|
219
|
-
*
|
|
220
|
-
* @example
|
|
221
|
-
* ```typescript
|
|
222
|
-
* // For VERIFY_EMAIL or VERIFY_PHONE
|
|
223
|
-
* { code: '123456' }
|
|
224
|
-
*
|
|
225
|
-
* // For FORCE_CHANGE_PASSWORD
|
|
226
|
-
* { newPassword: 'NewSecure123!' }
|
|
227
|
-
* ```
|
|
228
|
-
*/
|
|
229
|
-
@IsObject({ message: 'Challenge responses must be an object' })
|
|
230
|
-
challengeResponses!: Record<string, unknown>;
|
|
231
|
-
}
|