@lenne.tech/nest-server 11.6.1 → 11.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.env.js +141 -0
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/decorators/graphql-populate.decorator.d.ts +2 -2
- package/dist/core/common/decorators/restricted.decorator.d.ts +1 -0
- package/dist/core/common/decorators/restricted.decorator.js +1 -1
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/helpers/input.helper.d.ts +1 -0
- package/dist/core/common/helpers/input.helper.js +1 -1
- package/dist/core/common/helpers/input.helper.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +50 -0
- package/dist/core/modules/auth/auth-guard-strategy.enum.d.ts +1 -0
- package/dist/core/modules/auth/auth-guard-strategy.enum.js +1 -0
- package/dist/core/modules/auth/auth-guard-strategy.enum.js.map +1 -1
- package/dist/core/modules/auth/guards/auth.guard.js +11 -5
- package/dist/core/modules/auth/guards/auth.guard.js.map +1 -1
- package/dist/core/modules/auth/tokens.decorator.d.ts +1 -1
- package/dist/core/modules/better-auth/better-auth-auth.model.d.ts +9 -0
- package/dist/core/modules/better-auth/better-auth-auth.model.js +63 -0
- package/dist/core/modules/better-auth/better-auth-auth.model.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth-models.d.ts +44 -0
- package/dist/core/modules/better-auth/better-auth-models.js +185 -0
- package/dist/core/modules/better-auth/better-auth-models.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.d.ts +12 -0
- package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js +70 -0
- package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth-rate-limiter.service.d.ts +32 -0
- package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js +173 -0
- package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth-user.mapper.d.ts +43 -0
- package/dist/core/modules/better-auth/better-auth-user.mapper.js +159 -0
- package/dist/core/modules/better-auth/better-auth-user.mapper.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.config.d.ts +9 -0
- package/dist/core/modules/better-auth/better-auth.config.js +251 -0
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.middleware.d.ts +20 -0
- package/dist/core/modules/better-auth/better-auth.middleware.js +79 -0
- package/dist/core/modules/better-auth/better-auth.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.module.d.ts +30 -0
- package/dist/core/modules/better-auth/better-auth.module.js +265 -0
- package/dist/core/modules/better-auth/better-auth.module.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.resolver.d.ts +49 -0
- package/dist/core/modules/better-auth/better-auth.resolver.js +539 -0
- package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.service.d.ts +38 -0
- package/dist/core/modules/better-auth/better-auth.service.js +151 -0
- package/dist/core/modules/better-auth/better-auth.service.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.types.d.ts +38 -0
- package/dist/core/modules/better-auth/better-auth.types.js +15 -0
- package/dist/core/modules/better-auth/better-auth.types.js.map +1 -0
- package/dist/core/modules/better-auth/index.d.ts +11 -0
- package/dist/core/modules/better-auth/index.js +28 -0
- package/dist/core/modules/better-auth/index.js.map +1 -0
- package/dist/core/modules/user/core-user.model.d.ts +2 -0
- package/dist/core/modules/user/core-user.model.js +21 -0
- package/dist/core/modules/user/core-user.model.js.map +1 -1
- package/dist/core.module.js +7 -0
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +9 -1
- package/src/config.env.ts +148 -1
- package/src/core/common/decorators/restricted.decorator.ts +2 -2
- package/src/core/common/helpers/input.helper.ts +2 -2
- package/src/core/common/interfaces/server-options.interface.ts +344 -20
- package/src/core/modules/auth/auth-guard-strategy.enum.ts +1 -0
- package/src/core/modules/auth/guards/auth.guard.ts +20 -6
- package/src/core/modules/better-auth/README.md +1096 -0
- package/src/core/modules/better-auth/better-auth-auth.model.ts +69 -0
- package/src/core/modules/better-auth/better-auth-models.ts +143 -0
- package/src/core/modules/better-auth/better-auth-rate-limit.middleware.ts +113 -0
- package/src/core/modules/better-auth/better-auth-rate-limiter.service.ts +326 -0
- package/src/core/modules/better-auth/better-auth-user.mapper.ts +269 -0
- package/src/core/modules/better-auth/better-auth.config.ts +483 -0
- package/src/core/modules/better-auth/better-auth.middleware.ts +111 -0
- package/src/core/modules/better-auth/better-auth.module.ts +433 -0
- package/src/core/modules/better-auth/better-auth.resolver.ts +678 -0
- package/src/core/modules/better-auth/better-auth.service.ts +323 -0
- package/src/core/modules/better-auth/better-auth.types.ts +75 -0
- package/src/core/modules/better-auth/index.ts +25 -0
- package/src/core/modules/user/core-user.model.ts +29 -0
- package/src/core.module.ts +12 -0
- package/src/index.ts +6 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { Inject, Injectable, Logger, Optional } from '@nestjs/common';
|
|
2
|
+
import { Request } from 'express';
|
|
3
|
+
|
|
4
|
+
import { IBetterAuth } from '../../common/interfaces/server-options.interface';
|
|
5
|
+
import { ConfigService } from '../../common/services/config.service';
|
|
6
|
+
import { BetterAuthSessionUser } from './better-auth-user.mapper';
|
|
7
|
+
import { BetterAuthInstance } from './better-auth.config';
|
|
8
|
+
import { BETTER_AUTH_INSTANCE } from './better-auth.module';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Result of a session validation
|
|
12
|
+
*/
|
|
13
|
+
export interface SessionResult {
|
|
14
|
+
session: null | {
|
|
15
|
+
[key: string]: any;
|
|
16
|
+
expiresAt: Date;
|
|
17
|
+
id: string;
|
|
18
|
+
userId: string;
|
|
19
|
+
};
|
|
20
|
+
user: BetterAuthSessionUser | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* BetterAuthService provides a NestJS-friendly wrapper around the better-auth instance.
|
|
25
|
+
*
|
|
26
|
+
* This service:
|
|
27
|
+
* - Provides access to the better-auth API
|
|
28
|
+
* - Offers typed methods for common auth operations
|
|
29
|
+
* - Handles the case when better-auth is disabled gracefully
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* @Injectable()
|
|
34
|
+
* export class MyService {
|
|
35
|
+
* constructor(private readonly betterAuthService: BetterAuthService) {}
|
|
36
|
+
*
|
|
37
|
+
* async doSomething() {
|
|
38
|
+
* if (this.betterAuthService.isEnabled()) {
|
|
39
|
+
* const api = this.betterAuthService.getApi();
|
|
40
|
+
* // Use better-auth API
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
@Injectable()
|
|
47
|
+
export class BetterAuthService {
|
|
48
|
+
private readonly logger = new Logger(BetterAuthService.name);
|
|
49
|
+
private readonly config: IBetterAuth;
|
|
50
|
+
|
|
51
|
+
constructor(
|
|
52
|
+
@Optional() @Inject(BETTER_AUTH_INSTANCE) private readonly authInstance: BetterAuthInstance | null,
|
|
53
|
+
@Optional() private readonly configService?: ConfigService,
|
|
54
|
+
) {
|
|
55
|
+
// Better-Auth is enabled by default (zero-config) - only disabled if explicitly set to false
|
|
56
|
+
this.config = this.configService?.get<IBetterAuth>('betterAuth') || {};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Checks if better-auth is enabled and initialized
|
|
61
|
+
* Returns true only if:
|
|
62
|
+
* 1. The better-auth instance was successfully created (not null/undefined)
|
|
63
|
+
* 2. Better-Auth was not explicitly disabled (enabled !== false)
|
|
64
|
+
*
|
|
65
|
+
* Better-Auth is enabled by default unless explicitly set to enabled: false
|
|
66
|
+
*/
|
|
67
|
+
isEnabled(): boolean {
|
|
68
|
+
// First check: authInstance must exist (not null or undefined)
|
|
69
|
+
if (!this.authInstance) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
// Second check: must not be explicitly disabled
|
|
73
|
+
return this.config?.enabled !== false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Gets the better-auth instance
|
|
78
|
+
* Returns null if better-auth is disabled
|
|
79
|
+
*/
|
|
80
|
+
getInstance(): BetterAuthInstance | null {
|
|
81
|
+
return this.authInstance ?? null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Gets the better-auth API for direct access to endpoints
|
|
86
|
+
* Returns null if better-auth is disabled
|
|
87
|
+
*/
|
|
88
|
+
getApi(): BetterAuthInstance['api'] | null {
|
|
89
|
+
return this.authInstance?.api || null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Gets the current better-auth configuration
|
|
94
|
+
*/
|
|
95
|
+
getConfig(): IBetterAuth {
|
|
96
|
+
return this.config;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Checks if JWT plugin is enabled.
|
|
101
|
+
* JWT is enabled by default when the jwt config block is present,
|
|
102
|
+
* unless explicitly disabled with enabled: false.
|
|
103
|
+
*/
|
|
104
|
+
isJwtEnabled(): boolean {
|
|
105
|
+
return this.isEnabled() && !!this.config.jwt && this.config.jwt.enabled !== false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Checks if 2FA is enabled.
|
|
110
|
+
* 2FA is enabled by default when the twoFactor config block is present,
|
|
111
|
+
* unless explicitly disabled with enabled: false.
|
|
112
|
+
*/
|
|
113
|
+
isTwoFactorEnabled(): boolean {
|
|
114
|
+
return this.isEnabled() && !!this.config.twoFactor && this.config.twoFactor.enabled !== false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Checks if Passkey/WebAuthn is enabled.
|
|
119
|
+
* Passkey is enabled by default when the passkey config block is present,
|
|
120
|
+
* unless explicitly disabled with enabled: false.
|
|
121
|
+
*/
|
|
122
|
+
isPasskeyEnabled(): boolean {
|
|
123
|
+
return this.isEnabled() && !!this.config.passkey && this.config.passkey.enabled !== false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Checks if legacy password handling is enabled.
|
|
128
|
+
* Legacy password is enabled by default when the legacyPassword config block is present,
|
|
129
|
+
* unless explicitly disabled with enabled: false.
|
|
130
|
+
*/
|
|
131
|
+
isLegacyPasswordEnabled(): boolean {
|
|
132
|
+
return this.isEnabled() && !!this.config.legacyPassword && this.config.legacyPassword.enabled !== false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Gets the list of enabled social providers
|
|
137
|
+
* Dynamically iterates over all configured providers.
|
|
138
|
+
*
|
|
139
|
+
* A provider is considered enabled if:
|
|
140
|
+
* - It has clientId and clientSecret configured
|
|
141
|
+
* - It is NOT explicitly disabled (enabled !== false)
|
|
142
|
+
*
|
|
143
|
+
* This follows the same "enabled by default" pattern as Better-Auth itself.
|
|
144
|
+
*/
|
|
145
|
+
getEnabledSocialProviders(): string[] {
|
|
146
|
+
if (!this.isEnabled()) {
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const providers: string[] = [];
|
|
151
|
+
|
|
152
|
+
// Dynamically iterate over all configured social providers
|
|
153
|
+
if (this.config.socialProviders) {
|
|
154
|
+
for (const [name, provider] of Object.entries(this.config.socialProviders)) {
|
|
155
|
+
// Provider is enabled if: has credentials AND not explicitly disabled
|
|
156
|
+
if (provider?.clientId && provider?.clientSecret && provider?.enabled !== false) {
|
|
157
|
+
providers.push(name);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return providers;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Gets the base path for better-auth endpoints
|
|
167
|
+
*/
|
|
168
|
+
getBasePath(): string {
|
|
169
|
+
return this.config.basePath || '/iam';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Gets the base URL for better-auth
|
|
174
|
+
*/
|
|
175
|
+
getBaseUrl(): string {
|
|
176
|
+
return this.config.baseUrl || 'http://localhost:3000';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ===================================================================================================================
|
|
180
|
+
// Session Management Methods
|
|
181
|
+
// ===================================================================================================================
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Gets the current session from request headers
|
|
185
|
+
*
|
|
186
|
+
* @param req - Express request object or headers object
|
|
187
|
+
* @returns Session and user data, or null values if no valid session
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* const { session, user } = await betterAuthService.getSession(req);
|
|
192
|
+
* if (session) {
|
|
193
|
+
* console.log('User:', user.email);
|
|
194
|
+
* console.log('Session expires:', session.expiresAt);
|
|
195
|
+
* }
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
async getSession(req: Request | { headers: Record<string, string | string[] | undefined> }): Promise<SessionResult> {
|
|
199
|
+
if (!this.isEnabled()) {
|
|
200
|
+
return { session: null, user: null };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const api = this.getApi();
|
|
204
|
+
if (!api) {
|
|
205
|
+
return { session: null, user: null };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
// Convert headers to the format Better-Auth expects
|
|
210
|
+
const headers = new Headers();
|
|
211
|
+
const reqHeaders = 'headers' in req ? req.headers : {};
|
|
212
|
+
|
|
213
|
+
for (const [key, value] of Object.entries(reqHeaders)) {
|
|
214
|
+
if (typeof value === 'string') {
|
|
215
|
+
headers.set(key, value);
|
|
216
|
+
} else if (Array.isArray(value)) {
|
|
217
|
+
headers.set(key, value.join(', '));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const response = await api.getSession({ headers });
|
|
222
|
+
|
|
223
|
+
if (response && typeof response === 'object' && 'user' in response) {
|
|
224
|
+
return response as SessionResult;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return { session: null, user: null };
|
|
228
|
+
} catch (error) {
|
|
229
|
+
this.logger.debug(`getSession error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
230
|
+
return { session: null, user: null };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Revokes a specific session (logout for that session)
|
|
236
|
+
*
|
|
237
|
+
* This should be called when a user wants to log out from a specific device/session.
|
|
238
|
+
* The session token is typically stored in a cookie or sent as a bearer token.
|
|
239
|
+
*
|
|
240
|
+
* @param sessionToken - The session token to revoke
|
|
241
|
+
* @returns true if session was revoked, false otherwise
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```typescript
|
|
245
|
+
* // Get session token from cookie or header
|
|
246
|
+
* const sessionToken = req.cookies['better-auth.session_token'];
|
|
247
|
+
* const success = await betterAuthService.revokeSession(sessionToken);
|
|
248
|
+
* if (success) {
|
|
249
|
+
* res.clearCookie('better-auth.session_token');
|
|
250
|
+
* }
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
async revokeSession(sessionToken: string): Promise<boolean> {
|
|
254
|
+
if (!this.isEnabled() || !sessionToken) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const api = this.getApi();
|
|
259
|
+
if (!api) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
// Create headers with the session token
|
|
265
|
+
const headers = new Headers();
|
|
266
|
+
headers.set('Authorization', `Bearer ${sessionToken}`);
|
|
267
|
+
|
|
268
|
+
// Call Better-Auth's signOut endpoint
|
|
269
|
+
await api.signOut({ headers });
|
|
270
|
+
|
|
271
|
+
this.logger.debug('Session revoked successfully');
|
|
272
|
+
return true;
|
|
273
|
+
} catch (error) {
|
|
274
|
+
this.logger.debug(`revokeSession error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Checks if a session is close to expiring
|
|
281
|
+
*
|
|
282
|
+
* @param session - The session object from getSession()
|
|
283
|
+
* @param thresholdMinutes - Minutes before expiry to consider "close to expiring" (default: 5)
|
|
284
|
+
* @returns true if session expires within the threshold
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```typescript
|
|
288
|
+
* const { session } = await betterAuthService.getSession(req);
|
|
289
|
+
* if (session && betterAuthService.isSessionExpiringSoon(session)) {
|
|
290
|
+
* // Prompt user to refresh their session
|
|
291
|
+
* }
|
|
292
|
+
* ```
|
|
293
|
+
*/
|
|
294
|
+
isSessionExpiringSoon(session: SessionResult['session'], thresholdMinutes: number = 5): boolean {
|
|
295
|
+
if (!session?.expiresAt) {
|
|
296
|
+
return true; // No session or no expiry = treat as expiring
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const expiresAt = new Date(session.expiresAt);
|
|
300
|
+
const now = new Date();
|
|
301
|
+
const thresholdMs = thresholdMinutes * 60 * 1000;
|
|
302
|
+
|
|
303
|
+
return expiresAt.getTime() - now.getTime() < thresholdMs;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Gets the remaining session time in seconds
|
|
308
|
+
*
|
|
309
|
+
* @param session - The session object from getSession()
|
|
310
|
+
* @returns Seconds until session expires, or 0 if expired/invalid
|
|
311
|
+
*/
|
|
312
|
+
getSessionTimeRemaining(session: SessionResult['session']): number {
|
|
313
|
+
if (!session?.expiresAt) {
|
|
314
|
+
return 0;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const expiresAt = new Date(session.expiresAt);
|
|
318
|
+
const now = new Date();
|
|
319
|
+
const remaining = Math.max(0, Math.floor((expiresAt.getTime() - now.getTime()) / 1000));
|
|
320
|
+
|
|
321
|
+
return remaining;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for Better-Auth API responses
|
|
3
|
+
*
|
|
4
|
+
* These types provide type safety for interactions with the Better-Auth API
|
|
5
|
+
* and reduce the need for `as any` casts throughout the codebase.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { BetterAuthSessionUser } from './better-auth-user.mapper';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Better-Auth 2FA verification response
|
|
12
|
+
*/
|
|
13
|
+
export interface BetterAuth2FAResponse {
|
|
14
|
+
session?: BetterAuthSessionResponse['session'];
|
|
15
|
+
token?: string;
|
|
16
|
+
user?: BetterAuthSessionUser;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Better-Auth session response from getSession API
|
|
21
|
+
*/
|
|
22
|
+
export interface BetterAuthSessionResponse {
|
|
23
|
+
session: {
|
|
24
|
+
createdAt: Date;
|
|
25
|
+
expiresAt: Date;
|
|
26
|
+
id: string;
|
|
27
|
+
token: string;
|
|
28
|
+
updatedAt: Date;
|
|
29
|
+
userId: string;
|
|
30
|
+
};
|
|
31
|
+
user: BetterAuthSessionUser;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Better-Auth sign-in response
|
|
36
|
+
*/
|
|
37
|
+
export interface BetterAuthSignInResponse {
|
|
38
|
+
session?: BetterAuthSessionResponse['session'];
|
|
39
|
+
token?: string;
|
|
40
|
+
twoFactorRedirect?: boolean;
|
|
41
|
+
user?: BetterAuthSessionUser;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Better-Auth sign-up response
|
|
46
|
+
*/
|
|
47
|
+
export interface BetterAuthSignUpResponse {
|
|
48
|
+
session?: BetterAuthSessionResponse['session'];
|
|
49
|
+
user?: BetterAuthSessionUser;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Type guard to check if response has session
|
|
54
|
+
*/
|
|
55
|
+
export function hasSession<T extends { session?: BetterAuthSessionResponse['session'] }>(
|
|
56
|
+
response: T,
|
|
57
|
+
): response is T & { session: BetterAuthSessionResponse['session'] } {
|
|
58
|
+
return response?.session !== undefined && response.session !== null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Type guard to check if response has user
|
|
63
|
+
*/
|
|
64
|
+
export function hasUser<T extends { user?: BetterAuthSessionUser }>(
|
|
65
|
+
response: T,
|
|
66
|
+
): response is T & { user: BetterAuthSessionUser } {
|
|
67
|
+
return response?.user !== undefined && response.user !== null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Type guard to check if response requires 2FA
|
|
72
|
+
*/
|
|
73
|
+
export function requires2FA(response: BetterAuthSignInResponse): boolean {
|
|
74
|
+
return response?.twoFactorRedirect === true;
|
|
75
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BetterAuth Module exports
|
|
3
|
+
*
|
|
4
|
+
* Provides integration with the better-auth authentication framework.
|
|
5
|
+
* This module supports:
|
|
6
|
+
* - Email/Password authentication
|
|
7
|
+
* - JWT tokens for API clients
|
|
8
|
+
* - Two-Factor Authentication (TOTP)
|
|
9
|
+
* - Passkey/WebAuthn authentication
|
|
10
|
+
* - Social Login (Google, GitHub, Apple)
|
|
11
|
+
* - Legacy password handling for migration
|
|
12
|
+
* - Rate limiting for brute-force protection
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export * from './better-auth-auth.model';
|
|
16
|
+
export * from './better-auth-models';
|
|
17
|
+
export * from './better-auth-rate-limit.middleware';
|
|
18
|
+
export * from './better-auth-rate-limiter.service';
|
|
19
|
+
export * from './better-auth-user.mapper';
|
|
20
|
+
export * from './better-auth.config';
|
|
21
|
+
export * from './better-auth.middleware';
|
|
22
|
+
export * from './better-auth.module';
|
|
23
|
+
export * from './better-auth.resolver';
|
|
24
|
+
export * from './better-auth.service';
|
|
25
|
+
export * from './better-auth.types';
|
|
@@ -227,6 +227,35 @@ export abstract class CoreUserModel extends CorePersistenceModel {
|
|
|
227
227
|
})
|
|
228
228
|
verifiedAt: Date = undefined;
|
|
229
229
|
|
|
230
|
+
// ===================================================================================================================
|
|
231
|
+
// IAM Integration Fields (optional, used when Better-Auth or other IAM providers are enabled)
|
|
232
|
+
// ===================================================================================================================
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* IAM (Identity and Access Management) provider user ID
|
|
236
|
+
* Links this user to their identity in the IAM system (e.g., Better-Auth)
|
|
237
|
+
*/
|
|
238
|
+
@UnifiedField({
|
|
239
|
+
description: 'IAM provider user ID (used for Better-Auth or other IAM integration)',
|
|
240
|
+
isOptional: true,
|
|
241
|
+
mongoose: { index: true, sparse: true },
|
|
242
|
+
roles: RoleEnum.S_NO_ONE,
|
|
243
|
+
})
|
|
244
|
+
iamId: string = undefined;
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Whether 2FA is enabled for this user
|
|
248
|
+
* Managed by Better-Auth's twoFactor plugin - read-only from our perspective
|
|
249
|
+
*/
|
|
250
|
+
@UnifiedField({
|
|
251
|
+
description: 'Whether Two-Factor Authentication is enabled',
|
|
252
|
+
isOptional: true,
|
|
253
|
+
mongoose: { type: Boolean },
|
|
254
|
+
roles: RoleEnum.S_EVERYONE,
|
|
255
|
+
type: () => Boolean,
|
|
256
|
+
})
|
|
257
|
+
twoFactorEnabled: boolean = undefined;
|
|
258
|
+
|
|
230
259
|
// ===================================================================================================================
|
|
231
260
|
// Methods
|
|
232
261
|
// ===================================================================================================================
|
package/src/core.module.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { EmailService } from './core/common/services/email.service';
|
|
|
19
19
|
import { MailjetService } from './core/common/services/mailjet.service';
|
|
20
20
|
import { ModelDocService } from './core/common/services/model-doc.service';
|
|
21
21
|
import { TemplateService } from './core/common/services/template.service';
|
|
22
|
+
import { BetterAuthModule } from './core/modules/better-auth/better-auth.module';
|
|
22
23
|
import { CoreHealthCheckModule } from './core/modules/health-check/core-health-check.module';
|
|
23
24
|
|
|
24
25
|
/**
|
|
@@ -228,6 +229,17 @@ export class CoreModule implements NestModule {
|
|
|
228
229
|
imports.push(CoreHealthCheckModule);
|
|
229
230
|
}
|
|
230
231
|
|
|
232
|
+
// Add BetterAuthModule - enabled by default unless explicitly disabled
|
|
233
|
+
if (config.betterAuth?.enabled !== false) {
|
|
234
|
+
imports.push(
|
|
235
|
+
BetterAuthModule.forRoot({
|
|
236
|
+
config: config.betterAuth || {},
|
|
237
|
+
// Pass JWT secrets for backwards compatibility fallback
|
|
238
|
+
fallbackSecrets: [config.jwt?.secret, config.jwt?.refresh?.secret],
|
|
239
|
+
}),
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
231
243
|
// Set exports
|
|
232
244
|
const exports: any[] = [ConfigService, EmailService, TemplateService, MailjetService];
|
|
233
245
|
if (!process.env.VITEST) {
|
package/src/index.ts
CHANGED
|
@@ -121,6 +121,12 @@ export * from './core/modules/auth/strategies/jwt-refresh.strategy';
|
|
|
121
121
|
export * from './core/modules/auth/strategies/jwt.strategy';
|
|
122
122
|
export * from './core/modules/auth/tokens.decorator';
|
|
123
123
|
|
|
124
|
+
// =====================================================================================================================
|
|
125
|
+
// Core - Modules - BetterAuth
|
|
126
|
+
// =====================================================================================================================
|
|
127
|
+
|
|
128
|
+
export * from './core/modules/better-auth';
|
|
129
|
+
|
|
124
130
|
// =====================================================================================================================
|
|
125
131
|
// Core - Modules - File
|
|
126
132
|
// =====================================================================================================================
|