@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.
Files changed (94) hide show
  1. package/README.md +633 -468
  2. package/cjs/config/auth.constants.js +8 -0
  3. package/cjs/controllers/authentication.controller.js +175 -167
  4. package/cjs/controllers/branch.controller.js +44 -1
  5. package/cjs/controllers/company-selection.controller.js +27 -20
  6. package/cjs/controllers/company.controller.js +44 -1
  7. package/cjs/controllers/email-verification.controller.js +220 -0
  8. package/cjs/controllers/index.js +1 -0
  9. package/cjs/controllers/user-permission.controller.js +82 -154
  10. package/cjs/controllers/user.controller.js +68 -15
  11. package/cjs/docs/auth-swagger.config.js +14 -7
  12. package/cjs/dtos/authentication.dto.js +16 -43
  13. package/cjs/dtos/index.js +0 -1
  14. package/cjs/dtos/user-permission.dto.js +28 -0
  15. package/cjs/dtos/user.dto.js +19 -4
  16. package/cjs/entities/index.js +16 -17
  17. package/cjs/entities/user-company-permission.entity.js +3 -9
  18. package/cjs/interceptors/clear-token.interceptor.js +10 -23
  19. package/cjs/interceptors/set-token.interceptor.js +15 -55
  20. package/cjs/interfaces/auth-email-provider.interface.js +1 -1
  21. package/cjs/modules/auth.module.js +79 -154
  22. package/cjs/services/auth-config.service.js +33 -35
  23. package/cjs/services/auth-datasource.provider.js +18 -42
  24. package/cjs/services/auth-email.service.js +89 -85
  25. package/cjs/services/authentication.service.js +248 -414
  26. package/cjs/services/branch.service.js +9 -4
  27. package/cjs/services/company-selection-session.service.js +1 -1
  28. package/cjs/services/user-permission.service.js +43 -73
  29. package/cjs/services/user.service.js +78 -64
  30. package/cjs/strategies/jwt.strategy.js +44 -44
  31. package/cjs/validators/password.validator.js +45 -0
  32. package/config/auth.constants.d.ts +2 -0
  33. package/controllers/authentication.controller.d.ts +4 -10
  34. package/controllers/company-selection.controller.d.ts +6 -2
  35. package/controllers/email-verification.controller.d.ts +20 -0
  36. package/controllers/index.d.ts +1 -0
  37. package/controllers/user-permission.controller.d.ts +5 -4
  38. package/controllers/user.controller.d.ts +6 -0
  39. package/dtos/authentication.dto.d.ts +0 -4
  40. package/dtos/company-branch.dto.d.ts +0 -1
  41. package/dtos/company.dto.d.ts +0 -1
  42. package/dtos/index.d.ts +0 -1
  43. package/dtos/user-permission.dto.d.ts +4 -0
  44. package/dtos/user.dto.d.ts +1 -3
  45. package/entities/index.d.ts +5 -10
  46. package/entities/user-company-permission.entity.d.ts +1 -3
  47. package/fesm/config/auth.constants.js +6 -13
  48. package/fesm/controllers/authentication.controller.js +178 -170
  49. package/fesm/controllers/branch.controller.js +45 -2
  50. package/fesm/controllers/company-selection.controller.js +28 -21
  51. package/fesm/controllers/company.controller.js +45 -2
  52. package/fesm/controllers/email-verification.controller.js +210 -0
  53. package/fesm/controllers/index.js +1 -0
  54. package/fesm/controllers/user-permission.controller.js +84 -156
  55. package/fesm/controllers/user.controller.js +71 -18
  56. package/fesm/docs/auth-swagger.config.js +14 -7
  57. package/fesm/dtos/authentication.dto.js +17 -44
  58. package/fesm/dtos/index.js +0 -1
  59. package/fesm/dtos/user-permission.dto.js +28 -0
  60. package/fesm/dtos/user.dto.js +20 -5
  61. package/fesm/entities/index.js +17 -9
  62. package/fesm/entities/user-company-permission.entity.js +3 -9
  63. package/fesm/interceptors/clear-token.interceptor.js +9 -22
  64. package/fesm/interceptors/set-token.interceptor.js +14 -54
  65. package/fesm/interfaces/auth-email-provider.interface.js +1 -1
  66. package/fesm/modules/auth.module.js +82 -157
  67. package/fesm/services/auth-config.service.js +34 -36
  68. package/fesm/services/auth-datasource.provider.js +19 -43
  69. package/fesm/services/auth-email.service.js +90 -86
  70. package/fesm/services/authentication.service.js +248 -414
  71. package/fesm/services/branch.service.js +9 -4
  72. package/fesm/services/company-selection-session.service.js +1 -1
  73. package/fesm/services/user-permission.service.js +44 -74
  74. package/fesm/services/user.service.js +79 -65
  75. package/fesm/strategies/jwt.strategy.js +44 -44
  76. package/fesm/validators/password.validator.js +48 -0
  77. package/interceptors/clear-token.interceptor.d.ts +0 -1
  78. package/interceptors/set-token.interceptor.d.ts +1 -1
  79. package/interfaces/authentication.interface.d.ts +0 -4
  80. package/interfaces/user-enricher.interface.d.ts +1 -4
  81. package/modules/auth.module.d.ts +9 -4
  82. package/package.json +4 -4
  83. package/services/auth-config.service.d.ts +7 -8
  84. package/services/auth-datasource.provider.d.ts +3 -4
  85. package/services/auth-email.service.d.ts +8 -4
  86. package/services/authentication.service.d.ts +16 -16
  87. package/services/company-selection-session.service.d.ts +1 -1
  88. package/services/user-permission.service.d.ts +6 -9
  89. package/services/user.service.d.ts +3 -0
  90. package/strategies/jwt.strategy.d.ts +4 -1
  91. package/validators/password.validator.d.ts +10 -0
  92. package/cjs/dtos/user-company-permission.dto.js +0 -110
  93. package/dtos/user-company-permission.dto.d.ts +0 -10
  94. package/fesm/dtos/user-company-permission.dto.js +0 -98
