@flusys/nestjs-auth 1.0.0-beta → 1.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 +590 -473
- package/cjs/config/auth.constants.js +12 -0
- package/cjs/controllers/authentication.controller.js +365 -24
- 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/user-permission.controller.js +82 -154
- package/cjs/controllers/user.controller.js +211 -7
- package/cjs/docs/auth-swagger.config.js +14 -7
- package/cjs/dtos/authentication.dto.js +114 -38
- 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 +5 -1
- package/cjs/entities/pending-registration.entity.js +125 -0
- package/cjs/entities/user-company-permission.entity.js +3 -9
- package/cjs/entities/user-token.entity.js +129 -0
- 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 +45 -0
- package/cjs/interfaces/index.js +2 -0
- package/cjs/interfaces/user-enricher.interface.js +4 -0
- package/cjs/modules/auth.module.js +65 -143
- package/cjs/services/auth-config.service.js +25 -27
- package/cjs/services/auth-datasource.provider.js +13 -23
- package/cjs/services/auth-email.service.js +456 -0
- package/cjs/services/authentication.service.js +297 -349
- package/cjs/services/branch.service.js +9 -4
- package/cjs/services/company-selection-session.service.js +1 -1
- package/cjs/services/index.js +1 -0
- package/cjs/services/user-permission.service.js +43 -73
- package/cjs/services/user.service.js +184 -34
- package/cjs/strategies/jwt.strategy.js +44 -44
- package/cjs/validators/password.validator.js +45 -0
- package/config/auth.constants.d.ts +3 -0
- package/controllers/authentication.controller.d.ts +16 -10
- package/controllers/company-selection.controller.d.ts +6 -2
- package/controllers/user-permission.controller.d.ts +5 -4
- package/controllers/user.controller.d.ts +33 -0
- package/dtos/authentication.dto.d.ts +20 -6
- 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 -2
- package/entities/index.d.ts +7 -3
- package/entities/pending-registration.entity.d.ts +17 -0
- package/entities/user-company-permission.entity.d.ts +1 -3
- package/entities/user-token.entity.d.ts +15 -0
- package/fesm/config/auth.constants.js +6 -2
- package/fesm/controllers/authentication.controller.js +368 -27
- 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/user-permission.controller.js +84 -156
- package/fesm/controllers/user.controller.js +214 -10
- package/fesm/docs/auth-swagger.config.js +14 -7
- package/fesm/dtos/authentication.dto.js +101 -39
- 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 +7 -1
- package/fesm/entities/pending-registration.entity.js +115 -0
- package/fesm/entities/user-company-permission.entity.js +3 -9
- package/fesm/entities/user-token.entity.js +113 -0
- 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 +38 -0
- package/fesm/interfaces/authentication.interface.js +1 -1
- package/fesm/interfaces/index.js +2 -0
- package/fesm/interfaces/user-enricher.interface.js +26 -0
- package/fesm/modules/auth.module.js +67 -145
- package/fesm/services/auth-config.service.js +26 -28
- package/fesm/services/auth-datasource.provider.js +14 -24
- package/fesm/services/auth-email.service.js +405 -0
- package/fesm/services/authentication.service.js +297 -349
- package/fesm/services/branch.service.js +9 -4
- package/fesm/services/company-selection-session.service.js +1 -1
- package/fesm/services/index.js +1 -0
- package/fesm/services/user-permission.service.js +44 -74
- package/fesm/services/user.service.js +186 -36
- 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/auth-email-provider.interface.d.ts +6 -0
- package/interfaces/auth-module-options.interface.d.ts +4 -1
- package/interfaces/authentication.interface.d.ts +13 -3
- package/interfaces/index.d.ts +2 -0
- package/interfaces/user-enricher.interface.d.ts +64 -0
- package/interfaces/user.interface.d.ts +1 -0
- package/modules/auth.module.d.ts +6 -3
- package/package.json +4 -4
- package/services/auth-config.service.d.ts +7 -8
- package/services/auth-datasource.provider.d.ts +2 -4
- package/services/auth-email.service.d.ts +54 -0
- package/services/authentication.service.d.ts +27 -16
- package/services/company-selection-session.service.d.ts +1 -1
- package/services/index.d.ts +1 -0
- package/services/user-permission.service.d.ts +6 -9
- package/services/user.service.d.ts +19 -3
- 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
|
@@ -25,62 +25,67 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
+
import { HybridCache } from '@flusys/nestjs-shared/classes';
|
|
29
|
+
import { ErrorHandler, generateUniqueSlug } from '@flusys/nestjs-shared/utils';
|
|
28
30
|
import { BadRequestException, Inject, Injectable, Logger, NotFoundException, Optional, Scope, UnauthorizedException } from '@nestjs/common';
|
|
29
31
|
import { JwtService } from '@nestjs/jwt';
|
|
30
|
-
import { InjectRepository } from '@nestjs/typeorm';
|
|
31
32
|
import * as bcrypt from 'bcrypt';
|
|
32
|
-
import { Repository } from 'typeorm';
|
|
33
|
-
import { ErrorHandler } from '@flusys/nestjs-shared/utils';
|
|
34
|
-
import { AuthConfigService } from './auth-config.service';
|
|
35
|
-
import { AuthDataSourceProvider } from './auth-datasource.provider';
|
|
36
|
-
import { PermissionType } from '../enums';
|
|
37
33
|
import { BCRYPT_SALT_ROUNDS, TOKEN_REVOKED_PREFIX } from '../config';
|
|
38
|
-
import { HybridCache } from '@flusys/nestjs-shared/classes';
|
|
39
34
|
import { CompanyBranch } from '../entities/company-branch.entity';
|
|
40
35
|
import { Company } from '../entities/company.entity';
|
|
41
36
|
import { UserCompanyPermission } from '../entities/user-company-permission.entity';
|
|
42
37
|
import { User } from '../entities/user.entity';
|
|
38
|
+
import { PermissionType } from '../enums';
|
|
39
|
+
import { AuthConfigService } from './auth-config.service';
|
|
40
|
+
import { AuthDataSourceProvider } from './auth-datasource.provider';
|
|
41
|
+
import { AuthEmailService } from './auth-email.service';
|
|
43
42
|
import { CompanySelectionSessionService } from './company-selection-session.service';
|
|
44
43
|
export class AuthenticationService {
|
|
44
|
+
// Feature Flags
|
|
45
|
+
/** Check if company feature is enabled (tenant-aware with global fallback) */ isCompanyFeatureEnabled() {
|
|
46
|
+
return this.dataSourceProvider?.isCompanyFeatureEnabled() ?? this.authConfig.isCompanyFeatureEnabled();
|
|
47
|
+
}
|
|
45
48
|
// Repository Getters
|
|
46
49
|
async getUserRepository() {
|
|
47
|
-
if (this.dataSourceProvider) {
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
if (this.injectedUserRepository) {
|
|
51
|
-
return this.injectedUserRepository;
|
|
50
|
+
if (!this.dataSourceProvider) {
|
|
51
|
+
throw new Error('User repository not available');
|
|
52
52
|
}
|
|
53
|
-
|
|
53
|
+
return this.dataSourceProvider.getRepository(User);
|
|
54
54
|
}
|
|
55
55
|
async getPermissionRepository() {
|
|
56
|
-
if (!this.
|
|
57
|
-
|
|
58
|
-
return this.dataSourceProvider.getRepository(UserCompanyPermission);
|
|
59
|
-
}
|
|
60
|
-
return this.injectedPermissionRepository ?? null;
|
|
56
|
+
if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
|
|
57
|
+
return this.dataSourceProvider.getRepository(UserCompanyPermission);
|
|
61
58
|
}
|
|
62
59
|
async getCompanyRepository() {
|
|
63
|
-
if (!this.
|
|
64
|
-
|
|
65
|
-
return this.dataSourceProvider.getRepository(Company);
|
|
66
|
-
}
|
|
67
|
-
return this.injectedCompanyRepository ?? null;
|
|
60
|
+
if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
|
|
61
|
+
return this.dataSourceProvider.getRepository(Company);
|
|
68
62
|
}
|
|
69
63
|
async getBranchRepository() {
|
|
70
|
-
if (!this.
|
|
71
|
-
|
|
72
|
-
return this.dataSourceProvider.getRepository(CompanyBranch);
|
|
73
|
-
}
|
|
74
|
-
return this.injectedBranchRepository ?? null;
|
|
64
|
+
if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
|
|
65
|
+
return this.dataSourceProvider.getRepository(CompanyBranch);
|
|
75
66
|
}
|
|
76
67
|
// Login
|
|
77
|
-
|
|
68
|
+
async login(email, password) {
|
|
69
|
+
// Check for pending registration first (user registered but hasn't verified email yet)
|
|
70
|
+
if (await this.authEmailService?.hasPendingRegistration(email)) {
|
|
71
|
+
return {
|
|
72
|
+
requiresEmailVerification: true,
|
|
73
|
+
email,
|
|
74
|
+
message: 'Please verify your email address to complete registration.'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
78
77
|
const user = await this.validateUser(email, password);
|
|
79
78
|
if (!user.isActive) {
|
|
80
79
|
throw new UnauthorizedException('User account is deactivated');
|
|
81
80
|
}
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
if (this.authEmailService?.isEmailEnabled() && !user.emailVerified) {
|
|
82
|
+
return {
|
|
83
|
+
requiresEmailVerification: true,
|
|
84
|
+
email: user.email,
|
|
85
|
+
message: 'Please verify your email address before logging in.'
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (!this.isCompanyFeatureEnabled()) {
|
|
84
89
|
return this.generateLoginResponse(user);
|
|
85
90
|
}
|
|
86
91
|
return this.handleCompanyBasedLogin(user);
|
|
@@ -112,7 +117,7 @@ export class AuthenticationService {
|
|
|
112
117
|
// If user has no company assignments yet, return simple login response
|
|
113
118
|
// This allows users to login and be assigned to companies later
|
|
114
119
|
if (companies.length === 0) {
|
|
115
|
-
return this.generateLoginResponse(user
|
|
120
|
+
return this.generateLoginResponse(user);
|
|
116
121
|
}
|
|
117
122
|
// Get branch permissions
|
|
118
123
|
const branchPermissions = await permissionRepository.find({
|
|
@@ -136,19 +141,80 @@ export class AuthenticationService {
|
|
|
136
141
|
return await this.generateCompanySelectionResponse(user, companies, branches);
|
|
137
142
|
}
|
|
138
143
|
// Registration
|
|
139
|
-
/** Register a new user. If
|
|
144
|
+
/** Register a new user. If email enabled, creates pending registration. Otherwise creates user directly. */ async register(dto, verifyUrlBase) {
|
|
140
145
|
const userRepository = await this.getUserRepository();
|
|
141
|
-
// Check if user exists
|
|
142
|
-
// Note: Generic error message to prevent email enumeration attacks
|
|
143
146
|
const existingUser = await userRepository.findOne({
|
|
144
147
|
where: {
|
|
145
148
|
email: dto.email
|
|
146
149
|
}
|
|
147
150
|
});
|
|
151
|
+
// When company feature is enabled and user exists, allow joining/creating company
|
|
152
|
+
if (existingUser && this.isCompanyFeatureEnabled()) {
|
|
153
|
+
return this.handleExistingUserCompanyRegistration(existingUser, dto);
|
|
154
|
+
}
|
|
155
|
+
// User exists but company feature is off - email must be unique
|
|
148
156
|
if (existingUser) {
|
|
149
|
-
throw new BadRequestException('Registration failed. Please try again or contact support.');
|
|
157
|
+
throw new BadRequestException('Registration failed due to email exists. Please try again or contact support.');
|
|
158
|
+
}
|
|
159
|
+
// If email verification is enabled, create pending registration instead of user
|
|
160
|
+
if (this.authEmailService?.isEmailEnabled() && verifyUrlBase) {
|
|
161
|
+
const companyData = this.isCompanyFeatureEnabled() ? {
|
|
162
|
+
companySlug: dto.companySlug,
|
|
163
|
+
branchSlug: dto.branchSlug,
|
|
164
|
+
newCompanyName: dto.newCompanyName,
|
|
165
|
+
newCompanyPhone: dto.newCompanyPhone,
|
|
166
|
+
newCompanyAddress: dto.newCompanyAddress
|
|
167
|
+
} : undefined;
|
|
168
|
+
const result = await this.authEmailService.createPendingRegistration({
|
|
169
|
+
email: dto.email,
|
|
170
|
+
name: dto.name,
|
|
171
|
+
phone: dto.phone,
|
|
172
|
+
password: dto.password,
|
|
173
|
+
companyData,
|
|
174
|
+
verifyUrlBase
|
|
175
|
+
});
|
|
176
|
+
return {
|
|
177
|
+
requiresEmailVerification: true,
|
|
178
|
+
message: result.message
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
// No email verification - create user directly
|
|
182
|
+
return this.createUserDirectly(dto);
|
|
183
|
+
}
|
|
184
|
+
/** Handle registration for existing user when company feature is enabled */ async handleExistingUserCompanyRegistration(existingUser, dto) {
|
|
185
|
+
if (!dto.companySlug && !dto.newCompanyName) {
|
|
186
|
+
throw new BadRequestException('Email already registered. Please login or provide a company to join/create.');
|
|
187
|
+
}
|
|
188
|
+
const permissionRepository = await this.getPermissionRepository();
|
|
189
|
+
const companyRepository = await this.getCompanyRepository();
|
|
190
|
+
if (!permissionRepository || !companyRepository) {
|
|
191
|
+
throw new BadRequestException('Company feature repositories not available');
|
|
150
192
|
}
|
|
151
|
-
//
|
|
193
|
+
// For existing users joining a company, check if already assigned
|
|
194
|
+
if (dto.companySlug) {
|
|
195
|
+
const company = await companyRepository.findOne({
|
|
196
|
+
where: {
|
|
197
|
+
slug: dto.companySlug,
|
|
198
|
+
isActive: true
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
if (company) {
|
|
202
|
+
const existingPermission = await permissionRepository.findOne({
|
|
203
|
+
where: {
|
|
204
|
+
userId: existingUser.id,
|
|
205
|
+
permissionType: PermissionType.COMPANY,
|
|
206
|
+
targetId: company.id
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
if (existingPermission) {
|
|
210
|
+
throw new BadRequestException('You are already assigned to this company. Please login.');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return this.processCompanyRegistration(existingUser, dto, true);
|
|
215
|
+
}
|
|
216
|
+
/** Create user directly (no email verification) */ async createUserDirectly(dto) {
|
|
217
|
+
const userRepository = await this.getUserRepository();
|
|
152
218
|
const hashedPassword = await bcrypt.hash(dto.password, BCRYPT_SALT_ROUNDS);
|
|
153
219
|
const user = userRepository.create({
|
|
154
220
|
name: dto.name,
|
|
@@ -160,112 +226,128 @@ export class AuthenticationService {
|
|
|
160
226
|
phoneVerified: false
|
|
161
227
|
});
|
|
162
228
|
await userRepository.save(user);
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
229
|
+
if (!this.isCompanyFeatureEnabled()) {
|
|
230
|
+
return this.generateRegistrationResponse(user);
|
|
231
|
+
}
|
|
232
|
+
return this.processCompanyRegistration(user, dto);
|
|
233
|
+
}
|
|
234
|
+
/** Create user from pending registration (after email verification) */ async createUserFromPendingRegistration(pending) {
|
|
235
|
+
const userRepository = await this.getUserRepository();
|
|
236
|
+
const existingUser = await userRepository.findOne({
|
|
237
|
+
where: {
|
|
238
|
+
email: pending.email
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
if (existingUser) {
|
|
242
|
+
throw new BadRequestException('This email has already been registered.');
|
|
243
|
+
}
|
|
244
|
+
const user = userRepository.create({
|
|
245
|
+
name: pending.name,
|
|
246
|
+
email: pending.email,
|
|
247
|
+
phone: pending.phone,
|
|
248
|
+
password: pending.passwordHash,
|
|
249
|
+
isActive: true,
|
|
250
|
+
emailVerified: true,
|
|
251
|
+
phoneVerified: false
|
|
252
|
+
});
|
|
253
|
+
await userRepository.save(user);
|
|
254
|
+
if (this.isCompanyFeatureEnabled() && pending.companyData) {
|
|
255
|
+
return this.processCompanyRegistration(user, pending.companyData);
|
|
166
256
|
}
|
|
167
|
-
|
|
168
|
-
return this.handleCompanyRegistration(user, dto);
|
|
257
|
+
return this.generateRegistrationResponse(user);
|
|
169
258
|
}
|
|
170
|
-
/**
|
|
259
|
+
/** Process company registration (join existing or create new) */ async processCompanyRegistration(user, data, throwOnUnavailable = false) {
|
|
171
260
|
const companyRepository = await this.getCompanyRepository();
|
|
172
261
|
const branchRepository = await this.getBranchRepository();
|
|
173
262
|
const permissionRepository = await this.getPermissionRepository();
|
|
174
263
|
if (!companyRepository || !branchRepository || !permissionRepository) {
|
|
175
|
-
|
|
264
|
+
if (throwOnUnavailable) throw new BadRequestException('Company feature repositories not available');
|
|
265
|
+
return this.generateRegistrationResponse(user);
|
|
176
266
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
267
|
+
const { company, branch } = await this.findOrCreateCompanyAndBranch(data, companyRepository, branchRepository);
|
|
268
|
+
if (company) {
|
|
269
|
+
await this.assignUserPermissions(user.id, company.id, branch?.id, permissionRepository);
|
|
270
|
+
return this.generateRegistrationResponseWithCompany(user, company, branch ?? undefined);
|
|
271
|
+
}
|
|
272
|
+
return this.generateRegistrationResponse(user);
|
|
273
|
+
}
|
|
274
|
+
/** Find existing company/branch or create new ones */ async findOrCreateCompanyAndBranch(data, companyRepo, branchRepo) {
|
|
275
|
+
if (data.companySlug) {
|
|
276
|
+
const company = await companyRepo.findOne({
|
|
182
277
|
where: {
|
|
183
|
-
slug:
|
|
278
|
+
slug: data.companySlug,
|
|
184
279
|
isActive: true
|
|
185
280
|
}
|
|
186
281
|
});
|
|
187
|
-
if (!company)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
where: {
|
|
194
|
-
slug: dto.branchSlug,
|
|
195
|
-
companyId: company.id,
|
|
196
|
-
isActive: true
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
if (!branch) {
|
|
200
|
-
throw new BadRequestException('Branch not found or inactive');
|
|
282
|
+
if (!company) throw new BadRequestException('Company not found or inactive');
|
|
283
|
+
const branch = data.branchSlug ? await branchRepo.findOne({
|
|
284
|
+
where: {
|
|
285
|
+
slug: data.branchSlug,
|
|
286
|
+
companyId: company.id,
|
|
287
|
+
isActive: true
|
|
201
288
|
}
|
|
202
|
-
}
|
|
203
|
-
// Get first active branch
|
|
204
|
-
branch = await branchRepository.findOne({
|
|
205
|
-
where: {
|
|
206
|
-
companyId: company.id,
|
|
207
|
-
isActive: true
|
|
208
|
-
},
|
|
209
|
-
order: {
|
|
210
|
-
serial: 'ASC',
|
|
211
|
-
createdAt: 'ASC'
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
} else if (dto.newCompanyName) {
|
|
216
|
-
const slug = this.generateSlug(dto.newCompanyName);
|
|
217
|
-
// Check if slug exists
|
|
218
|
-
const existingCompany = await companyRepository.findOne({
|
|
289
|
+
}) : await branchRepo.findOne({
|
|
219
290
|
where: {
|
|
220
|
-
|
|
291
|
+
companyId: company.id,
|
|
292
|
+
isActive: true
|
|
293
|
+
},
|
|
294
|
+
order: {
|
|
295
|
+
serial: 'ASC',
|
|
296
|
+
createdAt: 'ASC'
|
|
221
297
|
}
|
|
222
298
|
});
|
|
223
|
-
if (
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
299
|
+
if (data.branchSlug && !branch) throw new BadRequestException('Branch not found or inactive');
|
|
300
|
+
return {
|
|
301
|
+
company,
|
|
302
|
+
branch
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
if (data.newCompanyName) {
|
|
306
|
+
const slug = await generateUniqueSlug(data.newCompanyName, async (baseSlug)=>{
|
|
307
|
+
const rows = await companyRepo.createQueryBuilder('c').select('c.slug').where('c.slug = :base OR c.slug LIKE :pattern', {
|
|
308
|
+
base: baseSlug,
|
|
309
|
+
pattern: `${baseSlug}-%`
|
|
310
|
+
}).getMany();
|
|
311
|
+
return rows.map((r)=>r.slug);
|
|
312
|
+
});
|
|
313
|
+
const company = companyRepo.create({
|
|
314
|
+
name: data.newCompanyName,
|
|
229
315
|
slug,
|
|
230
|
-
phone:
|
|
231
|
-
address:
|
|
316
|
+
phone: data.newCompanyPhone,
|
|
317
|
+
address: data.newCompanyAddress,
|
|
232
318
|
isActive: true
|
|
233
319
|
});
|
|
234
|
-
await
|
|
235
|
-
|
|
236
|
-
branch = branchRepository.create({
|
|
320
|
+
await companyRepo.save(company);
|
|
321
|
+
const branch = branchRepo.create({
|
|
237
322
|
name: 'Main Branch',
|
|
238
323
|
slug: 'main-branch',
|
|
239
324
|
companyId: company.id,
|
|
240
325
|
isActive: true
|
|
241
326
|
});
|
|
242
|
-
await
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const companyPermission = permissionRepository.create({
|
|
248
|
-
userId: user.id,
|
|
249
|
-
permissionType: PermissionType.COMPANY,
|
|
250
|
-
targetId: company.id
|
|
251
|
-
});
|
|
252
|
-
await permissionRepository.save(companyPermission);
|
|
253
|
-
// Create branch permission if branch exists
|
|
254
|
-
if (branch) {
|
|
255
|
-
const branchPermission = permissionRepository.create({
|
|
256
|
-
userId: user.id,
|
|
257
|
-
permissionType: PermissionType.BRANCH,
|
|
258
|
-
targetId: branch.id
|
|
259
|
-
});
|
|
260
|
-
await permissionRepository.save(branchPermission);
|
|
261
|
-
}
|
|
262
|
-
return this.generateLoginResponseWithCompany(user, company, branch ?? undefined);
|
|
327
|
+
await branchRepo.save(branch);
|
|
328
|
+
return {
|
|
329
|
+
company,
|
|
330
|
+
branch
|
|
331
|
+
};
|
|
263
332
|
}
|
|
264
|
-
|
|
265
|
-
|
|
333
|
+
return {
|
|
334
|
+
company: null,
|
|
335
|
+
branch: null
|
|
336
|
+
};
|
|
266
337
|
}
|
|
267
|
-
/**
|
|
268
|
-
|
|
338
|
+
/** Assign user to company and optionally branch */ async assignUserPermissions(userId, companyId, branchId, permissionRepo) {
|
|
339
|
+
await permissionRepo.save(permissionRepo.create({
|
|
340
|
+
userId,
|
|
341
|
+
permissionType: PermissionType.COMPANY,
|
|
342
|
+
targetId: companyId
|
|
343
|
+
}));
|
|
344
|
+
if (branchId) {
|
|
345
|
+
await permissionRepo.save(permissionRepo.create({
|
|
346
|
+
userId,
|
|
347
|
+
permissionType: PermissionType.BRANCH,
|
|
348
|
+
targetId: branchId
|
|
349
|
+
}));
|
|
350
|
+
}
|
|
269
351
|
}
|
|
270
352
|
// Company/Branch Selection
|
|
271
353
|
/** Select company and branch (step 2 of login). Frontend receives all options and user selects both. */ async select(sessionId, companyId, branchId) {
|
|
@@ -277,66 +359,13 @@ export class AuthenticationService {
|
|
|
277
359
|
if (!session) {
|
|
278
360
|
throw new UnauthorizedException('Invalid or expired session');
|
|
279
361
|
}
|
|
280
|
-
|
|
281
|
-
const user = await userRepository.findOne({
|
|
282
|
-
where: {
|
|
283
|
-
id: session.userId
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
if (!user) {
|
|
287
|
-
throw new NotFoundException('User not found');
|
|
288
|
-
}
|
|
289
|
-
const permissionRepository = await this.getPermissionRepository();
|
|
290
|
-
const companyRepository = await this.getCompanyRepository();
|
|
291
|
-
const branchRepository = await this.getBranchRepository();
|
|
292
|
-
if (!permissionRepository || !companyRepository || !branchRepository) {
|
|
293
|
-
throw new BadRequestException('Repositories not available');
|
|
294
|
-
}
|
|
295
|
-
// Validate company access
|
|
296
|
-
const companyPermission = await permissionRepository.findOne({
|
|
297
|
-
where: {
|
|
298
|
-
userId: user.id,
|
|
299
|
-
permissionType: PermissionType.COMPANY,
|
|
300
|
-
targetId: companyId
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
if (!companyPermission) {
|
|
304
|
-
throw new UnauthorizedException('No access to this company');
|
|
305
|
-
}
|
|
306
|
-
const company = await companyRepository.findOne({
|
|
307
|
-
where: {
|
|
308
|
-
id: companyId,
|
|
309
|
-
isActive: true
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
if (!company) {
|
|
313
|
-
throw new UnauthorizedException('Company not found or inactive');
|
|
314
|
-
}
|
|
315
|
-
// Validate branch access
|
|
316
|
-
const branchPermission = await permissionRepository.findOne({
|
|
317
|
-
where: {
|
|
318
|
-
userId: user.id,
|
|
319
|
-
permissionType: PermissionType.BRANCH,
|
|
320
|
-
targetId: branchId
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
if (!branchPermission) {
|
|
324
|
-
throw new UnauthorizedException('No access to this branch');
|
|
325
|
-
}
|
|
326
|
-
const branch = await branchRepository.findOne({
|
|
327
|
-
where: {
|
|
328
|
-
id: branchId,
|
|
329
|
-
companyId,
|
|
330
|
-
isActive: true
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
if (!branch) {
|
|
334
|
-
throw new UnauthorizedException('Branch not found or inactive');
|
|
335
|
-
}
|
|
336
|
-
return this.generateLoginResponseWithCompany(user, company, branch);
|
|
362
|
+
return this.validateAndLoginWithCompany(session.userId, companyId, branchId);
|
|
337
363
|
}
|
|
338
364
|
/** Switch company/branch after login */ async switchCompany(userId, companyId, branchId) {
|
|
339
365
|
this.ensureCompanyFeatureEnabled();
|
|
366
|
+
return this.validateAndLoginWithCompany(userId, companyId, branchId);
|
|
367
|
+
}
|
|
368
|
+
/** Shared: Validate user access to company/branch and generate login response */ async validateAndLoginWithCompany(userId, companyId, branchId) {
|
|
340
369
|
const userRepository = await this.getUserRepository();
|
|
341
370
|
const user = await userRepository.findOne({
|
|
342
371
|
where: {
|
|
@@ -353,14 +382,14 @@ export class AuthenticationService {
|
|
|
353
382
|
throw new BadRequestException('Repositories not available');
|
|
354
383
|
}
|
|
355
384
|
// Validate company access
|
|
356
|
-
const
|
|
385
|
+
const hasCompanyAccess = await permissionRepository.findOne({
|
|
357
386
|
where: {
|
|
358
387
|
userId,
|
|
359
388
|
permissionType: PermissionType.COMPANY,
|
|
360
389
|
targetId: companyId
|
|
361
390
|
}
|
|
362
391
|
});
|
|
363
|
-
if (!
|
|
392
|
+
if (!hasCompanyAccess) {
|
|
364
393
|
throw new UnauthorizedException('No access to this company');
|
|
365
394
|
}
|
|
366
395
|
const company = await companyRepository.findOne({
|
|
@@ -372,14 +401,15 @@ export class AuthenticationService {
|
|
|
372
401
|
if (!company) {
|
|
373
402
|
throw new UnauthorizedException('Company not found or inactive');
|
|
374
403
|
}
|
|
375
|
-
|
|
404
|
+
// Validate branch access
|
|
405
|
+
const hasBranchAccess = await permissionRepository.findOne({
|
|
376
406
|
where: {
|
|
377
407
|
userId,
|
|
378
408
|
permissionType: PermissionType.BRANCH,
|
|
379
409
|
targetId: branchId
|
|
380
410
|
}
|
|
381
411
|
});
|
|
382
|
-
if (!
|
|
412
|
+
if (!hasBranchAccess) {
|
|
383
413
|
throw new UnauthorizedException('No access to this branch');
|
|
384
414
|
}
|
|
385
415
|
const branch = await branchRepository.findOne({
|
|
@@ -400,6 +430,9 @@ export class AuthenticationService {
|
|
|
400
430
|
const payload = this.jwtService.verify(refreshToken, {
|
|
401
431
|
secret: this.authConfig.getRefreshTokenSecret()
|
|
402
432
|
});
|
|
433
|
+
if (await this.isTokenRevoked(payload.id, payload.iat)) {
|
|
434
|
+
throw new UnauthorizedException('Token has been revoked');
|
|
435
|
+
}
|
|
403
436
|
const userRepository = await this.getUserRepository();
|
|
404
437
|
const user = await userRepository.findOne({
|
|
405
438
|
where: {
|
|
@@ -409,7 +442,7 @@ export class AuthenticationService {
|
|
|
409
442
|
if (!user || !user.isActive) {
|
|
410
443
|
throw new UnauthorizedException('Invalid refresh token');
|
|
411
444
|
}
|
|
412
|
-
|
|
445
|
+
return this.generateTokens({
|
|
413
446
|
id: user.id,
|
|
414
447
|
email: user.email,
|
|
415
448
|
type: 'access',
|
|
@@ -419,44 +452,9 @@ export class AuthenticationService {
|
|
|
419
452
|
},
|
|
420
453
|
...payload.branchId && {
|
|
421
454
|
branchId: payload.branchId
|
|
422
|
-
},
|
|
423
|
-
...payload.companyLogoId && {
|
|
424
|
-
companyLogoId: payload.companyLogoId
|
|
425
|
-
},
|
|
426
|
-
...payload.branchLogoId && {
|
|
427
|
-
branchLogoId: payload.branchLogoId
|
|
428
455
|
}
|
|
429
|
-
};
|
|
430
|
-
const refreshPayload = {
|
|
431
|
-
id: user.id,
|
|
432
|
-
email: user.email,
|
|
433
|
-
type: 'refresh',
|
|
434
|
-
profilePictureId: user.profilePictureId ?? undefined,
|
|
435
|
-
...payload.companyId && {
|
|
436
|
-
companyId: payload.companyId
|
|
437
|
-
},
|
|
438
|
-
...payload.branchId && {
|
|
439
|
-
branchId: payload.branchId
|
|
440
|
-
},
|
|
441
|
-
...payload.companyLogoId && {
|
|
442
|
-
companyLogoId: payload.companyLogoId
|
|
443
|
-
},
|
|
444
|
-
...payload.branchLogoId && {
|
|
445
|
-
branchLogoId: payload.branchLogoId
|
|
446
|
-
}
|
|
447
|
-
};
|
|
448
|
-
return {
|
|
449
|
-
accessToken: this.jwtService.sign(accessPayload, {
|
|
450
|
-
secret: this.authConfig.getJwtSecret(),
|
|
451
|
-
expiresIn: this.authConfig.getJwtExpiration()
|
|
452
|
-
}),
|
|
453
|
-
refreshToken: this.jwtService.sign(refreshPayload, {
|
|
454
|
-
secret: this.authConfig.getRefreshTokenSecret(),
|
|
455
|
-
expiresIn: this.authConfig.getRefreshTokenExpiration()
|
|
456
|
-
})
|
|
457
|
-
};
|
|
456
|
+
});
|
|
458
457
|
} catch (error) {
|
|
459
|
-
// Log error details for debugging (not exposed to client)
|
|
460
458
|
ErrorHandler.logError(this.logger, error, 'refreshToken');
|
|
461
459
|
throw new UnauthorizedException('Invalid or expired refresh token');
|
|
462
460
|
}
|
|
@@ -468,11 +466,8 @@ export class AuthenticationService {
|
|
|
468
466
|
id: userId
|
|
469
467
|
}
|
|
470
468
|
});
|
|
471
|
-
if (!user)
|
|
472
|
-
|
|
473
|
-
}
|
|
474
|
-
const isPasswordValid = await bcrypt.compare(currentPassword, user.password || '');
|
|
475
|
-
if (!isPasswordValid) {
|
|
469
|
+
if (!user) throw new UnauthorizedException('User not found');
|
|
470
|
+
if (!await bcrypt.compare(currentPassword, user.password || '')) {
|
|
476
471
|
throw new UnauthorizedException('Current password is incorrect');
|
|
477
472
|
}
|
|
478
473
|
user.password = await bcrypt.hash(newPassword, BCRYPT_SALT_ROUNDS);
|
|
@@ -480,8 +475,9 @@ export class AuthenticationService {
|
|
|
480
475
|
}
|
|
481
476
|
/** Logout - Revokes all tokens. Stores revocation timestamp in cache. */ async logout(userId) {
|
|
482
477
|
if (!this.cacheManager) {
|
|
483
|
-
// Cache
|
|
484
|
-
|
|
478
|
+
// Cache is required for secure logout - tokens cannot be revoked without it
|
|
479
|
+
this.logger.error(`SECURITY: Cache not available for logout (userId: ${userId}). ` + 'Token revocation failed - tokens will remain valid until expiry. ' + 'Configure cache (Redis) to enable proper token revocation.');
|
|
480
|
+
throw new BadRequestException('Logout service temporarily unavailable. Please try again later.');
|
|
485
481
|
}
|
|
486
482
|
const revokedAt = Math.floor(Date.now() / 1000); // Unix timestamp in seconds
|
|
487
483
|
const cacheKey = `${TOKEN_REVOKED_PREFIX}:${userId}`;
|
|
@@ -489,6 +485,7 @@ export class AuthenticationService {
|
|
|
489
485
|
const refreshTokenExpiry = this.authConfig.getRefreshTokenExpiration();
|
|
490
486
|
const ttlMs = this.parseExpiryToMs(refreshTokenExpiry);
|
|
491
487
|
await this.cacheManager.set(cacheKey, revokedAt, ttlMs);
|
|
488
|
+
this.logger.log(`User ${userId} logged out - all tokens revoked`);
|
|
492
489
|
}
|
|
493
490
|
/** Check if a token is revoked (issued before user's revocation timestamp) */ async isTokenRevoked(userId, tokenIssuedAt) {
|
|
494
491
|
if (!this.cacheManager || !tokenIssuedAt) {
|
|
@@ -606,53 +603,28 @@ export class AuthenticationService {
|
|
|
606
603
|
id: userId
|
|
607
604
|
}
|
|
608
605
|
});
|
|
609
|
-
if (!user)
|
|
610
|
-
throw new UnauthorizedException('User not found');
|
|
611
|
-
}
|
|
612
|
-
// Build response (no tokens)
|
|
606
|
+
if (!user) throw new UnauthorizedException('User not found');
|
|
613
607
|
const response = {
|
|
614
|
-
user:
|
|
615
|
-
id: user.id,
|
|
616
|
-
email: user.email,
|
|
617
|
-
name: user.name ?? undefined,
|
|
618
|
-
profilePictureId: user.profilePictureId ?? undefined
|
|
619
|
-
}
|
|
608
|
+
user: this.toUserInfo(user)
|
|
620
609
|
};
|
|
621
|
-
|
|
622
|
-
if (this.authConfig.isCompanyFeatureEnabled()) {
|
|
623
|
-
// When company feature is enabled, companyId MUST be present
|
|
610
|
+
if (this.isCompanyFeatureEnabled()) {
|
|
624
611
|
if (!companyId) {
|
|
625
|
-
throw new UnauthorizedException('Company feature is enabled but user has no company assigned.
|
|
612
|
+
throw new UnauthorizedException('Company feature is enabled but user has no company assigned.');
|
|
626
613
|
}
|
|
627
614
|
const company = await this.getCompanyById(companyId);
|
|
628
|
-
if (!company)
|
|
629
|
-
|
|
630
|
-
}
|
|
631
|
-
response.company = {
|
|
632
|
-
id: company.id,
|
|
633
|
-
name: company.name,
|
|
634
|
-
slug: company.slug,
|
|
635
|
-
logoId: company.logoId ?? null
|
|
636
|
-
};
|
|
637
|
-
// Branch is optional in some configurations, but if provided it must exist
|
|
615
|
+
if (!company) throw new UnauthorizedException('Company not found.');
|
|
616
|
+
response.company = this.toCompanyInfo(company);
|
|
638
617
|
if (branchId) {
|
|
639
618
|
const branch = await this.getBranchById(branchId);
|
|
640
|
-
if (!branch)
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
response.branch = {
|
|
644
|
-
id: branch.id,
|
|
645
|
-
name: branch.name,
|
|
646
|
-
slug: branch.slug,
|
|
647
|
-
logoId: branch.logoId ?? null
|
|
648
|
-
};
|
|
619
|
+
if (!branch) throw new UnauthorizedException('Branch not found.');
|
|
620
|
+
response.branch = this.toBranchInfo(branch);
|
|
649
621
|
}
|
|
650
622
|
}
|
|
651
623
|
return response;
|
|
652
624
|
}
|
|
653
625
|
// Helper Methods
|
|
654
626
|
ensureCompanyFeatureEnabled() {
|
|
655
|
-
if (!this.
|
|
627
|
+
if (!this.isCompanyFeatureEnabled()) {
|
|
656
628
|
throw new BadRequestException('Company feature is not enabled');
|
|
657
629
|
}
|
|
658
630
|
}
|
|
@@ -663,11 +635,7 @@ export class AuthenticationService {
|
|
|
663
635
|
email
|
|
664
636
|
}
|
|
665
637
|
});
|
|
666
|
-
if (!user) {
|
|
667
|
-
throw new UnauthorizedException('Invalid credentials');
|
|
668
|
-
}
|
|
669
|
-
const isPasswordValid = await bcrypt.compare(password, user.password || '');
|
|
670
|
-
if (!isPasswordValid) {
|
|
638
|
+
if (!user || !await bcrypt.compare(password, user.password || '')) {
|
|
671
639
|
throw new UnauthorizedException('Invalid credentials');
|
|
672
640
|
}
|
|
673
641
|
user.lastLoginAt = new Date();
|
|
@@ -675,82 +643,77 @@ export class AuthenticationService {
|
|
|
675
643
|
return user;
|
|
676
644
|
}
|
|
677
645
|
// Response Generators
|
|
678
|
-
|
|
679
|
-
|
|
646
|
+
/** Create user info object */ toUserInfo(user) {
|
|
647
|
+
return {
|
|
680
648
|
id: user.id,
|
|
681
649
|
email: user.email,
|
|
682
|
-
|
|
650
|
+
name: user.name ?? undefined,
|
|
651
|
+
phone: user.phone ?? null,
|
|
652
|
+
profilePictureId: user.profilePictureId ?? null
|
|
683
653
|
};
|
|
684
|
-
|
|
654
|
+
}
|
|
655
|
+
/** Create company info object */ toCompanyInfo(company) {
|
|
656
|
+
return {
|
|
657
|
+
id: company.id,
|
|
658
|
+
name: company.name,
|
|
659
|
+
slug: company.slug,
|
|
660
|
+
logoId: company.logoId ?? null
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
/** Create branch info object */ toBranchInfo(branch) {
|
|
664
|
+
return {
|
|
665
|
+
id: branch.id,
|
|
666
|
+
name: branch.name,
|
|
667
|
+
slug: branch.slug,
|
|
668
|
+
logoId: branch.logoId ?? null
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
/** Generate JWT tokens */ generateTokens(payload) {
|
|
672
|
+
const accessToken = this.jwtService.sign({
|
|
673
|
+
...payload,
|
|
674
|
+
type: 'access'
|
|
675
|
+
}, {
|
|
685
676
|
secret: this.authConfig.getJwtSecret(),
|
|
686
677
|
expiresIn: this.authConfig.getJwtExpiration()
|
|
687
678
|
});
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
email: user.email,
|
|
679
|
+
const refreshToken = this.jwtService.sign({
|
|
680
|
+
...payload,
|
|
691
681
|
type: 'refresh'
|
|
692
|
-
}
|
|
693
|
-
const refreshToken = this.jwtService.sign(refreshPayload, {
|
|
682
|
+
}, {
|
|
694
683
|
secret: this.authConfig.getRefreshTokenSecret(),
|
|
695
684
|
expiresIn: this.authConfig.getRefreshTokenExpiration()
|
|
696
685
|
});
|
|
697
686
|
return {
|
|
698
687
|
accessToken,
|
|
699
|
-
refreshToken
|
|
700
|
-
user: {
|
|
701
|
-
id: user.id,
|
|
702
|
-
email: user.email,
|
|
703
|
-
name: user.name ?? undefined,
|
|
704
|
-
profilePictureId: user.profilePictureId ?? null
|
|
705
|
-
},
|
|
706
|
-
companyFeatureEnabled
|
|
688
|
+
refreshToken
|
|
707
689
|
};
|
|
708
690
|
}
|
|
709
|
-
|
|
710
|
-
const
|
|
691
|
+
generateLoginResponse(user) {
|
|
692
|
+
const { accessToken, refreshToken } = this.generateTokens({
|
|
711
693
|
id: user.id,
|
|
712
694
|
email: user.email,
|
|
713
|
-
type: 'access'
|
|
714
|
-
companyId: company.id,
|
|
715
|
-
branchId: branch?.id
|
|
716
|
-
};
|
|
717
|
-
const accessToken = this.jwtService.sign(payload, {
|
|
718
|
-
secret: this.authConfig.getJwtSecret(),
|
|
719
|
-
expiresIn: this.authConfig.getJwtExpiration()
|
|
695
|
+
type: 'access'
|
|
720
696
|
});
|
|
721
|
-
|
|
697
|
+
return {
|
|
698
|
+
accessToken,
|
|
699
|
+
refreshToken,
|
|
700
|
+
user: this.toUserInfo(user)
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
generateLoginResponseWithCompany(user, company, branch) {
|
|
704
|
+
const { accessToken, refreshToken } = this.generateTokens({
|
|
722
705
|
id: user.id,
|
|
723
706
|
email: user.email,
|
|
724
|
-
type: '
|
|
707
|
+
type: 'access',
|
|
725
708
|
companyId: company.id,
|
|
726
709
|
branchId: branch?.id
|
|
727
|
-
};
|
|
728
|
-
const refreshToken = this.jwtService.sign(refreshPayload, {
|
|
729
|
-
secret: this.authConfig.getRefreshTokenSecret(),
|
|
730
|
-
expiresIn: this.authConfig.getRefreshTokenExpiration()
|
|
731
710
|
});
|
|
732
711
|
return {
|
|
733
712
|
accessToken,
|
|
734
713
|
refreshToken,
|
|
735
|
-
user:
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
name: user.name ?? undefined,
|
|
739
|
-
profilePictureId: user.profilePictureId ?? null
|
|
740
|
-
},
|
|
741
|
-
company: {
|
|
742
|
-
id: company.id,
|
|
743
|
-
name: company.name,
|
|
744
|
-
slug: company.slug,
|
|
745
|
-
logoId: company.logoId ?? null
|
|
746
|
-
},
|
|
747
|
-
branch: branch ? {
|
|
748
|
-
id: branch.id,
|
|
749
|
-
name: branch.name,
|
|
750
|
-
slug: branch.slug,
|
|
751
|
-
logoId: branch.logoId ?? null
|
|
752
|
-
} : undefined,
|
|
753
|
-
companyFeatureEnabled: true
|
|
714
|
+
user: this.toUserInfo(user),
|
|
715
|
+
company: this.toCompanyInfo(company),
|
|
716
|
+
branch: branch ? this.toBranchInfo(branch) : undefined
|
|
754
717
|
};
|
|
755
718
|
}
|
|
756
719
|
async generateCompanySelectionResponse(user, companies, branches) {
|
|
@@ -758,55 +721,49 @@ export class AuthenticationService {
|
|
|
758
721
|
throw new BadRequestException('Session service not available');
|
|
759
722
|
}
|
|
760
723
|
const sessionId = await this.sessionService.createSession(user.id);
|
|
761
|
-
// Build companies with their branches
|
|
762
724
|
const companiesWithBranches = companies.map((company)=>({
|
|
763
|
-
|
|
764
|
-
name: company.name,
|
|
765
|
-
slug: company.slug,
|
|
725
|
+
...this.toCompanyInfo(company),
|
|
766
726
|
isActive: company.isActive,
|
|
767
|
-
logoId: company.logoId ?? null,
|
|
768
727
|
branches: branches.filter((b)=>b.companyId === company.id).map((b)=>({
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
slug: b.slug,
|
|
772
|
-
isActive: b.isActive,
|
|
773
|
-
logoId: b.logoId ?? null
|
|
728
|
+
...this.toBranchInfo(b),
|
|
729
|
+
isActive: b.isActive
|
|
774
730
|
}))
|
|
775
731
|
}));
|
|
776
732
|
return {
|
|
777
733
|
requiresSelection: true,
|
|
778
734
|
sessionId,
|
|
779
735
|
expiresAt: new Date(Date.now() + 5 * 60 * 1000),
|
|
780
|
-
user:
|
|
781
|
-
id: user.id,
|
|
782
|
-
email: user.email,
|
|
783
|
-
name: user.name ?? undefined,
|
|
784
|
-
profilePictureId: user.profilePictureId ?? null
|
|
785
|
-
},
|
|
736
|
+
user: this.toUserInfo(user),
|
|
786
737
|
companies: companiesWithBranches
|
|
787
738
|
};
|
|
788
739
|
}
|
|
740
|
+
generateRegistrationResponse(user) {
|
|
741
|
+
return {
|
|
742
|
+
...this.generateLoginResponse(user),
|
|
743
|
+
requiresEmailVerification: false
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
generateRegistrationResponseWithCompany(user, company, branch) {
|
|
747
|
+
return {
|
|
748
|
+
...this.generateLoginResponseWithCompany(user, company, branch),
|
|
749
|
+
requiresEmailVerification: false
|
|
750
|
+
};
|
|
751
|
+
}
|
|
789
752
|
// NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
|
|
790
|
-
constructor(jwtService, authConfig, cacheManager, dataSourceProvider, sessionService,
|
|
753
|
+
constructor(jwtService, authConfig, cacheManager, dataSourceProvider, sessionService, authEmailService){
|
|
791
754
|
_define_property(this, "jwtService", void 0);
|
|
792
755
|
_define_property(this, "authConfig", void 0);
|
|
793
756
|
_define_property(this, "cacheManager", void 0);
|
|
794
757
|
_define_property(this, "dataSourceProvider", void 0);
|
|
795
758
|
_define_property(this, "sessionService", void 0);
|
|
796
|
-
_define_property(this, "
|
|
797
|
-
_define_property(this, "injectedPermissionRepository", void 0);
|
|
798
|
-
_define_property(this, "injectedCompanyRepository", void 0);
|
|
799
|
-
_define_property(this, "injectedBranchRepository", void 0);
|
|
759
|
+
_define_property(this, "authEmailService", void 0);
|
|
800
760
|
_define_property(this, "logger", void 0);
|
|
801
761
|
this.jwtService = jwtService;
|
|
802
762
|
this.authConfig = authConfig;
|
|
803
763
|
this.cacheManager = cacheManager;
|
|
804
764
|
this.dataSourceProvider = dataSourceProvider;
|
|
805
765
|
this.sessionService = sessionService;
|
|
806
|
-
this.
|
|
807
|
-
this.injectedPermissionRepository = injectedPermissionRepository;
|
|
808
|
-
this.injectedCompanyRepository = injectedCompanyRepository;
|
|
809
|
-
this.injectedBranchRepository = injectedBranchRepository;
|
|
766
|
+
this.authEmailService = authEmailService;
|
|
810
767
|
this.logger = new Logger(AuthenticationService.name);
|
|
811
768
|
}
|
|
812
769
|
}
|
|
@@ -823,13 +780,7 @@ AuthenticationService = _ts_decorate([
|
|
|
823
780
|
_ts_param(4, Optional()),
|
|
824
781
|
_ts_param(4, Inject(CompanySelectionSessionService)),
|
|
825
782
|
_ts_param(5, Optional()),
|
|
826
|
-
_ts_param(5,
|
|
827
|
-
_ts_param(6, Optional()),
|
|
828
|
-
_ts_param(6, InjectRepository(UserCompanyPermission)),
|
|
829
|
-
_ts_param(7, Optional()),
|
|
830
|
-
_ts_param(7, InjectRepository(Company)),
|
|
831
|
-
_ts_param(8, Optional()),
|
|
832
|
-
_ts_param(8, InjectRepository(CompanyBranch)),
|
|
783
|
+
_ts_param(5, Inject(AuthEmailService)),
|
|
833
784
|
_ts_metadata("design:type", Function),
|
|
834
785
|
_ts_metadata("design:paramtypes", [
|
|
835
786
|
typeof JwtService === "undefined" ? Object : JwtService,
|
|
@@ -837,9 +788,6 @@ AuthenticationService = _ts_decorate([
|
|
|
837
788
|
typeof HybridCache === "undefined" ? Object : HybridCache,
|
|
838
789
|
typeof AuthDataSourceProvider === "undefined" ? Object : AuthDataSourceProvider,
|
|
839
790
|
typeof CompanySelectionSessionService === "undefined" ? Object : CompanySelectionSessionService,
|
|
840
|
-
typeof
|
|
841
|
-
typeof Repository === "undefined" ? Object : Repository,
|
|
842
|
-
typeof Repository === "undefined" ? Object : Repository,
|
|
843
|
-
typeof Repository === "undefined" ? Object : Repository
|
|
791
|
+
typeof AuthEmailService === "undefined" ? Object : AuthEmailService
|
|
844
792
|
])
|
|
845
793
|
], AuthenticationService);
|