@lenne.tech/nest-server 11.9.0 → 11.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.env.js +2 -0
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/helpers/logging.helper.d.ts +6 -0
- package/dist/core/common/helpers/logging.helper.js +55 -0
- package/dist/core/common/helpers/logging.helper.js.map +1 -0
- package/dist/core/common/interfaces/server-options.interface.d.ts +37 -19
- package/dist/core/modules/auth/guards/roles.guard.js +33 -2
- package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
- package/dist/core/modules/auth/services/core-auth.service.d.ts +5 -5
- package/dist/core/modules/auth/services/core-auth.service.js +4 -4
- package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
- package/dist/core/modules/auth/tokens.decorator.d.ts +1 -1
- package/dist/core/modules/better-auth/better-auth.config.js +32 -10
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.resolver.d.ts +16 -16
- package/dist/core/modules/better-auth/better-auth.resolver.js +34 -34
- package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.types.d.ts +2 -1
- package/dist/core/modules/better-auth/better-auth.types.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.d.ts +10 -0
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js +91 -0
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-auth.model.d.ts +9 -0
- package/dist/core/modules/better-auth/{better-auth-auth.model.js → core-better-auth-auth.model.js} +17 -17
- package/dist/core/modules/better-auth/core-better-auth-auth.model.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth-migration-status.model.d.ts → core-better-auth-migration-status.model.d.ts} +1 -1
- package/dist/core/modules/better-auth/{better-auth-migration-status.model.js → core-better-auth-migration-status.model.js} +14 -14
- package/dist/core/modules/better-auth/core-better-auth-migration-status.model.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth-models.d.ts → core-better-auth-models.d.ts} +8 -8
- package/dist/core/modules/better-auth/{better-auth-models.js → core-better-auth-models.js} +61 -61
- package/dist/core/modules/better-auth/core-better-auth-models.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-rate-limit.middleware.d.ts +12 -0
- package/dist/core/modules/better-auth/{better-auth-rate-limit.middleware.js → core-better-auth-rate-limit.middleware.js} +10 -10
- package/dist/core/modules/better-auth/core-better-auth-rate-limit.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth-rate-limiter.service.d.ts → core-better-auth-rate-limiter.service.d.ts} +1 -1
- package/dist/core/modules/better-auth/{better-auth-rate-limiter.service.js → core-better-auth-rate-limiter.service.js} +8 -8
- package/dist/core/modules/better-auth/core-better-auth-rate-limiter.service.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth-user.mapper.d.ts → core-better-auth-user.mapper.d.ts} +1 -1
- package/dist/core/modules/better-auth/{better-auth-user.mapper.js → core-better-auth-user.mapper.js} +10 -9
- package/dist/core/modules/better-auth/core-better-auth-user.mapper.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-web.helper.d.ts +19 -0
- package/dist/core/modules/better-auth/core-better-auth-web.helper.js +152 -0
- package/dist/core/modules/better-auth/core-better-auth-web.helper.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth.controller.d.ts +23 -32
- package/dist/core/modules/better-auth/core-better-auth.controller.js +184 -201
- package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.middleware.d.ts +22 -0
- package/dist/core/modules/better-auth/{better-auth.middleware.js → core-better-auth.middleware.js} +45 -18
- package/dist/core/modules/better-auth/core-better-auth.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/{better-auth.module.d.ts → core-better-auth.module.d.ts} +6 -6
- package/dist/core/modules/better-auth/{better-auth.module.js → core-better-auth.module.js} +65 -60
- package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth.resolver.d.ts +19 -19
- package/dist/core/modules/better-auth/core-better-auth.resolver.js +18 -18
- package/dist/core/modules/better-auth/core-better-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/{better-auth.service.d.ts → core-better-auth.service.d.ts} +3 -2
- package/dist/core/modules/better-auth/{better-auth.service.js → core-better-auth.service.js} +75 -35
- package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -0
- package/dist/core/modules/better-auth/index.d.ts +11 -9
- package/dist/core/modules/better-auth/index.js +11 -9
- package/dist/core/modules/better-auth/index.js.map +1 -1
- package/dist/core/modules/error-code/core-error-code.controller.js.map +1 -1
- package/dist/core/modules/user/interfaces/core-user-service-options.interface.d.ts +2 -2
- package/dist/core.module.js +6 -6
- 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/server/modules/better-auth/better-auth.controller.d.ts +5 -5
- package/dist/server/modules/better-auth/better-auth.controller.js +4 -4
- package/dist/server/modules/better-auth/better-auth.controller.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.module.js +3 -3
- package/dist/server/modules/better-auth/better-auth.module.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.resolver.d.ts +17 -17
- package/dist/server/modules/better-auth/better-auth.resolver.js +18 -18
- package/dist/server/modules/better-auth/better-auth.resolver.js.map +1 -1
- package/dist/server/modules/user/user.service.d.ts +2 -2
- package/dist/server/modules/user/user.service.js +2 -2
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/test/test.helper.d.ts +1 -0
- package/dist/test/test.helper.js +5 -1
- package/dist/test/test.helper.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +5 -3
- package/src/config.env.ts +15 -0
- package/src/core/common/helpers/logging.helper.ts +134 -0
- package/src/core/common/interfaces/server-options.interface.ts +419 -234
- package/src/core/modules/auth/guards/roles.guard.ts +44 -3
- package/src/core/modules/auth/services/core-auth.service.ts +4 -4
- package/src/core/modules/better-auth/ARCHITECTURE.md +102 -0
- package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +277 -8
- package/src/core/modules/better-auth/README.md +97 -53
- package/src/core/modules/better-auth/better-auth.config.ts +66 -18
- package/src/core/modules/better-auth/better-auth.resolver.ts +32 -32
- package/src/core/modules/better-auth/better-auth.types.ts +3 -2
- package/src/core/modules/better-auth/core-better-auth-api.middleware.ts +134 -0
- package/src/core/modules/better-auth/{better-auth-auth.model.ts → core-better-auth-auth.model.ts} +6 -6
- package/src/core/modules/better-auth/{better-auth-migration-status.model.ts → core-better-auth-migration-status.model.ts} +1 -1
- package/src/core/modules/better-auth/{better-auth-models.ts → core-better-auth-models.ts} +9 -9
- package/src/core/modules/better-auth/{better-auth-rate-limit.middleware.ts → core-better-auth-rate-limit.middleware.ts} +5 -5
- package/src/core/modules/better-auth/{better-auth-rate-limiter.service.ts → core-better-auth-rate-limiter.service.ts} +2 -2
- package/src/core/modules/better-auth/{better-auth-user.mapper.ts → core-better-auth-user.mapper.ts} +4 -3
- package/src/core/modules/better-auth/core-better-auth-web.helper.ts +272 -0
- package/src/core/modules/better-auth/core-better-auth.controller.ts +386 -230
- package/src/core/modules/better-auth/{better-auth.middleware.ts → core-better-auth.middleware.ts} +57 -17
- package/src/core/modules/better-auth/{better-auth.module.ts → core-better-auth.module.ts} +77 -66
- package/src/core/modules/better-auth/core-better-auth.resolver.ts +42 -42
- package/src/core/modules/better-auth/{better-auth.service.ts → core-better-auth.service.ts} +86 -40
- package/src/core/modules/better-auth/index.ts +18 -11
- package/src/core/modules/error-code/INTEGRATION-CHECKLIST.md +4 -1
- package/src/core/modules/error-code/core-error-code.controller.ts +3 -2
- package/src/core/modules/user/interfaces/core-user-service-options.interface.ts +3 -3
- package/src/core.module.ts +12 -12
- package/src/index.ts +1 -0
- package/src/server/modules/better-auth/better-auth.controller.ts +4 -4
- package/src/server/modules/better-auth/better-auth.module.ts +1 -1
- package/src/server/modules/better-auth/better-auth.resolver.ts +31 -31
- package/src/server/modules/user/user.service.ts +2 -2
- package/src/test/test.helper.ts +13 -1
- package/dist/core/modules/better-auth/better-auth-auth.model.d.ts +0 -9
- package/dist/core/modules/better-auth/better-auth-auth.model.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-migration-status.model.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-models.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.d.ts +0 -12
- package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth-user.mapper.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth.middleware.d.ts +0 -21
- package/dist/core/modules/better-auth/better-auth.middleware.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth.module.js.map +0 -1
- package/dist/core/modules/better-auth/better-auth.service.js.map +0 -1
|
@@ -3,17 +3,17 @@ import { Request, Response } from 'express';
|
|
|
3
3
|
|
|
4
4
|
import { Roles } from '../../common/decorators/roles.decorator';
|
|
5
5
|
import { RoleEnum } from '../../common/enums/role.enum';
|
|
6
|
-
import {
|
|
6
|
+
import { CoreBetterAuthAuthModel } from './core-better-auth-auth.model';
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} from './better-auth-models';
|
|
14
|
-
import {
|
|
15
|
-
import { BetterAuthService } from './better-auth.service';
|
|
8
|
+
CoreBetterAuth2FASetupModel,
|
|
9
|
+
CoreBetterAuthFeaturesModel,
|
|
10
|
+
CoreBetterAuthPasskeyChallengeModel,
|
|
11
|
+
CoreBetterAuthPasskeyModel,
|
|
12
|
+
CoreBetterAuthSessionModel,
|
|
13
|
+
} from './core-better-auth-models';
|
|
14
|
+
import { CoreBetterAuthUserMapper } from './core-better-auth-user.mapper';
|
|
16
15
|
import { CoreBetterAuthResolver } from './core-better-auth.resolver';
|
|
16
|
+
import { CoreBetterAuthService } from './core-better-auth.service';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Default BetterAuth GraphQL Resolver
|
|
@@ -28,11 +28,11 @@ import { CoreBetterAuthResolver } from './core-better-auth.resolver';
|
|
|
28
28
|
* @example
|
|
29
29
|
* ```typescript
|
|
30
30
|
* // In your project - src/server/modules/better-auth/better-auth.resolver.ts
|
|
31
|
-
* @Resolver(() =>
|
|
31
|
+
* @Resolver(() => CoreBetterAuthAuthModel)
|
|
32
32
|
* export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
33
33
|
* constructor(
|
|
34
|
-
* betterAuthService:
|
|
35
|
-
* userMapper:
|
|
34
|
+
* betterAuthService: CoreBetterAuthService,
|
|
35
|
+
* userMapper: CoreBetterAuthUserMapper,
|
|
36
36
|
* private readonly emailService: EmailService,
|
|
37
37
|
* ) {
|
|
38
38
|
* super(betterAuthService, userMapper);
|
|
@@ -51,12 +51,12 @@ import { CoreBetterAuthResolver } from './core-better-auth.resolver';
|
|
|
51
51
|
* }
|
|
52
52
|
* ```
|
|
53
53
|
*/
|
|
54
|
-
@Resolver(() =>
|
|
54
|
+
@Resolver(() => CoreBetterAuthAuthModel)
|
|
55
55
|
@Roles(RoleEnum.ADMIN)
|
|
56
|
-
export class
|
|
56
|
+
export class DefaultBetterAuthResolver extends CoreBetterAuthResolver {
|
|
57
57
|
constructor(
|
|
58
|
-
protected override readonly betterAuthService:
|
|
59
|
-
protected override readonly userMapper:
|
|
58
|
+
protected override readonly betterAuthService: CoreBetterAuthService,
|
|
59
|
+
protected override readonly userMapper: CoreBetterAuthUserMapper,
|
|
60
60
|
) {
|
|
61
61
|
super(betterAuthService, userMapper);
|
|
62
62
|
}
|
|
@@ -65,12 +65,12 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
65
65
|
// Queries
|
|
66
66
|
// ===========================================================================
|
|
67
67
|
|
|
68
|
-
@Query(() =>
|
|
68
|
+
@Query(() => CoreBetterAuthSessionModel, {
|
|
69
69
|
description: 'Get current Better-Auth session',
|
|
70
70
|
nullable: true,
|
|
71
71
|
})
|
|
72
72
|
@Roles(RoleEnum.S_USER)
|
|
73
|
-
override async betterAuthSession(@Context() ctx: { req: Request }): Promise<
|
|
73
|
+
override async betterAuthSession(@Context() ctx: { req: Request }): Promise<CoreBetterAuthSessionModel | null> {
|
|
74
74
|
return super.betterAuthSession(ctx);
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -80,18 +80,18 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
80
80
|
return super.betterAuthEnabled();
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
@Query(() =>
|
|
83
|
+
@Query(() => CoreBetterAuthFeaturesModel, { description: 'Get enabled Better-Auth features' })
|
|
84
84
|
@Roles(RoleEnum.S_EVERYONE)
|
|
85
|
-
override betterAuthFeatures():
|
|
85
|
+
override betterAuthFeatures(): CoreBetterAuthFeaturesModel {
|
|
86
86
|
return super.betterAuthFeatures();
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
@Query(() => [
|
|
89
|
+
@Query(() => [CoreBetterAuthPasskeyModel], {
|
|
90
90
|
description: 'List passkeys for the current user',
|
|
91
91
|
nullable: true,
|
|
92
92
|
})
|
|
93
93
|
@Roles(RoleEnum.S_USER)
|
|
94
|
-
override async betterAuthListPasskeys(@Context() ctx: { req: Request }): Promise<
|
|
94
|
+
override async betterAuthListPasskeys(@Context() ctx: { req: Request }): Promise<CoreBetterAuthPasskeyModel[] | null> {
|
|
95
95
|
return super.betterAuthListPasskeys(ctx);
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -99,7 +99,7 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
99
99
|
// Authentication Mutations
|
|
100
100
|
// ===========================================================================
|
|
101
101
|
|
|
102
|
-
@Mutation(() =>
|
|
102
|
+
@Mutation(() => CoreBetterAuthAuthModel, {
|
|
103
103
|
description: 'Sign in via Better-Auth (email/password)',
|
|
104
104
|
})
|
|
105
105
|
@Roles(RoleEnum.S_EVERYONE)
|
|
@@ -107,11 +107,11 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
107
107
|
@Args('email') email: string,
|
|
108
108
|
@Args('password') password: string,
|
|
109
109
|
@Context() ctx: { req: Request; res: Response },
|
|
110
|
-
): Promise<
|
|
110
|
+
): Promise<CoreBetterAuthAuthModel> {
|
|
111
111
|
return super.betterAuthSignIn(email, password, ctx);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
@Mutation(() =>
|
|
114
|
+
@Mutation(() => CoreBetterAuthAuthModel, {
|
|
115
115
|
description: 'Sign up via Better-Auth (email/password)',
|
|
116
116
|
})
|
|
117
117
|
@Roles(RoleEnum.S_EVERYONE)
|
|
@@ -119,7 +119,7 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
119
119
|
@Args('email') email: string,
|
|
120
120
|
@Args('password') password: string,
|
|
121
121
|
@Args('name', { nullable: true }) name?: string,
|
|
122
|
-
): Promise<
|
|
122
|
+
): Promise<CoreBetterAuthAuthModel> {
|
|
123
123
|
return super.betterAuthSignUp(email, password, name);
|
|
124
124
|
}
|
|
125
125
|
|
|
@@ -133,25 +133,25 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
133
133
|
// 2FA Mutations
|
|
134
134
|
// ===========================================================================
|
|
135
135
|
|
|
136
|
-
@Mutation(() =>
|
|
136
|
+
@Mutation(() => CoreBetterAuthAuthModel, {
|
|
137
137
|
description: 'Verify 2FA code during sign-in',
|
|
138
138
|
})
|
|
139
139
|
@Roles(RoleEnum.S_EVERYONE)
|
|
140
140
|
override async betterAuthVerify2FA(
|
|
141
141
|
@Args('code') code: string,
|
|
142
142
|
@Context() ctx: { req: Request },
|
|
143
|
-
): Promise<
|
|
143
|
+
): Promise<CoreBetterAuthAuthModel> {
|
|
144
144
|
return super.betterAuthVerify2FA(code, ctx);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
@Mutation(() =>
|
|
147
|
+
@Mutation(() => CoreBetterAuth2FASetupModel, {
|
|
148
148
|
description: 'Enable 2FA for the current user',
|
|
149
149
|
})
|
|
150
150
|
@Roles(RoleEnum.S_USER)
|
|
151
151
|
override async betterAuthEnable2FA(
|
|
152
152
|
@Args('password') password: string,
|
|
153
153
|
@Context() ctx: { req: Request },
|
|
154
|
-
): Promise<
|
|
154
|
+
): Promise<CoreBetterAuth2FASetupModel> {
|
|
155
155
|
return super.betterAuthEnable2FA(password, ctx);
|
|
156
156
|
}
|
|
157
157
|
|
|
@@ -179,13 +179,13 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
179
179
|
// Passkey Mutations
|
|
180
180
|
// ===========================================================================
|
|
181
181
|
|
|
182
|
-
@Mutation(() =>
|
|
182
|
+
@Mutation(() => CoreBetterAuthPasskeyChallengeModel, {
|
|
183
183
|
description: 'Get passkey registration challenge for WebAuthn',
|
|
184
184
|
})
|
|
185
185
|
@Roles(RoleEnum.S_USER)
|
|
186
186
|
override async betterAuthGetPasskeyChallenge(
|
|
187
187
|
@Context() ctx: { req: Request },
|
|
188
|
-
): Promise<
|
|
188
|
+
): Promise<CoreBetterAuthPasskeyChallengeModel> {
|
|
189
189
|
return super.betterAuthGetPasskeyChallenge(ctx);
|
|
190
190
|
}
|
|
191
191
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* and reduce the need for `as any` casts throughout the codebase.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { BetterAuthSessionUser } from './better-auth-user.mapper';
|
|
8
|
+
import { BetterAuthSessionUser } from './core-better-auth-user.mapper';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Better-Auth 2FA verification response
|
|
@@ -68,8 +68,9 @@ export interface BetterAuthSignUpResponse {
|
|
|
68
68
|
/**
|
|
69
69
|
* Type guard to check if response has session
|
|
70
70
|
* Preserves the original type while asserting session is defined
|
|
71
|
+
* Includes optional token for Better Auth session authentication
|
|
71
72
|
*/
|
|
72
|
-
export function hasSession<T>(response: T): response is T & { session: { expiresAt: Date; id: string } } {
|
|
73
|
+
export function hasSession<T>(response: T): response is T & { session: { expiresAt: Date; id: string; token?: string } } {
|
|
73
74
|
return (
|
|
74
75
|
response !== null &&
|
|
75
76
|
typeof response === 'object' &&
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { Injectable, Logger, NestMiddleware } from '@nestjs/common';
|
|
2
|
+
import { NextFunction, Request, Response } from 'express';
|
|
3
|
+
|
|
4
|
+
import { isProduction } from '../../common/helpers/logging.helper';
|
|
5
|
+
import { extractSessionToken, sendWebResponse, toWebRequest } from './core-better-auth-web.helper';
|
|
6
|
+
import { CoreBetterAuthService } from './core-better-auth.service';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* List of paths that are handled by CoreBetterAuthController
|
|
10
|
+
* These should NOT be forwarded to Better Auth's native handler
|
|
11
|
+
*
|
|
12
|
+
* Only paths with nest-server-specific logic belong here:
|
|
13
|
+
* - sign-in/email: Legacy user migration, password normalization
|
|
14
|
+
* - sign-up/email: User linking to own DB, password sync
|
|
15
|
+
* - sign-out: Custom cookie clearing
|
|
16
|
+
* - session: Custom response format with mapped user
|
|
17
|
+
*
|
|
18
|
+
* All other paths (Passkey, 2FA, etc.) go directly to Better Auth's
|
|
19
|
+
* native handler via this middleware for maximum compatibility.
|
|
20
|
+
*/
|
|
21
|
+
const CONTROLLER_HANDLED_PATHS = [
|
|
22
|
+
'/sign-in/email',
|
|
23
|
+
'/sign-up/email',
|
|
24
|
+
'/sign-out',
|
|
25
|
+
'/session',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Middleware that forwards Better Auth API requests to the native Better Auth handler.
|
|
30
|
+
*
|
|
31
|
+
* This middleware handles ALL Better Auth plugin functionality directly:
|
|
32
|
+
* - Passkey/WebAuthn (registration, authentication, management)
|
|
33
|
+
* - Two-Factor Authentication (TOTP enable, disable, verify)
|
|
34
|
+
* - Social Login OAuth flows
|
|
35
|
+
* - Magic link authentication
|
|
36
|
+
* - Email verification
|
|
37
|
+
*
|
|
38
|
+
* The middleware:
|
|
39
|
+
* 1. Checks if the request path starts with the Better Auth base path (e.g., /iam)
|
|
40
|
+
* 2. Skips paths that need nest-server-specific logic (sign-in, sign-up, session)
|
|
41
|
+
* 3. Extracts session token and signs cookies for Better Auth compatibility
|
|
42
|
+
* 4. Converts the Express request to a Web Standard Request
|
|
43
|
+
* 5. Calls Better Auth's native handler and sends the response
|
|
44
|
+
*
|
|
45
|
+
* IMPORTANT: Cookie signing is handled here to ensure Better Auth receives
|
|
46
|
+
* properly signed session cookies for all plugin endpoints.
|
|
47
|
+
*/
|
|
48
|
+
@Injectable()
|
|
49
|
+
export class CoreBetterAuthApiMiddleware implements NestMiddleware {
|
|
50
|
+
private readonly logger = new Logger(CoreBetterAuthApiMiddleware.name);
|
|
51
|
+
private readonly isProd = isProduction();
|
|
52
|
+
|
|
53
|
+
constructor(private readonly betterAuthService: CoreBetterAuthService) {}
|
|
54
|
+
|
|
55
|
+
async use(req: Request, res: Response, next: NextFunction) {
|
|
56
|
+
// Skip if Better-Auth is not enabled
|
|
57
|
+
if (!this.betterAuthService.isEnabled()) {
|
|
58
|
+
return next();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const basePath = this.betterAuthService.getBasePath();
|
|
62
|
+
const requestPath = req.path;
|
|
63
|
+
|
|
64
|
+
// Only handle requests that start with the Better Auth base path
|
|
65
|
+
if (!requestPath.startsWith(basePath)) {
|
|
66
|
+
return next();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Get the path relative to the base path
|
|
70
|
+
const relativePath = requestPath.slice(basePath.length);
|
|
71
|
+
|
|
72
|
+
// Skip paths that are handled by CoreBetterAuthController (nest-server-specific logic)
|
|
73
|
+
if (CONTROLLER_HANDLED_PATHS.some((path) => relativePath === path || relativePath.startsWith(`${path}/`))) {
|
|
74
|
+
return next();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Get the Better Auth instance
|
|
78
|
+
const authInstance = this.betterAuthService.getInstance();
|
|
79
|
+
if (!authInstance) {
|
|
80
|
+
this.logger.warn('Better Auth instance not available');
|
|
81
|
+
return next();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!this.isProd) {
|
|
85
|
+
this.logger.debug(`Forwarding to Better Auth handler: ${req.method} ${requestPath}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
// Extract session token from cookies or Authorization header
|
|
90
|
+
const sessionToken = extractSessionToken(req, basePath);
|
|
91
|
+
|
|
92
|
+
// Get config for cookie signing
|
|
93
|
+
const config = this.betterAuthService.getConfig();
|
|
94
|
+
|
|
95
|
+
// Convert Express request to Web Standard Request with proper cookie signing
|
|
96
|
+
// This ensures Better Auth receives signed cookies for session validation
|
|
97
|
+
const webRequest = await toWebRequest(req, {
|
|
98
|
+
basePath,
|
|
99
|
+
baseUrl: this.betterAuthService.getBaseUrl(),
|
|
100
|
+
logger: this.logger,
|
|
101
|
+
secret: config.secret,
|
|
102
|
+
sessionToken,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Call Better Auth's native handler
|
|
106
|
+
const response = await authInstance.handler(webRequest);
|
|
107
|
+
|
|
108
|
+
if (!this.isProd) {
|
|
109
|
+
this.logger.debug(`Better Auth handler response: ${response.status}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Convert Web Standard Response to Express response using shared helper
|
|
113
|
+
await sendWebResponse(res, response);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
// Log error with appropriate detail level
|
|
116
|
+
if (this.isProd) {
|
|
117
|
+
this.logger.error('Better Auth handler error');
|
|
118
|
+
} else {
|
|
119
|
+
this.logger.error(`Better Auth handler error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Send error response if headers not sent
|
|
123
|
+
if (!res.headersSent) {
|
|
124
|
+
const message = this.isProd
|
|
125
|
+
? 'Authentication error'
|
|
126
|
+
: (error instanceof Error ? error.message : 'Unknown error');
|
|
127
|
+
res.status(500).json({
|
|
128
|
+
error: 'Authentication handler error',
|
|
129
|
+
message,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
package/src/core/modules/better-auth/{better-auth-auth.model.ts → core-better-auth-auth.model.ts}
RENAMED
|
@@ -2,7 +2,7 @@ import { Field, ObjectType } from '@nestjs/graphql';
|
|
|
2
2
|
|
|
3
3
|
import { Restricted } from '../../common/decorators/restricted.decorator';
|
|
4
4
|
import { RoleEnum } from '../../common/enums/role.enum';
|
|
5
|
-
import {
|
|
5
|
+
import { CoreBetterAuthSessionInfoModel, CoreBetterAuthUserModel } from './core-better-auth-models';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Better-Auth Authentication Response Model
|
|
@@ -13,7 +13,7 @@ import { BetterAuthSessionInfoModel, BetterAuthUserModel } from './better-auth-m
|
|
|
13
13
|
*/
|
|
14
14
|
@ObjectType({ description: 'Better-Auth Authentication Response' })
|
|
15
15
|
@Restricted(RoleEnum.S_EVERYONE)
|
|
16
|
-
export class
|
|
16
|
+
export class CoreBetterAuthAuthModel {
|
|
17
17
|
/**
|
|
18
18
|
* Whether the authentication was successful
|
|
19
19
|
*/
|
|
@@ -43,20 +43,20 @@ export class BetterAuthAuthModel {
|
|
|
43
43
|
/**
|
|
44
44
|
* Authenticated user
|
|
45
45
|
*/
|
|
46
|
-
@Field(() =>
|
|
46
|
+
@Field(() => CoreBetterAuthUserModel, {
|
|
47
47
|
description: 'Authenticated user',
|
|
48
48
|
nullable: true,
|
|
49
49
|
})
|
|
50
|
-
user?:
|
|
50
|
+
user?: CoreBetterAuthUserModel;
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Session information
|
|
54
54
|
*/
|
|
55
|
-
@Field(() =>
|
|
55
|
+
@Field(() => CoreBetterAuthSessionInfoModel, {
|
|
56
56
|
description: 'Session information',
|
|
57
57
|
nullable: true,
|
|
58
58
|
})
|
|
59
|
-
session?:
|
|
59
|
+
session?: CoreBetterAuthSessionInfoModel;
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Error message if authentication failed
|
|
@@ -8,7 +8,7 @@ import { Field, Int, ObjectType } from '@nestjs/graphql';
|
|
|
8
8
|
* safe to disable Legacy Auth.
|
|
9
9
|
*/
|
|
10
10
|
@ObjectType({ description: 'Migration status from Legacy Auth to Better-Auth (IAM)' })
|
|
11
|
-
export class
|
|
11
|
+
export class CoreBetterAuthMigrationStatusModel {
|
|
12
12
|
/**
|
|
13
13
|
* Total number of users in the system
|
|
14
14
|
*/
|
|
@@ -8,7 +8,7 @@ import { RoleEnum } from '../../common/enums/role.enum';
|
|
|
8
8
|
*/
|
|
9
9
|
@ObjectType({ description: 'Better-Auth User' })
|
|
10
10
|
@Restricted(RoleEnum.S_EVERYONE)
|
|
11
|
-
export class
|
|
11
|
+
export class CoreBetterAuthUserModel {
|
|
12
12
|
@Field(() => String, { description: 'User ID' })
|
|
13
13
|
id: string;
|
|
14
14
|
|
|
@@ -36,15 +36,15 @@ export class BetterAuthUserModel {
|
|
|
36
36
|
*/
|
|
37
37
|
@ObjectType({ description: 'Better-Auth Session' })
|
|
38
38
|
@Restricted(RoleEnum.S_USER)
|
|
39
|
-
export class
|
|
39
|
+
export class CoreBetterAuthSessionModel {
|
|
40
40
|
@Field(() => String, { description: 'Session ID' })
|
|
41
41
|
id: string;
|
|
42
42
|
|
|
43
43
|
@Field(() => Date, { description: 'Session expiration date' })
|
|
44
44
|
expiresAt: Date;
|
|
45
45
|
|
|
46
|
-
@Field(() =>
|
|
47
|
-
user:
|
|
46
|
+
@Field(() => CoreBetterAuthUserModel, { description: 'Session user' })
|
|
47
|
+
user: CoreBetterAuthUserModel;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
@@ -52,7 +52,7 @@ export class BetterAuthSessionModel {
|
|
|
52
52
|
*/
|
|
53
53
|
@ObjectType({ description: 'Better-Auth Session Info' })
|
|
54
54
|
@Restricted(RoleEnum.S_EVERYONE)
|
|
55
|
-
export class
|
|
55
|
+
export class CoreBetterAuthSessionInfoModel {
|
|
56
56
|
@Field(() => String, { description: 'Session ID', nullable: true })
|
|
57
57
|
id?: string;
|
|
58
58
|
|
|
@@ -68,7 +68,7 @@ export class BetterAuthSessionInfoModel {
|
|
|
68
68
|
*/
|
|
69
69
|
@ObjectType({ description: 'Better-Auth 2FA setup data' })
|
|
70
70
|
@Restricted(RoleEnum.S_USER)
|
|
71
|
-
export class
|
|
71
|
+
export class CoreBetterAuth2FASetupModel {
|
|
72
72
|
@Field(() => Boolean, { description: 'Whether the operation was successful' })
|
|
73
73
|
success: boolean;
|
|
74
74
|
|
|
@@ -87,7 +87,7 @@ export class BetterAuth2FASetupModel {
|
|
|
87
87
|
*/
|
|
88
88
|
@ObjectType({ description: 'Better-Auth Passkey' })
|
|
89
89
|
@Restricted(RoleEnum.S_USER)
|
|
90
|
-
export class
|
|
90
|
+
export class CoreBetterAuthPasskeyModel {
|
|
91
91
|
@Field(() => String, { description: 'Passkey ID' })
|
|
92
92
|
id: string;
|
|
93
93
|
|
|
@@ -106,7 +106,7 @@ export class BetterAuthPasskeyModel {
|
|
|
106
106
|
*/
|
|
107
107
|
@ObjectType({ description: 'Better-Auth Passkey registration challenge' })
|
|
108
108
|
@Restricted(RoleEnum.S_USER)
|
|
109
|
-
export class
|
|
109
|
+
export class CoreBetterAuthPasskeyChallengeModel {
|
|
110
110
|
@Field(() => Boolean, { description: 'Whether the operation was successful' })
|
|
111
111
|
success: boolean;
|
|
112
112
|
|
|
@@ -122,7 +122,7 @@ export class BetterAuthPasskeyChallengeModel {
|
|
|
122
122
|
*/
|
|
123
123
|
@ObjectType({ description: 'Better-Auth features status' })
|
|
124
124
|
@Restricted(RoleEnum.S_EVERYONE)
|
|
125
|
-
export class
|
|
125
|
+
export class CoreBetterAuthFeaturesModel {
|
|
126
126
|
@Field(() => Boolean, { description: 'Whether Better-Auth is enabled' })
|
|
127
127
|
enabled: boolean;
|
|
128
128
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { HttpException, HttpStatus, Injectable, NestMiddleware } from '@nestjs/common';
|
|
2
2
|
import { NextFunction, Request, Response } from 'express';
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { CoreBetterAuthRateLimiter, RateLimitResult } from './core-better-auth-rate-limiter.service';
|
|
5
|
+
import { CoreBetterAuthService } from './core-better-auth.service';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Middleware that applies rate limiting to Better-Auth endpoints
|
|
@@ -30,10 +30,10 @@ import { BetterAuthService } from './better-auth.service';
|
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
32
|
@Injectable()
|
|
33
|
-
export class
|
|
33
|
+
export class CoreBetterAuthRateLimitMiddleware implements NestMiddleware {
|
|
34
34
|
constructor(
|
|
35
|
-
private readonly rateLimiter:
|
|
36
|
-
private readonly betterAuthService:
|
|
35
|
+
private readonly rateLimiter: CoreBetterAuthRateLimiter,
|
|
36
|
+
private readonly betterAuthService: CoreBetterAuthService,
|
|
37
37
|
) {}
|
|
38
38
|
|
|
39
39
|
use(req: Request, res: Response, next: NextFunction) {
|
|
@@ -74,8 +74,8 @@ const DEFAULT_CONFIG: Required<IBetterAuthRateLimit> = {
|
|
|
74
74
|
* ```
|
|
75
75
|
*/
|
|
76
76
|
@Injectable()
|
|
77
|
-
export class
|
|
78
|
-
private readonly logger = new Logger(
|
|
77
|
+
export class CoreBetterAuthRateLimiter {
|
|
78
|
+
private readonly logger = new Logger(CoreBetterAuthRateLimiter.name);
|
|
79
79
|
private readonly store = new Map<string, RateLimitEntry>();
|
|
80
80
|
private config: Required<IBetterAuthRateLimit> = DEFAULT_CONFIG;
|
|
81
81
|
private cleanupInterval: NodeJS.Timeout | null = null;
|
package/src/core/modules/better-auth/{better-auth-user.mapper.ts → core-better-auth-user.mapper.ts}
RENAMED
|
@@ -17,6 +17,7 @@ const scryptPromise = (password: string, salt: string, keylen: number, options:
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
import { RoleEnum } from '../../common/enums/role.enum';
|
|
20
|
+
import { maskEmail } from '../../common/helpers/logging.helper';
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* Interface for Better-Auth session user
|
|
@@ -97,8 +98,8 @@ export interface SyncedUserDocument {
|
|
|
97
98
|
* - Legacy → IAM: Creates account entry in `accounts` from `users.password`
|
|
98
99
|
*/
|
|
99
100
|
@Injectable()
|
|
100
|
-
export class
|
|
101
|
-
private readonly logger = new Logger(
|
|
101
|
+
export class CoreBetterAuthUserMapper {
|
|
102
|
+
private readonly logger = new Logger(CoreBetterAuthUserMapper.name);
|
|
102
103
|
|
|
103
104
|
constructor(@Optional() @InjectConnection() private readonly connection?: Connection) {}
|
|
104
105
|
|
|
@@ -379,7 +380,7 @@ export class BetterAuthUserMapper {
|
|
|
379
380
|
const sha256Match = await bcrypt.compare(sha256(plainPassword), legacyUser.password);
|
|
380
381
|
if (!directMatch && !sha256Match) {
|
|
381
382
|
// Security: Wrong password provided for migration - reject
|
|
382
|
-
this.logger.warn(`Migration password verification failed for ${userEmail}`);
|
|
383
|
+
this.logger.warn(`Migration password verification failed for ${maskEmail(userEmail)}`);
|
|
383
384
|
return false;
|
|
384
385
|
}
|
|
385
386
|
} else {
|