@@ -1,5 +1,6 @@
1
1
  export * from './authentication.controller';
2
2
  export * from './company-selection.controller';
3
+ export * from './email-verification.controller';
3
4
  export * from './user.controller';
4
5
  export * from './company.controller';
5
6
  export * from './branch.controller';
@@ -1,8 +1,8 @@
1
- import { AssignUserCompanyDto, AssignUserBranchDto, UserCompaniesResponseDto, UserBranchesResponseDto } from '../dtos/user-permission.dto';
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 userPermissionService;
5
- constructor(userPermissionService: UserPermissionService);
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 {
@@ -1,5 +1,4 @@
1
1
  export declare class CreateCompanyBranchDto {
2
- [key: string]: unknown;
3
2
  name: string;
4
3
  slug: string;
5
4
  companyId: string;
@@ -1,5 +1,4 @@
1
1
  export declare class CreateCompanyDto {
2
- [key: string]: unknown;
3
2
  name: string;
4
3
  slug: string;
5
4
  logoId?: string;
package/dtos/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export * from './authentication.dto';
2
2
  export * from './company-branch.dto';
3
3
  export * from './company.dto';
4
- export * from './user-company-permission.dto';
5
4
  export * from './user.dto';
6
5
  export * from './user-permission.dto';
@@ -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
  }
@@ -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;
@@ -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
- import { CompanyBranch } from './company-branch.entity';
8
- import { Company } from './company.entity';
9
- import { PendingRegistration } from './pending-registration.entity';
10
- import { UserCompanyPermission } from './user-company-permission.entity';
11
- import { User } from './user.entity';
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: 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
- * User Enricher Provider Token
5
- *
6
- * Optional provider for extending user list and profile.
7
- * Consuming apps can provide their own implementation.
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, Get, HttpCode, HttpStatus, Inject, Optional, Post, Req, UnauthorizedException, UseGuards, UseInterceptors } from '@nestjs/common';
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 { ChangePasswordDto, CompanySelectionResponseDto, ForgotPasswordDto, LoginDto, LoginResponseDto, MeResponseDto, RefreshTokenDto, RegistrationDto, RegistrationResponseDto, ResendVerificationDto, ResetPasswordDto, VerifyEmailDto } from '../dtos/authentication.dto';
35
- import { MessageResponseDto } from '@flusys/nestjs-shared/dtos';
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
- /** Build auth URL for email links */ buildAuthUrl(req, path) {
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
- // Core Endpoints
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
- /** Refresh access token. Browser uses HTTP-only cookie, API/Mobile uses request body. */ async refreshToken(req, refreshTokenDto) {
63
- // Get refresh token from cookie (browser) or body (API/mobile)
64
- const cookieName = this.authConfig?.getRefreshTokenCookieName() ?? 'fsn_refresh_token';
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
- /** Change password */ async changePassword(userId, dto) {
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
- // Email Auth Endpoints
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
- type: LoginResponseDto
160
- }),
161
- ApiResponse({
162
- status: 200,
163
- type: CompanySelectionResponseDto
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
- Get('me'),
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);