@flusys/nestjs-auth 1.1.0-beta → 1.1.0
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/README.md +633 -468
- package/cjs/config/auth.constants.js +8 -0
- package/cjs/controllers/authentication.controller.js +175 -167
- package/cjs/controllers/branch.controller.js +44 -1
- package/cjs/controllers/company-selection.controller.js +27 -20
- package/cjs/controllers/company.controller.js +44 -1
- package/cjs/controllers/email-verification.controller.js +220 -0
- package/cjs/controllers/index.js +1 -0
- package/cjs/controllers/user-permission.controller.js +82 -154
- package/cjs/controllers/user.controller.js +68 -15
- package/cjs/docs/auth-swagger.config.js +14 -7
- package/cjs/dtos/authentication.dto.js +16 -43
- package/cjs/dtos/index.js +0 -1
- package/cjs/dtos/user-permission.dto.js +28 -0
- package/cjs/dtos/user.dto.js +19 -4
- package/cjs/entities/index.js +16 -17
- package/cjs/entities/user-company-permission.entity.js +3 -9
- package/cjs/interceptors/clear-token.interceptor.js +10 -23
- package/cjs/interceptors/set-token.interceptor.js +15 -55
- package/cjs/interfaces/auth-email-provider.interface.js +1 -1
- package/cjs/modules/auth.module.js +79 -154
- package/cjs/services/auth-config.service.js +33 -35
- package/cjs/services/auth-datasource.provider.js +18 -42
- package/cjs/services/auth-email.service.js +89 -85
- package/cjs/services/authentication.service.js +248 -414
- package/cjs/services/branch.service.js +9 -4
- package/cjs/services/company-selection-session.service.js +1 -1
- package/cjs/services/user-permission.service.js +43 -73
- package/cjs/services/user.service.js +78 -64
- package/cjs/strategies/jwt.strategy.js +44 -44
- package/cjs/validators/password.validator.js +45 -0
- package/config/auth.constants.d.ts +2 -0
- package/controllers/authentication.controller.d.ts +4 -10
- package/controllers/company-selection.controller.d.ts +6 -2
- package/controllers/email-verification.controller.d.ts +20 -0
- package/controllers/index.d.ts +1 -0
- package/controllers/user-permission.controller.d.ts +5 -4
- package/controllers/user.controller.d.ts +6 -0
- package/dtos/authentication.dto.d.ts +0 -4
- package/dtos/company-branch.dto.d.ts +0 -1
- package/dtos/company.dto.d.ts +0 -1
- package/dtos/index.d.ts +0 -1
- package/dtos/user-permission.dto.d.ts +4 -0
- package/dtos/user.dto.d.ts +1 -3
- package/entities/index.d.ts +5 -10
- package/entities/user-company-permission.entity.d.ts +1 -3
- package/fesm/config/auth.constants.js +6 -13
- package/fesm/controllers/authentication.controller.js +178 -170
- package/fesm/controllers/branch.controller.js +45 -2
- package/fesm/controllers/company-selection.controller.js +28 -21
- package/fesm/controllers/company.controller.js +45 -2
- package/fesm/controllers/email-verification.controller.js +210 -0
- package/fesm/controllers/index.js +1 -0
- package/fesm/controllers/user-permission.controller.js +84 -156
- package/fesm/controllers/user.controller.js +71 -18
- package/fesm/docs/auth-swagger.config.js +14 -7
- package/fesm/dtos/authentication.dto.js +17 -44
- package/fesm/dtos/index.js +0 -1
- package/fesm/dtos/user-permission.dto.js +28 -0
- package/fesm/dtos/user.dto.js +20 -5
- package/fesm/entities/index.js +17 -9
- package/fesm/entities/user-company-permission.entity.js +3 -9
- package/fesm/interceptors/clear-token.interceptor.js +9 -22
- package/fesm/interceptors/set-token.interceptor.js +14 -54
- package/fesm/interfaces/auth-email-provider.interface.js +1 -1
- package/fesm/modules/auth.module.js +82 -157
- package/fesm/services/auth-config.service.js +34 -36
- package/fesm/services/auth-datasource.provider.js +19 -43
- package/fesm/services/auth-email.service.js +90 -86
- package/fesm/services/authentication.service.js +248 -414
- package/fesm/services/branch.service.js +9 -4
- package/fesm/services/company-selection-session.service.js +1 -1
- package/fesm/services/user-permission.service.js +44 -74
- package/fesm/services/user.service.js +79 -65
- package/fesm/strategies/jwt.strategy.js +44 -44
- package/fesm/validators/password.validator.js +48 -0
- package/interceptors/clear-token.interceptor.d.ts +0 -1
- package/interceptors/set-token.interceptor.d.ts +1 -1
- package/interfaces/authentication.interface.d.ts +0 -4
- package/interfaces/user-enricher.interface.d.ts +1 -4
- package/modules/auth.module.d.ts +9 -4
- package/package.json +4 -4
- package/services/auth-config.service.d.ts +7 -8
- package/services/auth-datasource.provider.d.ts +3 -4
- package/services/auth-email.service.d.ts +8 -4
- package/services/authentication.service.d.ts +16 -16
- package/services/company-selection-session.service.d.ts +1 -1
- package/services/user-permission.service.d.ts +6 -9
- package/services/user.service.d.ts +3 -0
- package/strategies/jwt.strategy.d.ts +4 -1
- package/validators/password.validator.d.ts +10 -0
- package/cjs/dtos/user-company-permission.dto.js +0 -110
- package/dtos/user-company-permission.dto.d.ts +0 -10
- package/fesm/dtos/user-company-permission.dto.js +0 -98
package/controllers/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { AssignUserCompanyDto,
|
|
1
|
+
import { AssignUserBranchDto, AssignUserCompanyDto, UserBranchesResponseDto, UserCompaniesResponseDto } from '../dtos/user-permission.dto';
|
|
2
2
|
import { UserPermissionService } from '../services/user-permission.service';
|
|
3
3
|
export declare class UserPermissionController {
|
|
4
|
-
private readonly
|
|
5
|
-
constructor(
|
|
4
|
+
private readonly permissionService;
|
|
5
|
+
constructor(permissionService: UserPermissionService);
|
|
6
6
|
assignUserCompany(dto: AssignUserCompanyDto): Promise<{
|
|
7
7
|
success: boolean;
|
|
8
8
|
message: string;
|
|
@@ -12,5 +12,6 @@ export declare class UserPermissionController {
|
|
|
12
12
|
success: boolean;
|
|
13
13
|
message: string;
|
|
14
14
|
}>;
|
|
15
|
-
getUserBranches(userId: string): Promise<UserBranchesResponseDto>;
|
|
15
|
+
getUserBranches(userId: string, companyId?: string): Promise<UserBranchesResponseDto>;
|
|
16
|
+
private processBatchPermissions;
|
|
16
17
|
}
|
|
@@ -23,12 +23,15 @@ export declare class UserController extends UserController_base {
|
|
|
23
23
|
};
|
|
24
24
|
}>;
|
|
25
25
|
verifyEmail(id: string): Promise<{
|
|
26
|
+
success: boolean;
|
|
26
27
|
message: string;
|
|
27
28
|
}>;
|
|
28
29
|
verifyPhone(id: string): Promise<{
|
|
30
|
+
success: boolean;
|
|
29
31
|
message: string;
|
|
30
32
|
}>;
|
|
31
33
|
updateStatus(id: string, isActive: boolean, user: ILoggedUserInfo): Promise<{
|
|
34
|
+
success: boolean;
|
|
32
35
|
message: string;
|
|
33
36
|
}>;
|
|
34
37
|
profile(dto: UpdateUserDto, user: ILoggedUserInfo): Promise<{
|
|
@@ -38,10 +41,12 @@ export declare class UserController extends UserController_base {
|
|
|
38
41
|
}>;
|
|
39
42
|
getProfileSections(): Promise<{
|
|
40
43
|
success: boolean;
|
|
44
|
+
message: string;
|
|
41
45
|
data: import("..").IProfileSection[];
|
|
42
46
|
}>;
|
|
43
47
|
getProfileSectionData(id: string, sectionId: string, user: ILoggedUserInfo): Promise<{
|
|
44
48
|
success: boolean;
|
|
49
|
+
message: string;
|
|
45
50
|
data: import("..").IProfileSectionData;
|
|
46
51
|
}>;
|
|
47
52
|
updateProfileSection(id: string, sectionId: string, data: Record<string, any>, user: ILoggedUserInfo): Promise<{
|
|
@@ -50,6 +55,7 @@ export declare class UserController extends UserController_base {
|
|
|
50
55
|
}>;
|
|
51
56
|
getProfileCompletion(id: string, user: ILoggedUserInfo): Promise<{
|
|
52
57
|
success: boolean;
|
|
58
|
+
message: string;
|
|
53
59
|
data: {
|
|
54
60
|
completionPercentage: number;
|
|
55
61
|
};
|
|
@@ -51,14 +51,12 @@ export declare class BranchSelectionInfoDto {
|
|
|
51
51
|
id: string;
|
|
52
52
|
name: string;
|
|
53
53
|
slug: string;
|
|
54
|
-
isActive: boolean;
|
|
55
54
|
logoId?: string | null;
|
|
56
55
|
}
|
|
57
56
|
export declare class CompanyWithBranchesDto {
|
|
58
57
|
id: string;
|
|
59
58
|
name: string;
|
|
60
59
|
slug: string;
|
|
61
|
-
isActive: boolean;
|
|
62
60
|
logoId?: string | null;
|
|
63
61
|
branches: BranchSelectionInfoDto[];
|
|
64
62
|
}
|
|
@@ -68,7 +66,6 @@ export declare class LoginResponseDto {
|
|
|
68
66
|
user: UserInfoDto;
|
|
69
67
|
company?: CompanyInfoDto;
|
|
70
68
|
branch?: BranchInfoDto;
|
|
71
|
-
companyFeatureEnabled?: boolean;
|
|
72
69
|
}
|
|
73
70
|
export declare class RegistrationResponseDto {
|
|
74
71
|
accessToken?: string;
|
|
@@ -76,7 +73,6 @@ export declare class RegistrationResponseDto {
|
|
|
76
73
|
user: UserInfoDto;
|
|
77
74
|
company?: CompanyInfoDto;
|
|
78
75
|
branch?: BranchInfoDto;
|
|
79
|
-
companyFeatureEnabled?: boolean;
|
|
80
76
|
requiresEmailVerification?: boolean;
|
|
81
77
|
}
|
|
82
78
|
export declare class MeResponseDto {
|
package/dtos/company.dto.d.ts
CHANGED
package/dtos/index.d.ts
CHANGED
|
@@ -26,8 +26,12 @@ export declare class UserBranchPermissionDto {
|
|
|
26
26
|
companyId: string;
|
|
27
27
|
}
|
|
28
28
|
export declare class UserCompaniesResponseDto {
|
|
29
|
+
success: boolean;
|
|
30
|
+
message: string;
|
|
29
31
|
data: UserCompanyPermissionDto[];
|
|
30
32
|
}
|
|
31
33
|
export declare class UserBranchesResponseDto {
|
|
34
|
+
success: boolean;
|
|
35
|
+
message: string;
|
|
32
36
|
data: UserBranchPermissionDto[];
|
|
33
37
|
}
|
package/dtos/user.dto.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export declare class CreateUserDto {
|
|
2
|
-
[key: string]: unknown;
|
|
3
2
|
name: string;
|
|
4
3
|
phone?: string;
|
|
5
4
|
email: string;
|
|
@@ -11,12 +10,12 @@ export declare class CreateUserDto {
|
|
|
11
10
|
additionalFields?: Record<string, any>;
|
|
12
11
|
}
|
|
13
12
|
export declare class UpdateUserDto {
|
|
14
|
-
[key: string]: unknown;
|
|
15
13
|
id: string;
|
|
16
14
|
name?: string;
|
|
17
15
|
phone?: string;
|
|
18
16
|
email?: string;
|
|
19
17
|
password?: string;
|
|
18
|
+
oldPassword?: string;
|
|
20
19
|
profilePictureId?: string;
|
|
21
20
|
isActive?: boolean;
|
|
22
21
|
emailVerified?: boolean;
|
|
@@ -24,7 +23,6 @@ export declare class UpdateUserDto {
|
|
|
24
23
|
additionalFields?: Record<string, any>;
|
|
25
24
|
}
|
|
26
25
|
export declare class UserResponseDto {
|
|
27
|
-
[key: string]: unknown;
|
|
28
26
|
id: string;
|
|
29
27
|
name: string;
|
|
30
28
|
phone?: string | null;
|
package/entities/index.d.ts
CHANGED
|
@@ -4,13 +4,8 @@ export * from './pending-registration.entity';
|
|
|
4
4
|
export { PermissionType, UserCompanyPermission } from './user-company-permission.entity';
|
|
5
5
|
export * from './user.entity';
|
|
6
6
|
export * from './user-token.entity';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import { UserToken } from './user-token.entity';
|
|
13
|
-
export declare const CoreEntities: (typeof PendingRegistration | typeof User | typeof UserToken)[];
|
|
14
|
-
export declare const CompanyEntities: (typeof Company | typeof CompanyBranch | typeof UserCompanyPermission)[];
|
|
15
|
-
export declare const AllEntities: (typeof Company | typeof CompanyBranch | typeof PendingRegistration | typeof User | typeof UserCompanyPermission | typeof UserToken)[];
|
|
16
|
-
export declare function getEntitiesByConfig(enableCompanyFeature: boolean): (typeof Company | typeof CompanyBranch | typeof PendingRegistration | typeof User | typeof UserCompanyPermission | typeof UserToken)[];
|
|
7
|
+
export interface IAuthEntitiesConfig {
|
|
8
|
+
enableCompanyFeature?: boolean;
|
|
9
|
+
enableEmailVerification?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function getEntitiesByConfig(config: IAuthEntitiesConfig): any[];
|
|
@@ -7,7 +7,7 @@ export declare class UserCompanyPermission {
|
|
|
7
7
|
id: string;
|
|
8
8
|
userId: string;
|
|
9
9
|
user?: User;
|
|
10
|
-
permissionType:
|
|
10
|
+
permissionType: string;
|
|
11
11
|
targetId: string;
|
|
12
12
|
metadata?: Record<string, any>;
|
|
13
13
|
isActive: boolean;
|
|
@@ -15,6 +15,4 @@ export declare class UserCompanyPermission {
|
|
|
15
15
|
branch?: CompanyBranch;
|
|
16
16
|
createdAt: Date;
|
|
17
17
|
updatedAt: Date;
|
|
18
|
-
isCompanyPermission(): boolean;
|
|
19
|
-
isBranchPermission(): boolean;
|
|
20
18
|
}
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
// Injection Tokens
|
|
2
2
|
export const AUTH_MODULE_OPTIONS = 'AUTH_MODULE_OPTIONS';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
* @example
|
|
10
|
-
* providers: [
|
|
11
|
-
* { provide: USER_ENRICHER, useClass: MyUserEnricher },
|
|
12
|
-
* ]
|
|
13
|
-
*/ export const USER_ENRICHER = 'USER_ENRICHER';
|
|
14
|
-
// Security Configuration
|
|
15
|
-
/** Bcrypt salt rounds (12 = ~250ms hashing time) */ export const BCRYPT_SALT_ROUNDS = 12;
|
|
3
|
+
export const USER_ENRICHER = 'USER_ENRICHER';
|
|
4
|
+
// Security
|
|
5
|
+
export const BCRYPT_SALT_ROUNDS = 12;
|
|
6
|
+
// Cookie
|
|
7
|
+
export const DEFAULT_COOKIE_NAME = 'fsn_refresh_token';
|
|
8
|
+
export const MAX_COOKIE_BYTES = 3500;
|
|
16
9
|
// Cache Key Prefixes
|
|
17
10
|
export const SESSION_CACHE_PREFIX = 'auth:session';
|
|
18
11
|
export const TOKEN_REVOKED_PREFIX = 'auth:revoked';
|
|
@@ -26,26 +26,29 @@ function _ts_param(paramIndex, decorator) {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
import { CurrentUser } from '@flusys/nestjs-shared/decorators';
|
|
29
|
+
import { MessageResponseDto } from '@flusys/nestjs-shared/dtos';
|
|
29
30
|
import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
|
|
30
31
|
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
31
|
-
import { Body, Controller,
|
|
32
|
-
import { ApiBearerAuth, ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
|
32
|
+
import { Body, Controller, HttpCode, HttpStatus, Inject, Optional, Post, Req, UnauthorizedException, UseGuards, UseInterceptors } from '@nestjs/common';
|
|
33
|
+
import { ApiBearerAuth, ApiBody, ApiExtraModels, ApiHeader, ApiOperation, ApiResponse, ApiTags, getSchemaPath } from '@nestjs/swagger';
|
|
34
|
+
import { SkipThrottle, Throttle } from '@nestjs/throttler';
|
|
33
35
|
import { Request } from 'express';
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
+
import { DEFAULT_COOKIE_NAME } from '../config';
|
|
37
|
+
import { ChangePasswordDto, CompanySelectionResponseDto, LoginDto, LoginResponseDto, LoginResponseRequiresEmailVerificationDto, MeResponseDto, RefreshTokenDto, RegistrationDto, RegistrationResponseDto } from '../dtos/authentication.dto';
|
|
36
38
|
import { ClearToken } from '../interceptors/clear-token.interceptor';
|
|
37
39
|
import { SetToken } from '../interceptors/set-token.interceptor';
|
|
38
40
|
import { AuthConfigService } from '../services/auth-config.service';
|
|
39
|
-
import { AuthEmailService } from '../services/auth-email.service';
|
|
40
41
|
import { AuthenticationService } from '../services/authentication.service';
|
|
41
42
|
export class AuthenticationController {
|
|
42
|
-
|
|
43
|
+
buildAuthUrl(req, path) {
|
|
43
44
|
const frontendUrl = this.authConfig?.getFrontendUrl();
|
|
44
45
|
return frontendUrl ? `${frontendUrl}${path}` : `${req.protocol}://${req.get('host')}${path}`;
|
|
45
46
|
}
|
|
46
|
-
|
|
47
|
-
/** Login - Returns tokens or company/branch selection requirement */ async login(loginDto) {
|
|
47
|
+
async login(loginDto) {
|
|
48
48
|
const result = await this.authService.login(loginDto.email, loginDto.password);
|
|
49
|
+
if ('requiresEmailVerification' in result) {
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
49
52
|
if ('requiresSelection' in result) {
|
|
50
53
|
return result;
|
|
51
54
|
}
|
|
@@ -54,14 +57,15 @@ export class AuthenticationController {
|
|
|
54
57
|
/** Register new user. If company feature enabled, can join/create company. */ async register(req, registrationDto) {
|
|
55
58
|
const verifyUrlBase = this.buildAuthUrl(req, '/auth/verify-email');
|
|
56
59
|
const result = await this.authService.register(registrationDto, verifyUrlBase);
|
|
57
|
-
return
|
|
58
|
-
...result,
|
|
59
|
-
companyFeatureEnabled: this.authConfig?.isCompanyFeatureEnabled() ?? false
|
|
60
|
-
};
|
|
60
|
+
return result;
|
|
61
61
|
}
|
|
62
|
-
/**
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Refresh access token.
|
|
64
|
+
* - **Browser**: Prefers HTTP-only cookie, falls back to body if cookie missing.
|
|
65
|
+
* - **API/Mobile**: Must send `refreshToken` in request body. Set header `x-client-type: mobile` or `x-client-type: api`.
|
|
66
|
+
*/ async refreshToken(req, refreshTokenDto) {
|
|
67
|
+
const cookieName = this.authConfig?.getRefreshTokenCookieName() ?? DEFAULT_COOKIE_NAME;
|
|
68
|
+
// Always check cookie first, then body - handles Browser, SSR, and Mobile/API
|
|
65
69
|
const refreshToken = req.cookies?.[cookieName] || refreshTokenDto.refreshToken;
|
|
66
70
|
if (!refreshToken) {
|
|
67
71
|
throw new UnauthorizedException('Refresh token is required');
|
|
@@ -78,75 +82,29 @@ export class AuthenticationController {
|
|
|
78
82
|
message: 'Logged out successfully'
|
|
79
83
|
};
|
|
80
84
|
}
|
|
81
|
-
|
|
85
|
+
async changePassword(userId, dto) {
|
|
82
86
|
await this.authService.changePassword(userId, dto.currentPassword, dto.newPassword);
|
|
83
87
|
return {
|
|
84
88
|
success: true,
|
|
85
89
|
message: 'Password changed successfully'
|
|
86
90
|
};
|
|
87
91
|
}
|
|
88
|
-
|
|
89
|
-
/** Forgot password - Request password reset email */ async forgotPassword(req, dto) {
|
|
90
|
-
if (!this.authEmailService) {
|
|
91
|
-
return {
|
|
92
|
-
success: false,
|
|
93
|
-
message: 'Email service is not configured'
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
return this.authEmailService.forgotPassword(dto.email, this.buildAuthUrl(req, '/auth/reset-password'));
|
|
97
|
-
}
|
|
98
|
-
/** Reset password - Reset password using token */ async resetPassword(dto) {
|
|
99
|
-
if (!this.authEmailService) {
|
|
100
|
-
return {
|
|
101
|
-
success: false,
|
|
102
|
-
message: 'Email service is not configured'
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
return this.authEmailService.resetPassword(dto.token, dto.newPassword);
|
|
106
|
-
}
|
|
107
|
-
/** Resend verification email - For unauthenticated users (handles both pending registration and existing users) */ async resendVerification(req, dto) {
|
|
108
|
-
if (!this.authEmailService) {
|
|
109
|
-
return {
|
|
110
|
-
success: false,
|
|
111
|
-
message: 'Email service is not configured'
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
return this.authEmailService.resendVerificationEmail(dto.email, this.buildAuthUrl(req, '/auth/verify-email'));
|
|
115
|
-
}
|
|
116
|
-
/** Verify email - Verify email using token. Handles both pending registration and existing user verification. */ async verifyEmail(dto) {
|
|
117
|
-
if (!this.authEmailService) {
|
|
118
|
-
return {
|
|
119
|
-
success: false,
|
|
120
|
-
message: 'Email service is not configured'
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
const result = await this.authEmailService.verifyEmail(dto.token);
|
|
124
|
-
if (result.type === 'pending') {
|
|
125
|
-
// Create user from pending registration
|
|
126
|
-
const registrationResult = await this.authService.createUserFromPendingRegistration(result.pending);
|
|
127
|
-
return {
|
|
128
|
-
...registrationResult,
|
|
129
|
-
companyFeatureEnabled: this.authConfig?.isCompanyFeatureEnabled() ?? false
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
return {
|
|
133
|
-
success: true,
|
|
134
|
-
message: result.message
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
// NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
|
|
138
|
-
constructor(authService, authConfig, authEmailService){
|
|
92
|
+
constructor(authService, authConfig){
|
|
139
93
|
_define_property(this, "authService", void 0);
|
|
140
94
|
_define_property(this, "authConfig", void 0);
|
|
141
|
-
_define_property(this, "authEmailService", void 0);
|
|
142
95
|
this.authService = authService;
|
|
143
96
|
this.authConfig = authConfig;
|
|
144
|
-
this.authEmailService = authEmailService;
|
|
145
97
|
}
|
|
146
98
|
}
|
|
147
99
|
_ts_decorate([
|
|
148
100
|
Post('login'),
|
|
149
101
|
HttpCode(HttpStatus.OK),
|
|
102
|
+
Throttle({
|
|
103
|
+
default: {
|
|
104
|
+
limit: 5,
|
|
105
|
+
ttl: 60000
|
|
106
|
+
}
|
|
107
|
+
}),
|
|
150
108
|
UseInterceptors(SetToken),
|
|
151
109
|
ApiOperation({
|
|
152
110
|
summary: 'User login'
|
|
@@ -154,13 +112,102 @@ _ts_decorate([
|
|
|
154
112
|
ApiBody({
|
|
155
113
|
type: LoginDto
|
|
156
114
|
}),
|
|
115
|
+
ApiExtraModels(LoginResponseDto, CompanySelectionResponseDto, LoginResponseRequiresEmailVerificationDto),
|
|
157
116
|
ApiResponse({
|
|
158
117
|
status: 200,
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
118
|
+
description: 'Login successful, requires company selection, or requires email verification',
|
|
119
|
+
content: {
|
|
120
|
+
'application/json': {
|
|
121
|
+
schema: {
|
|
122
|
+
oneOf: [
|
|
123
|
+
{
|
|
124
|
+
$ref: getSchemaPath(LoginResponseDto)
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
$ref: getSchemaPath(CompanySelectionResponseDto)
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
$ref: getSchemaPath(LoginResponseRequiresEmailVerificationDto)
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
examples: {
|
|
135
|
+
loginSuccess: {
|
|
136
|
+
summary: 'Login Success',
|
|
137
|
+
description: 'Successful login with tokens (company/branch included when company feature enabled)',
|
|
138
|
+
value: {
|
|
139
|
+
accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
|
|
140
|
+
refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
|
|
141
|
+
user: {
|
|
142
|
+
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
143
|
+
email: 'john@example.com',
|
|
144
|
+
name: 'John Doe'
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
loginSuccessWithCompany: {
|
|
149
|
+
summary: 'Login Success (Company Feature)',
|
|
150
|
+
description: 'Login with company/branch - only when company feature is enabled',
|
|
151
|
+
value: {
|
|
152
|
+
accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
|
|
153
|
+
refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
|
|
154
|
+
user: {
|
|
155
|
+
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
156
|
+
email: 'john@example.com',
|
|
157
|
+
name: 'John Doe'
|
|
158
|
+
},
|
|
159
|
+
company: {
|
|
160
|
+
id: '550e8400-e29b-41d4-a716-446655440001',
|
|
161
|
+
name: 'Acme Corporation',
|
|
162
|
+
slug: 'acme-corp'
|
|
163
|
+
},
|
|
164
|
+
branch: {
|
|
165
|
+
id: '550e8400-e29b-41d4-a716-446655440002',
|
|
166
|
+
name: 'Main Branch',
|
|
167
|
+
slug: 'main-branch'
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
companySelection: {
|
|
172
|
+
summary: 'Company Selection Required (Company Feature)',
|
|
173
|
+
description: 'User has multiple companies/branches - only when company feature is enabled',
|
|
174
|
+
value: {
|
|
175
|
+
requiresSelection: true,
|
|
176
|
+
sessionId: 'sess_abc123def456',
|
|
177
|
+
expiresAt: '2024-12-18T15:30:00.000Z',
|
|
178
|
+
user: {
|
|
179
|
+
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
180
|
+
email: 'john@example.com',
|
|
181
|
+
name: 'John Doe'
|
|
182
|
+
},
|
|
183
|
+
companies: [
|
|
184
|
+
{
|
|
185
|
+
id: '550e8400-e29b-41d4-a716-446655440001',
|
|
186
|
+
name: 'Company A',
|
|
187
|
+
slug: 'company-a',
|
|
188
|
+
branches: [
|
|
189
|
+
{
|
|
190
|
+
id: '550e8400-e29b-41d4-a716-446655440010',
|
|
191
|
+
name: 'Branch 1',
|
|
192
|
+
slug: 'branch-1'
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
emailVerification: {
|
|
200
|
+
summary: 'Email Verification Required',
|
|
201
|
+
description: 'User must verify email before logging in',
|
|
202
|
+
value: {
|
|
203
|
+
requiresEmailVerification: true,
|
|
204
|
+
email: 'john@example.com',
|
|
205
|
+
message: 'Please verify your email address before logging in.'
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
164
211
|
}),
|
|
165
212
|
_ts_param(0, Body()),
|
|
166
213
|
_ts_metadata("design:type", Function),
|
|
@@ -172,6 +219,12 @@ _ts_decorate([
|
|
|
172
219
|
_ts_decorate([
|
|
173
220
|
Post('register'),
|
|
174
221
|
HttpCode(HttpStatus.CREATED),
|
|
222
|
+
Throttle({
|
|
223
|
+
default: {
|
|
224
|
+
limit: 3,
|
|
225
|
+
ttl: 60000
|
|
226
|
+
}
|
|
227
|
+
}),
|
|
175
228
|
UseInterceptors(SetToken),
|
|
176
229
|
ApiOperation({
|
|
177
230
|
summary: 'Register new user'
|
|
@@ -195,11 +248,50 @@ _ts_decorate([
|
|
|
195
248
|
_ts_decorate([
|
|
196
249
|
Post('refresh'),
|
|
197
250
|
HttpCode(HttpStatus.OK),
|
|
251
|
+
Throttle({
|
|
252
|
+
default: {
|
|
253
|
+
limit: 30,
|
|
254
|
+
ttl: 60000
|
|
255
|
+
}
|
|
256
|
+
}),
|
|
198
257
|
ApiOperation({
|
|
199
|
-
summary: 'Refresh access token'
|
|
258
|
+
summary: 'Refresh access token',
|
|
259
|
+
description: `
|
|
260
|
+
**Browser clients**: Refresh token is read from HTTP-only cookie (automatically set on login/register).
|
|
261
|
+
Falls back to request body if cookie is not available.
|
|
262
|
+
|
|
263
|
+
**API/Mobile clients**: Must send \`refreshToken\` in request body.
|
|
264
|
+
Set header \`x-client-type: mobile\` or \`x-client-type: api\` to identify as non-browser client.
|
|
265
|
+
|
|
266
|
+
**Client detection**: Uses \`x-client-type\` header first, then falls back to User-Agent detection.
|
|
267
|
+
`
|
|
268
|
+
}),
|
|
269
|
+
ApiHeader({
|
|
270
|
+
name: 'x-client-type',
|
|
271
|
+
required: false,
|
|
272
|
+
description: 'Client type: "browser"/"web" for cookie-based auth, "mobile"/"api" for body-based auth',
|
|
273
|
+
example: 'mobile'
|
|
200
274
|
}),
|
|
201
275
|
ApiBody({
|
|
202
|
-
type: RefreshTokenDto
|
|
276
|
+
type: RefreshTokenDto,
|
|
277
|
+
description: 'Required for API/Mobile clients. Browser clients prefer HTTP-only cookie with body fallback.'
|
|
278
|
+
}),
|
|
279
|
+
ApiResponse({
|
|
280
|
+
status: 200,
|
|
281
|
+
description: 'New access token',
|
|
282
|
+
schema: {
|
|
283
|
+
type: 'object',
|
|
284
|
+
properties: {
|
|
285
|
+
accessToken: {
|
|
286
|
+
type: 'string',
|
|
287
|
+
example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}),
|
|
292
|
+
ApiResponse({
|
|
293
|
+
status: 401,
|
|
294
|
+
description: 'Invalid or missing refresh token'
|
|
203
295
|
}),
|
|
204
296
|
_ts_param(0, Req()),
|
|
205
297
|
_ts_param(1, Body()),
|
|
@@ -212,7 +304,8 @@ _ts_decorate([
|
|
|
212
304
|
], AuthenticationController.prototype, "refreshToken", null);
|
|
213
305
|
_ts_decorate([
|
|
214
306
|
UseGuards(JwtAuthGuard),
|
|
215
|
-
|
|
307
|
+
Post('me'),
|
|
308
|
+
SkipThrottle(),
|
|
216
309
|
ApiBearerAuth(),
|
|
217
310
|
ApiOperation({
|
|
218
311
|
summary: 'Get current user info'
|
|
@@ -232,6 +325,7 @@ _ts_decorate([
|
|
|
232
325
|
UseGuards(JwtAuthGuard),
|
|
233
326
|
Post('logout'),
|
|
234
327
|
HttpCode(HttpStatus.OK),
|
|
328
|
+
SkipThrottle(),
|
|
235
329
|
UseInterceptors(ClearToken),
|
|
236
330
|
ApiBearerAuth(),
|
|
237
331
|
ApiOperation({
|
|
@@ -252,6 +346,12 @@ _ts_decorate([
|
|
|
252
346
|
UseGuards(JwtAuthGuard),
|
|
253
347
|
Post('change-password'),
|
|
254
348
|
HttpCode(HttpStatus.OK),
|
|
349
|
+
Throttle({
|
|
350
|
+
default: {
|
|
351
|
+
limit: 5,
|
|
352
|
+
ttl: 60000
|
|
353
|
+
}
|
|
354
|
+
}),
|
|
255
355
|
ApiBearerAuth(),
|
|
256
356
|
ApiOperation({
|
|
257
357
|
summary: 'Change password'
|
|
@@ -272,107 +372,15 @@ _ts_decorate([
|
|
|
272
372
|
]),
|
|
273
373
|
_ts_metadata("design:returntype", Promise)
|
|
274
374
|
], AuthenticationController.prototype, "changePassword", null);
|
|
275
|
-
_ts_decorate([
|
|
276
|
-
Post('forgot-password'),
|
|
277
|
-
HttpCode(HttpStatus.OK),
|
|
278
|
-
ApiOperation({
|
|
279
|
-
summary: 'Request password reset email'
|
|
280
|
-
}),
|
|
281
|
-
ApiBody({
|
|
282
|
-
type: ForgotPasswordDto
|
|
283
|
-
}),
|
|
284
|
-
ApiResponse({
|
|
285
|
-
status: 200,
|
|
286
|
-
type: MessageResponseDto
|
|
287
|
-
}),
|
|
288
|
-
_ts_param(0, Req()),
|
|
289
|
-
_ts_param(1, Body()),
|
|
290
|
-
_ts_metadata("design:type", Function),
|
|
291
|
-
_ts_metadata("design:paramtypes", [
|
|
292
|
-
typeof Request === "undefined" ? Object : Request,
|
|
293
|
-
typeof ForgotPasswordDto === "undefined" ? Object : ForgotPasswordDto
|
|
294
|
-
]),
|
|
295
|
-
_ts_metadata("design:returntype", Promise)
|
|
296
|
-
], AuthenticationController.prototype, "forgotPassword", null);
|
|
297
|
-
_ts_decorate([
|
|
298
|
-
Post('reset-password'),
|
|
299
|
-
HttpCode(HttpStatus.OK),
|
|
300
|
-
ApiOperation({
|
|
301
|
-
summary: 'Reset password using token'
|
|
302
|
-
}),
|
|
303
|
-
ApiBody({
|
|
304
|
-
type: ResetPasswordDto
|
|
305
|
-
}),
|
|
306
|
-
ApiResponse({
|
|
307
|
-
status: 200,
|
|
308
|
-
type: MessageResponseDto
|
|
309
|
-
}),
|
|
310
|
-
_ts_param(0, Body()),
|
|
311
|
-
_ts_metadata("design:type", Function),
|
|
312
|
-
_ts_metadata("design:paramtypes", [
|
|
313
|
-
typeof ResetPasswordDto === "undefined" ? Object : ResetPasswordDto
|
|
314
|
-
]),
|
|
315
|
-
_ts_metadata("design:returntype", Promise)
|
|
316
|
-
], AuthenticationController.prototype, "resetPassword", null);
|
|
317
|
-
_ts_decorate([
|
|
318
|
-
Post('resend-verification'),
|
|
319
|
-
HttpCode(HttpStatus.OK),
|
|
320
|
-
ApiOperation({
|
|
321
|
-
summary: 'Resend email verification link'
|
|
322
|
-
}),
|
|
323
|
-
ApiBody({
|
|
324
|
-
type: ResendVerificationDto
|
|
325
|
-
}),
|
|
326
|
-
ApiResponse({
|
|
327
|
-
status: 200,
|
|
328
|
-
type: MessageResponseDto
|
|
329
|
-
}),
|
|
330
|
-
_ts_param(0, Req()),
|
|
331
|
-
_ts_param(1, Body()),
|
|
332
|
-
_ts_metadata("design:type", Function),
|
|
333
|
-
_ts_metadata("design:paramtypes", [
|
|
334
|
-
typeof Request === "undefined" ? Object : Request,
|
|
335
|
-
typeof ResendVerificationDto === "undefined" ? Object : ResendVerificationDto
|
|
336
|
-
]),
|
|
337
|
-
_ts_metadata("design:returntype", Promise)
|
|
338
|
-
], AuthenticationController.prototype, "resendVerification", null);
|
|
339
|
-
_ts_decorate([
|
|
340
|
-
Post('verify-email'),
|
|
341
|
-
HttpCode(HttpStatus.OK),
|
|
342
|
-
UseInterceptors(SetToken),
|
|
343
|
-
ApiOperation({
|
|
344
|
-
summary: 'Verify email using token'
|
|
345
|
-
}),
|
|
346
|
-
ApiBody({
|
|
347
|
-
type: VerifyEmailDto
|
|
348
|
-
}),
|
|
349
|
-
ApiResponse({
|
|
350
|
-
status: 200,
|
|
351
|
-
type: MessageResponseDto
|
|
352
|
-
}),
|
|
353
|
-
ApiResponse({
|
|
354
|
-
status: 200,
|
|
355
|
-
type: RegistrationResponseDto
|
|
356
|
-
}),
|
|
357
|
-
_ts_param(0, Body()),
|
|
358
|
-
_ts_metadata("design:type", Function),
|
|
359
|
-
_ts_metadata("design:paramtypes", [
|
|
360
|
-
typeof VerifyEmailDto === "undefined" ? Object : VerifyEmailDto
|
|
361
|
-
]),
|
|
362
|
-
_ts_metadata("design:returntype", Promise)
|
|
363
|
-
], AuthenticationController.prototype, "verifyEmail", null);
|
|
364
375
|
AuthenticationController = _ts_decorate([
|
|
365
376
|
ApiTags('Authentication'),
|
|
366
377
|
Controller('auth'),
|
|
367
378
|
_ts_param(0, Inject(AuthenticationService)),
|
|
368
379
|
_ts_param(1, Optional()),
|
|
369
380
|
_ts_param(1, Inject(AuthConfigService)),
|
|
370
|
-
_ts_param(2, Optional()),
|
|
371
|
-
_ts_param(2, Inject(AuthEmailService)),
|
|
372
381
|
_ts_metadata("design:type", Function),
|
|
373
382
|
_ts_metadata("design:paramtypes", [
|
|
374
383
|
typeof AuthenticationService === "undefined" ? Object : AuthenticationService,
|
|
375
|
-
typeof AuthConfigService === "undefined" ? Object : AuthConfigService
|
|
376
|
-
typeof AuthEmailService === "undefined" ? Object : AuthEmailService
|
|
384
|
+
typeof AuthConfigService === "undefined" ? Object : AuthConfigService
|
|
377
385
|
])
|
|
378
386
|
], AuthenticationController);
|