@flusys/nestjs-auth 4.1.0 → 5.0.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 +151 -719
- package/cjs/config/message-keys.js +6 -61
- package/cjs/controllers/authentication.controller.js +113 -87
- package/cjs/controllers/company-selection.controller.js +24 -4
- package/cjs/controllers/email-verification.controller.js +12 -13
- package/cjs/docs/auth-swagger.config.js +143 -106
- package/cjs/dtos/authentication.dto.js +0 -15
- package/cjs/entities/user-company-permission.entity.js +0 -8
- package/cjs/entities/user-token.entity.js +0 -9
- package/cjs/interceptors/set-token.interceptor.js +15 -7
- package/cjs/services/auth-config.service.js +0 -4
- package/cjs/services/auth-email.service.js +8 -31
- package/cjs/services/authentication.service.js +99 -15
- package/cjs/services/user-permission.service.js +6 -8
- package/cjs/services/user.service.js +12 -9
- package/cjs/strategies/jwt.strategy.js +10 -3
- package/config/message-keys.d.ts +5 -153
- package/controllers/authentication.controller.d.ts +6 -7
- package/controllers/company-selection.controller.d.ts +3 -2
- package/controllers/email-verification.controller.d.ts +6 -2
- package/dtos/authentication.dto.d.ts +0 -2
- package/entities/user-company-permission.entity.d.ts +0 -1
- package/entities/user-token.entity.d.ts +0 -1
- package/fesm/config/message-keys.js +6 -53
- package/fesm/controllers/authentication.controller.js +115 -90
- package/fesm/controllers/company-selection.controller.js +25 -5
- package/fesm/controllers/email-verification.controller.js +13 -14
- package/fesm/docs/auth-swagger.config.js +146 -113
- package/fesm/dtos/authentication.dto.js +0 -15
- package/fesm/entities/user-company-permission.entity.js +0 -8
- package/fesm/entities/user-token.entity.js +0 -9
- package/fesm/interceptors/set-token.interceptor.js +15 -7
- package/fesm/services/auth-config.service.js +0 -4
- package/fesm/services/auth-email.service.js +8 -32
- package/fesm/services/authentication.service.js +100 -17
- package/fesm/services/user-permission.service.js +6 -8
- package/fesm/services/user.service.js +12 -10
- package/fesm/strategies/jwt.strategy.js +10 -3
- package/interfaces/auth-module-options.interface.d.ts +0 -1
- package/package.json +3 -3
- package/services/auth-config.service.d.ts +0 -1
- package/services/auth-email.service.d.ts +0 -4
- package/services/authentication.service.d.ts +1 -1
- package/services/user-permission.service.d.ts +1 -1
- package/services/user.service.d.ts +5 -5
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
// ==================== AUTH MODULE MESSAGE KEYS ====================
|
|
2
2
|
export const AUTH_MESSAGES = {
|
|
3
3
|
LOGIN_SUCCESS: 'auth.login.success',
|
|
4
|
-
|
|
4
|
+
LOGIN_REQUIRES_SELECTION: 'auth.login.requires.selection',
|
|
5
5
|
LOGIN_INVALID_CREDENTIALS: 'auth.login.invalid.credentials',
|
|
6
6
|
LOGIN_EMAIL_NOT_VERIFIED: 'auth.login.email.not.verified',
|
|
7
|
-
LOGIN_REQUIRES_SELECTION: 'auth.login.requires.selection',
|
|
8
7
|
LOGIN_ACCOUNT_DEACTIVATED: 'auth.login.account.deactivated',
|
|
9
8
|
LOGOUT_SUCCESS: 'auth.logout.success',
|
|
10
9
|
REGISTER_SUCCESS: 'auth.register.success',
|
|
11
10
|
REGISTER_FAILED: 'auth.register.failed',
|
|
11
|
+
REFRESH_SUCCESS: 'auth.refresh.success',
|
|
12
|
+
ME_SUCCESS: 'auth.me.success',
|
|
13
|
+
SELECT_SUCCESS: 'auth.select.success',
|
|
14
|
+
SWITCH_COMPANY_SUCCESS: 'auth.switch.company.success',
|
|
12
15
|
REGISTER_EMAIL_EXISTS: 'auth.register.email.exists',
|
|
13
16
|
REGISTER_COMPANY_ALREADY_ASSIGNED: 'auth.register.company.already.assigned',
|
|
14
17
|
PASSWORD_CHANGE_SUCCESS: 'auth.password.change.success',
|
|
15
|
-
PASSWORD_CHANGE_FAILED: 'auth.password.change.failed',
|
|
16
18
|
PASSWORD_CHANGE_INVALID_CURRENT: 'auth.password.change.invalid.current',
|
|
17
19
|
PASSWORD_CHANGE_CURRENT_REQUIRED: 'auth.password.change.current.required',
|
|
18
20
|
PASSWORD_RESET_EMAIL_SENT: 'auth.password.reset.email.sent',
|
|
19
21
|
PASSWORD_RESET_SUCCESS: 'auth.password.reset.success',
|
|
20
|
-
PASSWORD_RESET_FAILED: 'auth.password.reset.failed',
|
|
21
|
-
TOKEN_REFRESH_SUCCESS: 'auth.token.refresh.success',
|
|
22
22
|
TOKEN_REQUIRED: 'auth.token.required',
|
|
23
23
|
TOKEN_INVALID: 'auth.token.invalid',
|
|
24
24
|
TOKEN_EXPIRED: 'auth.token.expired',
|
|
25
25
|
TOKEN_REVOKED: 'auth.token.revoked',
|
|
26
26
|
TOKEN_TOO_LARGE: 'auth.token.too.large',
|
|
27
|
-
COMPANY_SELECT_SUCCESS: 'auth.company.select.success',
|
|
28
|
-
COMPANY_SWITCH_SUCCESS: 'auth.company.switch.success',
|
|
29
27
|
COMPANY_LIST_SUCCESS: 'auth.company.list.success',
|
|
30
28
|
COMPANY_NO_ACCESS: 'auth.company.no.access',
|
|
31
29
|
COMPANY_NOT_FOUND: 'auth.company.not.found',
|
|
@@ -34,7 +32,6 @@ export const AUTH_MESSAGES = {
|
|
|
34
32
|
BRANCH_LIST_SUCCESS: 'auth.branch.list.success',
|
|
35
33
|
BRANCH_NO_ACCESS: 'auth.branch.no.access',
|
|
36
34
|
BRANCH_NOT_FOUND: 'auth.branch.not.found',
|
|
37
|
-
ME_SUCCESS: 'auth.me.success',
|
|
38
35
|
EMAIL_NOT_CONFIGURED: 'auth.email.not.configured',
|
|
39
36
|
EMAIL_SEND_FAILED: 'auth.email.send.failed',
|
|
40
37
|
SESSION_INVALID: 'auth.session.invalid',
|
|
@@ -48,24 +45,13 @@ export const AUTH_MESSAGES = {
|
|
|
48
45
|
export const EMAIL_VERIFICATION_MESSAGES = {
|
|
49
46
|
VERIFICATION_SENT: 'email.verification.sent',
|
|
50
47
|
VERIFICATION_SUCCESS: 'email.verification.success',
|
|
51
|
-
VERIFICATION_FAILED: 'email.verification.failed',
|
|
52
48
|
VERIFICATION_TOKEN_INVALID: 'email.verification.token.invalid',
|
|
53
49
|
VERIFICATION_TOKEN_EXPIRED: 'email.verification.token.expired',
|
|
54
50
|
VERIFICATION_NOT_ENABLED: 'email.verification.not.enabled',
|
|
55
|
-
RESEND_SUCCESS: 'email.verification.resend.success'
|
|
56
|
-
RESEND_THROTTLED: 'email.verification.resend.throttled'
|
|
51
|
+
RESEND_SUCCESS: 'email.verification.resend.success'
|
|
57
52
|
};
|
|
58
53
|
export const USER_MESSAGES = {
|
|
59
|
-
CREATE_SUCCESS: 'user.create.success',
|
|
60
|
-
CREATE_MANY_SUCCESS: 'user.create.many.success',
|
|
61
|
-
GET_SUCCESS: 'user.get.success',
|
|
62
|
-
GET_ALL_SUCCESS: 'user.get.all.success',
|
|
63
|
-
UPDATE_SUCCESS: 'user.update.success',
|
|
64
|
-
UPDATE_MANY_SUCCESS: 'user.update.many.success',
|
|
65
|
-
DELETE_SUCCESS: 'user.delete.success',
|
|
66
|
-
RESTORE_SUCCESS: 'user.restore.success',
|
|
67
54
|
NOT_FOUND: 'user.not.found',
|
|
68
|
-
PROFILE_SUCCESS: 'user.profile.success',
|
|
69
55
|
PROFILE_UPDATE_SUCCESS: 'user.profile.update.success',
|
|
70
56
|
PROFILE_EXTRAS_SUCCESS: 'user.profile.extras.success',
|
|
71
57
|
PROFILE_SECTIONS_SUCCESS: 'user.profile.sections.success',
|
|
@@ -76,43 +62,10 @@ export const USER_MESSAGES = {
|
|
|
76
62
|
PHONE_VERIFY_SUCCESS: 'user.phone.verify.success',
|
|
77
63
|
STATUS_UPDATE_SUCCESS: 'user.status.update.success'
|
|
78
64
|
};
|
|
79
|
-
export const COMPANY_MESSAGES = {
|
|
80
|
-
CREATE_SUCCESS: 'company.create.success',
|
|
81
|
-
CREATE_MANY_SUCCESS: 'company.create.many.success',
|
|
82
|
-
GET_SUCCESS: 'company.get.success',
|
|
83
|
-
GET_ALL_SUCCESS: 'company.get.all.success',
|
|
84
|
-
UPDATE_SUCCESS: 'company.update.success',
|
|
85
|
-
UPDATE_MANY_SUCCESS: 'company.update.many.success',
|
|
86
|
-
DELETE_SUCCESS: 'company.delete.success',
|
|
87
|
-
RESTORE_SUCCESS: 'company.restore.success',
|
|
88
|
-
NOT_FOUND: 'company.not.found'
|
|
89
|
-
};
|
|
90
|
-
export const BRANCH_MESSAGES = {
|
|
91
|
-
CREATE_SUCCESS: 'branch.create.success',
|
|
92
|
-
CREATE_MANY_SUCCESS: 'branch.create.many.success',
|
|
93
|
-
GET_SUCCESS: 'branch.get.success',
|
|
94
|
-
GET_ALL_SUCCESS: 'branch.get.all.success',
|
|
95
|
-
UPDATE_SUCCESS: 'branch.update.success',
|
|
96
|
-
UPDATE_MANY_SUCCESS: 'branch.update.many.success',
|
|
97
|
-
DELETE_SUCCESS: 'branch.delete.success',
|
|
98
|
-
RESTORE_SUCCESS: 'branch.restore.success',
|
|
99
|
-
NOT_FOUND: 'branch.not.found'
|
|
100
|
-
};
|
|
101
65
|
export const USER_PERMISSION_MESSAGES = {
|
|
102
|
-
COMPANY_ASSIGN_SUCCESS: 'user.permission.company.assign.success',
|
|
103
66
|
COMPANY_LIST_SUCCESS: 'user.permission.company.list.success',
|
|
104
|
-
BRANCH_ASSIGN_SUCCESS: 'user.permission.branch.assign.success',
|
|
105
67
|
BRANCH_LIST_SUCCESS: 'user.permission.branch.list.success',
|
|
106
68
|
BATCH_SUCCESS: 'user.permission.batch.success',
|
|
107
69
|
ALREADY_ASSIGNED: 'user.permission.already.assigned',
|
|
108
70
|
PERMISSION_NOT_FOUND: 'user.permission.not.found'
|
|
109
71
|
};
|
|
110
|
-
// Aggregated export for backward compatibility
|
|
111
|
-
export const AUTH_MODULE_MESSAGES = {
|
|
112
|
-
AUTH: AUTH_MESSAGES,
|
|
113
|
-
EMAIL_VERIFICATION: EMAIL_VERIFICATION_MESSAGES,
|
|
114
|
-
USER: USER_MESSAGES,
|
|
115
|
-
COMPANY: COMPANY_MESSAGES,
|
|
116
|
-
BRANCH: BRANCH_MESSAGES,
|
|
117
|
-
USER_PERMISSION: USER_PERMISSION_MESSAGES
|
|
118
|
-
};
|
|
@@ -25,40 +25,56 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import {
|
|
28
|
+
import { buildFrontendUrl } from '@flusys/nestjs-shared';
|
|
29
29
|
import { CurrentUser } from '@flusys/nestjs-shared/decorators';
|
|
30
30
|
import { MessageResponseDto } from '@flusys/nestjs-shared/dtos';
|
|
31
31
|
import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
|
|
32
32
|
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
33
33
|
import { Body, Controller, HttpCode, HttpStatus, Inject, Optional, Post, Req, UnauthorizedException, UseGuards, UseInterceptors } from '@nestjs/common';
|
|
34
|
-
import { ApiBearerAuth, ApiBody,
|
|
34
|
+
import { ApiBearerAuth, ApiBody, ApiHeader, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
|
35
35
|
import { SkipThrottle, Throttle } from '@nestjs/throttler';
|
|
36
36
|
import { Request } from 'express';
|
|
37
|
-
import { DEFAULT_COOKIE_NAME } from '../config';
|
|
38
|
-
import { ChangePasswordDto,
|
|
37
|
+
import { AUTH_MESSAGES, DEFAULT_COOKIE_NAME, EMAIL_VERIFICATION_MESSAGES } from '../config';
|
|
38
|
+
import { ChangePasswordDto, LoginDto, RefreshTokenDto, RegistrationDto } from '../dtos/authentication.dto';
|
|
39
39
|
import { ClearToken } from '../interceptors/clear-token.interceptor';
|
|
40
40
|
import { SetToken } from '../interceptors/set-token.interceptor';
|
|
41
41
|
import { AuthConfigService } from '../services/auth-config.service';
|
|
42
42
|
import { AuthenticationService } from '../services/authentication.service';
|
|
43
43
|
export class AuthenticationController {
|
|
44
|
-
buildAuthUrl(req, path) {
|
|
45
|
-
const frontendUrl = this.authConfig?.getFrontendUrl();
|
|
46
|
-
return frontendUrl ? `${frontendUrl}${path}` : `${req.protocol}://${req.get('host')}${path}`;
|
|
47
|
-
}
|
|
48
44
|
async login(loginDto) {
|
|
49
45
|
const result = await this.authService.login(loginDto.email, loginDto.password);
|
|
50
46
|
if ('requiresEmailVerification' in result) {
|
|
51
|
-
return
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
message: 'Email verification required',
|
|
50
|
+
messageKey: AUTH_MESSAGES.LOGIN_EMAIL_NOT_VERIFIED,
|
|
51
|
+
data: result
|
|
52
|
+
};
|
|
52
53
|
}
|
|
53
54
|
if ('requiresSelection' in result) {
|
|
54
|
-
return
|
|
55
|
+
return {
|
|
56
|
+
success: true,
|
|
57
|
+
message: 'Company selection required',
|
|
58
|
+
messageKey: AUTH_MESSAGES.LOGIN_REQUIRES_SELECTION,
|
|
59
|
+
data: result
|
|
60
|
+
};
|
|
55
61
|
}
|
|
56
|
-
return
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
message: 'Login successful',
|
|
65
|
+
messageKey: AUTH_MESSAGES.LOGIN_SUCCESS,
|
|
66
|
+
data: result
|
|
67
|
+
};
|
|
57
68
|
}
|
|
58
69
|
/** Register new user. If company feature enabled, can join/create company. */ async register(req, registrationDto) {
|
|
59
|
-
const verifyUrlBase =
|
|
70
|
+
const verifyUrlBase = buildFrontendUrl(req, '/auth/verify-email');
|
|
60
71
|
const result = await this.authService.register(registrationDto, verifyUrlBase);
|
|
61
|
-
return
|
|
72
|
+
return {
|
|
73
|
+
success: true,
|
|
74
|
+
message: result.requiresEmailVerification ? 'Verification email sent' : 'Registration successful',
|
|
75
|
+
messageKey: result.requiresEmailVerification ? EMAIL_VERIFICATION_MESSAGES.VERIFICATION_SENT : AUTH_MESSAGES.REGISTER_SUCCESS,
|
|
76
|
+
data: result
|
|
77
|
+
};
|
|
62
78
|
}
|
|
63
79
|
/**
|
|
64
80
|
* Refresh access token.
|
|
@@ -74,10 +90,22 @@ export class AuthenticationController {
|
|
|
74
90
|
messageKey: AUTH_MESSAGES.TOKEN_REQUIRED
|
|
75
91
|
});
|
|
76
92
|
}
|
|
77
|
-
|
|
93
|
+
const data = await this.authService.refreshToken(refreshToken);
|
|
94
|
+
return {
|
|
95
|
+
success: true,
|
|
96
|
+
message: 'Token refreshed successfully',
|
|
97
|
+
messageKey: AUTH_MESSAGES.REFRESH_SUCCESS,
|
|
98
|
+
data
|
|
99
|
+
};
|
|
78
100
|
}
|
|
79
101
|
/** Get current user info. Returns user, company, and branch data (no tokens). */ async getCurrentUser(user) {
|
|
80
|
-
|
|
102
|
+
const data = await this.authService.getUserInfo(user.id, user.companyId, user.branchId);
|
|
103
|
+
return {
|
|
104
|
+
success: true,
|
|
105
|
+
message: 'User info retrieved successfully',
|
|
106
|
+
messageKey: AUTH_MESSAGES.ME_SUCCESS,
|
|
107
|
+
data
|
|
108
|
+
};
|
|
81
109
|
}
|
|
82
110
|
/** Logout - Clears refresh token cookie for browsers */ async logout(userId) {
|
|
83
111
|
await this.authService.logout(userId);
|
|
@@ -118,97 +146,93 @@ _ts_decorate([
|
|
|
118
146
|
ApiBody({
|
|
119
147
|
type: LoginDto
|
|
120
148
|
}),
|
|
121
|
-
ApiExtraModels(LoginResponseDto, CompanySelectionResponseDto, LoginResponseRequiresEmailVerificationDto),
|
|
122
149
|
ApiResponse({
|
|
123
150
|
status: 200,
|
|
124
151
|
description: 'Login successful, requires company selection, or requires email verification',
|
|
125
152
|
content: {
|
|
126
153
|
'application/json': {
|
|
127
|
-
schema: {
|
|
128
|
-
oneOf: [
|
|
129
|
-
{
|
|
130
|
-
$ref: getSchemaPath(LoginResponseDto)
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
$ref: getSchemaPath(CompanySelectionResponseDto)
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
$ref: getSchemaPath(LoginResponseRequiresEmailVerificationDto)
|
|
137
|
-
}
|
|
138
|
-
]
|
|
139
|
-
},
|
|
140
154
|
examples: {
|
|
141
155
|
loginSuccess: {
|
|
142
156
|
summary: 'Login Success',
|
|
143
|
-
description: 'Successful login with tokens (company/branch included when company feature enabled)',
|
|
144
157
|
value: {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
158
|
+
success: true,
|
|
159
|
+
message: 'Login successful',
|
|
160
|
+
messageKey: 'auth.login.success',
|
|
161
|
+
data: {
|
|
162
|
+
accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
|
|
163
|
+
user: {
|
|
164
|
+
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
165
|
+
email: 'john@example.com',
|
|
166
|
+
name: 'John Doe'
|
|
167
|
+
}
|
|
151
168
|
}
|
|
152
169
|
}
|
|
153
170
|
},
|
|
154
171
|
loginSuccessWithCompany: {
|
|
155
|
-
summary: 'Login Success (
|
|
156
|
-
description: 'Login with company/branch - only when company feature is enabled',
|
|
172
|
+
summary: 'Login Success (with company/branch)',
|
|
157
173
|
value: {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
+
success: true,
|
|
175
|
+
message: 'Login successful',
|
|
176
|
+
messageKey: 'auth.login.success',
|
|
177
|
+
data: {
|
|
178
|
+
accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
|
|
179
|
+
user: {
|
|
180
|
+
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
181
|
+
email: 'john@example.com',
|
|
182
|
+
name: 'John Doe'
|
|
183
|
+
},
|
|
184
|
+
company: {
|
|
185
|
+
id: 'company-id',
|
|
186
|
+
name: 'Acme Corp'
|
|
187
|
+
},
|
|
188
|
+
branch: {
|
|
189
|
+
id: 'branch-id',
|
|
190
|
+
name: 'Main Branch'
|
|
191
|
+
}
|
|
174
192
|
}
|
|
175
193
|
}
|
|
176
194
|
},
|
|
177
195
|
companySelection: {
|
|
178
|
-
summary: 'Company Selection Required
|
|
179
|
-
description: 'User has multiple companies/branches - only when company feature is enabled',
|
|
196
|
+
summary: 'Company Selection Required',
|
|
180
197
|
value: {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
198
|
+
success: true,
|
|
199
|
+
message: 'Company selection required',
|
|
200
|
+
messageKey: 'auth.login.requires.selection',
|
|
201
|
+
data: {
|
|
202
|
+
requiresSelection: true,
|
|
203
|
+
sessionId: 'sess_abc123def456',
|
|
204
|
+
expiresAt: '2024-12-18T15:30:00.000Z',
|
|
205
|
+
user: {
|
|
206
|
+
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
207
|
+
email: 'john@example.com'
|
|
208
|
+
},
|
|
209
|
+
companies: [
|
|
210
|
+
{
|
|
211
|
+
id: 'company-id',
|
|
212
|
+
name: 'Acme Corp',
|
|
213
|
+
slug: 'acme-corp',
|
|
214
|
+
branches: [
|
|
215
|
+
{
|
|
216
|
+
id: 'branch-id',
|
|
217
|
+
name: 'Main Branch',
|
|
218
|
+
slug: 'main-branch'
|
|
219
|
+
}
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
]
|
|
223
|
+
}
|
|
203
224
|
}
|
|
204
225
|
},
|
|
205
226
|
emailVerification: {
|
|
206
227
|
summary: 'Email Verification Required',
|
|
207
|
-
description: 'User must verify email before logging in',
|
|
208
228
|
value: {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
229
|
+
success: true,
|
|
230
|
+
message: 'Email verification required',
|
|
231
|
+
messageKey: 'auth.login.email.not.verified',
|
|
232
|
+
data: {
|
|
233
|
+
requiresEmailVerification: true,
|
|
234
|
+
email: 'john@example.com'
|
|
235
|
+
}
|
|
212
236
|
}
|
|
213
237
|
}
|
|
214
238
|
}
|
|
@@ -240,7 +264,7 @@ _ts_decorate([
|
|
|
240
264
|
}),
|
|
241
265
|
ApiResponse({
|
|
242
266
|
status: 201,
|
|
243
|
-
|
|
267
|
+
description: 'Registration successful or email verification required'
|
|
244
268
|
}),
|
|
245
269
|
_ts_param(0, Req()),
|
|
246
270
|
_ts_param(1, Body()),
|
|
@@ -284,13 +308,14 @@ Set header \`x-client-type: mobile\` or \`x-client-type: api\` to identify as no
|
|
|
284
308
|
}),
|
|
285
309
|
ApiResponse({
|
|
286
310
|
status: 200,
|
|
287
|
-
description: '
|
|
311
|
+
description: 'Token refreshed successfully',
|
|
288
312
|
schema: {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
313
|
+
example: {
|
|
314
|
+
success: true,
|
|
315
|
+
message: 'Token refreshed successfully',
|
|
316
|
+
messageKey: 'auth.refresh.success',
|
|
317
|
+
data: {
|
|
318
|
+
accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
|
|
294
319
|
}
|
|
295
320
|
}
|
|
296
321
|
}
|
|
@@ -318,7 +343,7 @@ _ts_decorate([
|
|
|
318
343
|
}),
|
|
319
344
|
ApiResponse({
|
|
320
345
|
status: 200,
|
|
321
|
-
|
|
346
|
+
description: 'Current user info retrieved successfully'
|
|
322
347
|
}),
|
|
323
348
|
_ts_param(0, CurrentUser()),
|
|
324
349
|
_ts_metadata("design:type", Function),
|
|
@@ -29,16 +29,28 @@ import { AUTH_MESSAGES } from '../config';
|
|
|
29
29
|
import { CurrentUser } from '@flusys/nestjs-shared/decorators';
|
|
30
30
|
import { Body, Controller, HttpCode, HttpStatus, Inject, Post, UseGuards, UseInterceptors } from '@nestjs/common';
|
|
31
31
|
import { ApiBearerAuth, ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
|
32
|
-
import {
|
|
32
|
+
import { SelectDto, SwitchCompanyDto } from '../dtos/authentication.dto';
|
|
33
33
|
import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
|
|
34
34
|
import { SetToken } from '../interceptors/set-token.interceptor';
|
|
35
35
|
import { AuthenticationService } from '../services/authentication.service';
|
|
36
36
|
export class CompanySelectionController {
|
|
37
37
|
async select(dto) {
|
|
38
|
-
|
|
38
|
+
const data = await this.authService.select(dto.sessionId, dto.companyId, dto.branchId);
|
|
39
|
+
return {
|
|
40
|
+
success: true,
|
|
41
|
+
message: 'Company selected successfully',
|
|
42
|
+
messageKey: AUTH_MESSAGES.SELECT_SUCCESS,
|
|
43
|
+
data
|
|
44
|
+
};
|
|
39
45
|
}
|
|
40
46
|
async switchCompany(userId, dto) {
|
|
41
|
-
|
|
47
|
+
const data = await this.authService.switchCompany(userId, dto.companyId, dto.branchId);
|
|
48
|
+
return {
|
|
49
|
+
success: true,
|
|
50
|
+
message: 'Company switched successfully',
|
|
51
|
+
messageKey: AUTH_MESSAGES.SWITCH_COMPANY_SUCCESS,
|
|
52
|
+
data
|
|
53
|
+
};
|
|
42
54
|
}
|
|
43
55
|
async getUserCompanies(userId) {
|
|
44
56
|
const companies = await this.authService.getUserCompanies(userId);
|
|
@@ -76,7 +88,7 @@ _ts_decorate([
|
|
|
76
88
|
}),
|
|
77
89
|
ApiResponse({
|
|
78
90
|
status: 200,
|
|
79
|
-
|
|
91
|
+
description: 'Company selected, tokens returned'
|
|
80
92
|
}),
|
|
81
93
|
_ts_param(0, Body()),
|
|
82
94
|
_ts_metadata("design:type", Function),
|
|
@@ -100,7 +112,7 @@ _ts_decorate([
|
|
|
100
112
|
}),
|
|
101
113
|
ApiResponse({
|
|
102
114
|
status: 200,
|
|
103
|
-
|
|
115
|
+
description: 'Company switched, new tokens returned'
|
|
104
116
|
}),
|
|
105
117
|
_ts_param(0, CurrentUser('id')),
|
|
106
118
|
_ts_param(1, Body()),
|
|
@@ -120,6 +132,10 @@ _ts_decorate([
|
|
|
120
132
|
summary: "Get user's available companies",
|
|
121
133
|
description: 'Get list of companies the current user has access to'
|
|
122
134
|
}),
|
|
135
|
+
ApiResponse({
|
|
136
|
+
status: 200,
|
|
137
|
+
description: 'User companies retrieved successfully'
|
|
138
|
+
}),
|
|
123
139
|
_ts_param(0, CurrentUser('id')),
|
|
124
140
|
_ts_metadata("design:type", Function),
|
|
125
141
|
_ts_metadata("design:paramtypes", [
|
|
@@ -148,6 +164,10 @@ _ts_decorate([
|
|
|
148
164
|
]
|
|
149
165
|
}
|
|
150
166
|
}),
|
|
167
|
+
ApiResponse({
|
|
168
|
+
status: 200,
|
|
169
|
+
description: 'Company branches retrieved successfully'
|
|
170
|
+
}),
|
|
151
171
|
_ts_param(0, CurrentUser('id')),
|
|
152
172
|
_ts_param(1, Body('companyId')),
|
|
153
173
|
_ts_metadata("design:type", Function),
|
|
@@ -25,13 +25,14 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import {
|
|
28
|
+
import { buildFrontendUrl } from '@flusys/nestjs-shared';
|
|
29
29
|
import { MessageResponseDto } from '@flusys/nestjs-shared/dtos';
|
|
30
30
|
import { BadRequestException, Body, Controller, HttpCode, HttpStatus, Inject, Post, Req, UseInterceptors } from '@nestjs/common';
|
|
31
31
|
import { ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
|
32
32
|
import { Throttle } from '@nestjs/throttler';
|
|
33
33
|
import { Request } from 'express';
|
|
34
|
-
import {
|
|
34
|
+
import { AUTH_MESSAGES, EMAIL_VERIFICATION_MESSAGES } from '../config';
|
|
35
|
+
import { ForgotPasswordDto, ResendVerificationDto, ResetPasswordDto, VerifyEmailDto } from '../dtos/authentication.dto';
|
|
35
36
|
import { SetToken } from '../interceptors/set-token.interceptor';
|
|
36
37
|
import { AuthConfigService } from '../services/auth-config.service';
|
|
37
38
|
import { AuthDataSourceProvider } from '../services/auth-datasource.provider';
|
|
@@ -46,13 +47,9 @@ export class EmailVerificationController {
|
|
|
46
47
|
});
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
|
-
buildAuthUrl(req, path) {
|
|
50
|
-
const frontendUrl = this.authConfig.getFrontendUrl();
|
|
51
|
-
return frontendUrl ? `${frontendUrl}${path}` : `${req.protocol}://${req.get('host')}${path}`;
|
|
52
|
-
}
|
|
53
50
|
async forgotPassword(req, dto) {
|
|
54
51
|
this.ensureEmailVerificationEnabled();
|
|
55
|
-
return this.authEmailService.forgotPassword(dto.email,
|
|
52
|
+
return this.authEmailService.forgotPassword(dto.email, buildFrontendUrl(req, '/auth/reset-password'));
|
|
56
53
|
}
|
|
57
54
|
async resetPassword(dto) {
|
|
58
55
|
this.ensureEmailVerificationEnabled();
|
|
@@ -60,13 +57,19 @@ export class EmailVerificationController {
|
|
|
60
57
|
}
|
|
61
58
|
async resendVerification(req, dto) {
|
|
62
59
|
this.ensureEmailVerificationEnabled();
|
|
63
|
-
return this.authEmailService.resendVerificationEmail(dto.email,
|
|
60
|
+
return this.authEmailService.resendVerificationEmail(dto.email, buildFrontendUrl(req, '/auth/verify-email'));
|
|
64
61
|
}
|
|
65
62
|
async verifyEmail(dto) {
|
|
66
63
|
this.ensureEmailVerificationEnabled();
|
|
67
64
|
const result = await this.authEmailService.verifyEmail(dto.token);
|
|
68
65
|
if (result.type === 'pending') {
|
|
69
|
-
|
|
66
|
+
const data = await this.authService.createUserFromPendingRegistration(result.pending);
|
|
67
|
+
return {
|
|
68
|
+
success: true,
|
|
69
|
+
message: 'Registration completed successfully',
|
|
70
|
+
messageKey: AUTH_MESSAGES.REGISTER_SUCCESS,
|
|
71
|
+
data
|
|
72
|
+
};
|
|
70
73
|
}
|
|
71
74
|
return {
|
|
72
75
|
success: true,
|
|
@@ -185,11 +188,7 @@ _ts_decorate([
|
|
|
185
188
|
}),
|
|
186
189
|
ApiResponse({
|
|
187
190
|
status: 200,
|
|
188
|
-
|
|
189
|
-
}),
|
|
190
|
-
ApiResponse({
|
|
191
|
-
status: 200,
|
|
192
|
-
type: RegistrationResponseDto
|
|
191
|
+
description: 'Email verified or registration completed'
|
|
193
192
|
}),
|
|
194
193
|
_ts_param(0, Body()),
|
|
195
194
|
_ts_metadata("design:type", Function),
|