@flusys/nestjs-auth 1.1.0-beta → 2.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 +8 -0
- package/cjs/controllers/authentication.controller.js +218 -39
- 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 +68 -15
- package/cjs/docs/auth-swagger.config.js +14 -7
- package/cjs/dtos/authentication.dto.js +16 -43
- package/cjs/dtos/index.js +0 -1
- package/cjs/dtos/user-permission.dto.js +28 -0
- package/cjs/dtos/user.dto.js +19 -4
- package/cjs/entities/user-company-permission.entity.js +0 -6
- package/cjs/interceptors/clear-token.interceptor.js +10 -23
- package/cjs/interceptors/set-token.interceptor.js +15 -55
- package/cjs/interfaces/auth-email-provider.interface.js +1 -1
- package/cjs/modules/auth.module.js +63 -147
- package/cjs/services/auth-config.service.js +22 -28
- package/cjs/services/auth-datasource.provider.js +9 -21
- package/cjs/services/auth-email.service.js +89 -85
- package/cjs/services/authentication.service.js +225 -396
- package/cjs/services/branch.service.js +9 -4
- package/cjs/services/company-selection-session.service.js +1 -1
- package/cjs/services/user-permission.service.js +43 -73
- package/cjs/services/user.service.js +78 -64
- package/cjs/strategies/jwt.strategy.js +44 -44
- package/cjs/validators/password.validator.js +45 -0
- package/config/auth.constants.d.ts +2 -0
- package/controllers/authentication.controller.d.ts +5 -3
- package/controllers/company-selection.controller.d.ts +6 -2
- package/controllers/user-permission.controller.d.ts +5 -4
- package/controllers/user.controller.d.ts +6 -0
- package/dtos/authentication.dto.d.ts +0 -4
- package/dtos/company-branch.dto.d.ts +0 -1
- package/dtos/company.dto.d.ts +0 -1
- package/dtos/index.d.ts +0 -1
- package/dtos/user-permission.dto.d.ts +4 -0
- package/dtos/user.dto.d.ts +1 -3
- package/entities/index.d.ts +3 -3
- package/entities/user-company-permission.entity.d.ts +0 -2
- package/fesm/config/auth.constants.js +6 -13
- package/fesm/controllers/authentication.controller.js +221 -42
- 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 +71 -18
- package/fesm/docs/auth-swagger.config.js +14 -7
- package/fesm/dtos/authentication.dto.js +17 -44
- package/fesm/dtos/index.js +0 -1
- package/fesm/dtos/user-permission.dto.js +28 -0
- package/fesm/dtos/user.dto.js +20 -5
- package/fesm/entities/user-company-permission.entity.js +0 -6
- package/fesm/interceptors/clear-token.interceptor.js +9 -22
- package/fesm/interceptors/set-token.interceptor.js +14 -54
- package/fesm/interfaces/auth-email-provider.interface.js +1 -1
- package/fesm/modules/auth.module.js +64 -148
- package/fesm/services/auth-config.service.js +23 -29
- package/fesm/services/auth-datasource.provider.js +10 -22
- package/fesm/services/auth-email.service.js +90 -86
- package/fesm/services/authentication.service.js +225 -396
- package/fesm/services/branch.service.js +9 -4
- package/fesm/services/company-selection-session.service.js +1 -1
- package/fesm/services/user-permission.service.js +44 -74
- package/fesm/services/user.service.js +79 -65
- package/fesm/strategies/jwt.strategy.js +44 -44
- package/fesm/validators/password.validator.js +48 -0
- package/interceptors/clear-token.interceptor.d.ts +0 -1
- package/interceptors/set-token.interceptor.d.ts +1 -1
- package/interfaces/authentication.interface.d.ts +0 -4
- package/interfaces/user-enricher.interface.d.ts +1 -4
- package/modules/auth.module.d.ts +6 -3
- package/package.json +4 -4
- package/services/auth-config.service.d.ts +6 -8
- package/services/auth-datasource.provider.d.ts +2 -4
- package/services/auth-email.service.d.ts +8 -4
- package/services/authentication.service.d.ts +15 -16
- package/services/company-selection-session.service.d.ts +1 -1
- package/services/user-permission.service.d.ts +6 -9
- package/services/user.service.d.ts +3 -0
- package/strategies/jwt.strategy.d.ts +4 -1
- package/validators/password.validator.d.ts +10 -0
- package/cjs/dtos/user-company-permission.dto.js +0 -110
- package/dtos/user-company-permission.dto.d.ts +0 -10
- package/fesm/dtos/user-company-permission.dto.js +0 -98
|
@@ -25,62 +25,59 @@ 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
|
-
import { AuthEmailService } from './auth-email.service';
|
|
40
34
|
import { CompanyBranch } from '../entities/company-branch.entity';
|
|
41
35
|
import { Company } from '../entities/company.entity';
|
|
42
36
|
import { UserCompanyPermission } from '../entities/user-company-permission.entity';
|
|
43
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';
|
|
44
42
|
import { CompanySelectionSessionService } from './company-selection-session.service';
|
|
45
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
|
+
}
|
|
46
48
|
// Repository Getters
|
|
47
49
|
async getUserRepository() {
|
|
48
|
-
if (this.dataSourceProvider) {
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
if (this.injectedUserRepository) {
|
|
52
|
-
return this.injectedUserRepository;
|
|
50
|
+
if (!this.dataSourceProvider) {
|
|
51
|
+
throw new Error('User repository not available');
|
|
53
52
|
}
|
|
54
|
-
|
|
53
|
+
return this.dataSourceProvider.getRepository(User);
|
|
55
54
|
}
|
|
56
55
|
async getPermissionRepository() {
|
|
57
|
-
if (!this.
|
|
58
|
-
|
|
59
|
-
return this.dataSourceProvider.getRepository(UserCompanyPermission);
|
|
60
|
-
}
|
|
61
|
-
return this.injectedPermissionRepository ?? null;
|
|
56
|
+
if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
|
|
57
|
+
return this.dataSourceProvider.getRepository(UserCompanyPermission);
|
|
62
58
|
}
|
|
63
59
|
async getCompanyRepository() {
|
|
64
|
-
if (!this.
|
|
65
|
-
|
|
66
|
-
return this.dataSourceProvider.getRepository(Company);
|
|
67
|
-
}
|
|
68
|
-
return this.injectedCompanyRepository ?? null;
|
|
60
|
+
if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
|
|
61
|
+
return this.dataSourceProvider.getRepository(Company);
|
|
69
62
|
}
|
|
70
63
|
async getBranchRepository() {
|
|
71
|
-
if (!this.
|
|
72
|
-
|
|
73
|
-
return this.dataSourceProvider.getRepository(CompanyBranch);
|
|
74
|
-
}
|
|
75
|
-
return this.injectedBranchRepository ?? null;
|
|
64
|
+
if (!this.isCompanyFeatureEnabled() || !this.dataSourceProvider) return null;
|
|
65
|
+
return this.dataSourceProvider.getRepository(CompanyBranch);
|
|
76
66
|
}
|
|
77
67
|
// Login
|
|
78
|
-
|
|
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
|
+
}
|
|
79
77
|
const user = await this.validateUser(email, password);
|
|
80
78
|
if (!user.isActive) {
|
|
81
79
|
throw new UnauthorizedException('User account is deactivated');
|
|
82
80
|
}
|
|
83
|
-
// Check email verification if email service is enabled
|
|
84
81
|
if (this.authEmailService?.isEmailEnabled() && !user.emailVerified) {
|
|
85
82
|
return {
|
|
86
83
|
requiresEmailVerification: true,
|
|
@@ -88,7 +85,7 @@ export class AuthenticationService {
|
|
|
88
85
|
message: 'Please verify your email address before logging in.'
|
|
89
86
|
};
|
|
90
87
|
}
|
|
91
|
-
if (!this.
|
|
88
|
+
if (!this.isCompanyFeatureEnabled()) {
|
|
92
89
|
return this.generateLoginResponse(user);
|
|
93
90
|
}
|
|
94
91
|
return this.handleCompanyBasedLogin(user);
|
|
@@ -120,7 +117,7 @@ export class AuthenticationService {
|
|
|
120
117
|
// If user has no company assignments yet, return simple login response
|
|
121
118
|
// This allows users to login and be assigned to companies later
|
|
122
119
|
if (companies.length === 0) {
|
|
123
|
-
return this.generateLoginResponse(user
|
|
120
|
+
return this.generateLoginResponse(user);
|
|
124
121
|
}
|
|
125
122
|
// Get branch permissions
|
|
126
123
|
const branchPermissions = await permissionRepository.find({
|
|
@@ -151,12 +148,17 @@ export class AuthenticationService {
|
|
|
151
148
|
email: dto.email
|
|
152
149
|
}
|
|
153
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
|
|
154
156
|
if (existingUser) {
|
|
155
|
-
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.');
|
|
156
158
|
}
|
|
157
159
|
// If email verification is enabled, create pending registration instead of user
|
|
158
160
|
if (this.authEmailService?.isEmailEnabled() && verifyUrlBase) {
|
|
159
|
-
const companyData = this.
|
|
161
|
+
const companyData = this.isCompanyFeatureEnabled() ? {
|
|
160
162
|
companySlug: dto.companySlug,
|
|
161
163
|
branchSlug: dto.branchSlug,
|
|
162
164
|
newCompanyName: dto.newCompanyName,
|
|
@@ -179,6 +181,38 @@ export class AuthenticationService {
|
|
|
179
181
|
// No email verification - create user directly
|
|
180
182
|
return this.createUserDirectly(dto);
|
|
181
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');
|
|
192
|
+
}
|
|
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
|
+
}
|
|
182
216
|
/** Create user directly (no email verification) */ async createUserDirectly(dto) {
|
|
183
217
|
const userRepository = await this.getUserRepository();
|
|
184
218
|
const hashedPassword = await bcrypt.hash(dto.password, BCRYPT_SALT_ROUNDS);
|
|
@@ -192,14 +226,13 @@ export class AuthenticationService {
|
|
|
192
226
|
phoneVerified: false
|
|
193
227
|
});
|
|
194
228
|
await userRepository.save(user);
|
|
195
|
-
if (!this.
|
|
196
|
-
return this.generateRegistrationResponse(user
|
|
229
|
+
if (!this.isCompanyFeatureEnabled()) {
|
|
230
|
+
return this.generateRegistrationResponse(user);
|
|
197
231
|
}
|
|
198
|
-
return this.
|
|
232
|
+
return this.processCompanyRegistration(user, dto);
|
|
199
233
|
}
|
|
200
234
|
/** Create user from pending registration (after email verification) */ async createUserFromPendingRegistration(pending) {
|
|
201
235
|
const userRepository = await this.getUserRepository();
|
|
202
|
-
// Check if user already exists (race condition protection)
|
|
203
236
|
const existingUser = await userRepository.findOne({
|
|
204
237
|
where: {
|
|
205
238
|
email: pending.email
|
|
@@ -208,7 +241,6 @@ export class AuthenticationService {
|
|
|
208
241
|
if (existingUser) {
|
|
209
242
|
throw new BadRequestException('This email has already been registered.');
|
|
210
243
|
}
|
|
211
|
-
// Create user with verified email
|
|
212
244
|
const user = userRepository.create({
|
|
213
245
|
name: pending.name,
|
|
214
246
|
email: pending.email,
|
|
@@ -219,107 +251,103 @@ export class AuthenticationService {
|
|
|
219
251
|
phoneVerified: false
|
|
220
252
|
});
|
|
221
253
|
await userRepository.save(user);
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return this.handleCompanyRegistrationFromPending(user, pending.companyData);
|
|
254
|
+
if (this.isCompanyFeatureEnabled() && pending.companyData) {
|
|
255
|
+
return this.processCompanyRegistration(user, pending.companyData);
|
|
225
256
|
}
|
|
226
|
-
return this.generateRegistrationResponse(user
|
|
227
|
-
}
|
|
228
|
-
/** Handle company registration from pending registration data */ async handleCompanyRegistrationFromPending(user, companyData) {
|
|
229
|
-
return this.processCompanyRegistration(user, companyData, false);
|
|
230
|
-
}
|
|
231
|
-
/** Handle company-related registration logic */ async handleCompanyRegistration(user, dto, requiresEmailVerification) {
|
|
232
|
-
return this.processCompanyRegistration(user, dto, requiresEmailVerification);
|
|
257
|
+
return this.generateRegistrationResponse(user);
|
|
233
258
|
}
|
|
234
|
-
/**
|
|
259
|
+
/** Process company registration (join existing or create new) */ async processCompanyRegistration(user, data, throwOnUnavailable = false) {
|
|
235
260
|
const companyRepository = await this.getCompanyRepository();
|
|
236
261
|
const branchRepository = await this.getBranchRepository();
|
|
237
262
|
const permissionRepository = await this.getPermissionRepository();
|
|
238
263
|
if (!companyRepository || !branchRepository || !permissionRepository) {
|
|
239
|
-
|
|
264
|
+
if (throwOnUnavailable) throw new BadRequestException('Company feature repositories not available');
|
|
265
|
+
return this.generateRegistrationResponse(user);
|
|
240
266
|
}
|
|
241
|
-
|
|
242
|
-
|
|
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) {
|
|
243
275
|
if (data.companySlug) {
|
|
244
|
-
company = await
|
|
276
|
+
const company = await companyRepo.findOne({
|
|
245
277
|
where: {
|
|
246
278
|
slug: data.companySlug,
|
|
247
279
|
isActive: true
|
|
248
280
|
}
|
|
249
281
|
});
|
|
250
|
-
if (!company)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
slug: data.branchSlug,
|
|
257
|
-
companyId: company.id,
|
|
258
|
-
isActive: true
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
if (!branch) {
|
|
262
|
-
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
|
|
263
288
|
}
|
|
264
|
-
}
|
|
265
|
-
branch = await branchRepository.findOne({
|
|
266
|
-
where: {
|
|
267
|
-
companyId: company.id,
|
|
268
|
-
isActive: true
|
|
269
|
-
},
|
|
270
|
-
order: {
|
|
271
|
-
serial: 'ASC',
|
|
272
|
-
createdAt: 'ASC'
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
} else if (data.newCompanyName) {
|
|
277
|
-
const slug = this.generateSlug(data.newCompanyName);
|
|
278
|
-
const existingCompany = await companyRepository.findOne({
|
|
289
|
+
}) : await branchRepo.findOne({
|
|
279
290
|
where: {
|
|
280
|
-
|
|
291
|
+
companyId: company.id,
|
|
292
|
+
isActive: true
|
|
293
|
+
},
|
|
294
|
+
order: {
|
|
295
|
+
serial: 'ASC',
|
|
296
|
+
createdAt: 'ASC'
|
|
281
297
|
}
|
|
282
298
|
});
|
|
283
|
-
if (
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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({
|
|
287
314
|
name: data.newCompanyName,
|
|
288
315
|
slug,
|
|
289
316
|
phone: data.newCompanyPhone,
|
|
290
317
|
address: data.newCompanyAddress,
|
|
291
318
|
isActive: true
|
|
292
319
|
});
|
|
293
|
-
await
|
|
294
|
-
branch =
|
|
320
|
+
await companyRepo.save(company);
|
|
321
|
+
const branch = branchRepo.create({
|
|
295
322
|
name: 'Main Branch',
|
|
296
323
|
slug: 'main-branch',
|
|
297
324
|
companyId: company.id,
|
|
298
325
|
isActive: true
|
|
299
326
|
});
|
|
300
|
-
await
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
permissionType: PermissionType.COMPANY,
|
|
306
|
-
targetId: company.id
|
|
307
|
-
});
|
|
308
|
-
await permissionRepository.save(companyPermission);
|
|
309
|
-
if (branch) {
|
|
310
|
-
const branchPermission = permissionRepository.create({
|
|
311
|
-
userId: user.id,
|
|
312
|
-
permissionType: PermissionType.BRANCH,
|
|
313
|
-
targetId: branch.id
|
|
314
|
-
});
|
|
315
|
-
await permissionRepository.save(branchPermission);
|
|
316
|
-
}
|
|
317
|
-
return this.generateRegistrationResponseWithCompany(user, company, branch ?? undefined, requiresEmailVerification);
|
|
327
|
+
await branchRepo.save(branch);
|
|
328
|
+
return {
|
|
329
|
+
company,
|
|
330
|
+
branch
|
|
331
|
+
};
|
|
318
332
|
}
|
|
319
|
-
return
|
|
333
|
+
return {
|
|
334
|
+
company: null,
|
|
335
|
+
branch: null
|
|
336
|
+
};
|
|
320
337
|
}
|
|
321
|
-
/**
|
|
322
|
-
|
|
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
|
+
}
|
|
323
351
|
}
|
|
324
352
|
// Company/Branch Selection
|
|
325
353
|
/** Select company and branch (step 2 of login). Frontend receives all options and user selects both. */ async select(sessionId, companyId, branchId) {
|
|
@@ -331,66 +359,13 @@ export class AuthenticationService {
|
|
|
331
359
|
if (!session) {
|
|
332
360
|
throw new UnauthorizedException('Invalid or expired session');
|
|
333
361
|
}
|
|
334
|
-
|
|
335
|
-
const user = await userRepository.findOne({
|
|
336
|
-
where: {
|
|
337
|
-
id: session.userId
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
if (!user) {
|
|
341
|
-
throw new NotFoundException('User not found');
|
|
342
|
-
}
|
|
343
|
-
const permissionRepository = await this.getPermissionRepository();
|
|
344
|
-
const companyRepository = await this.getCompanyRepository();
|
|
345
|
-
const branchRepository = await this.getBranchRepository();
|
|
346
|
-
if (!permissionRepository || !companyRepository || !branchRepository) {
|
|
347
|
-
throw new BadRequestException('Repositories not available');
|
|
348
|
-
}
|
|
349
|
-
// Validate company access
|
|
350
|
-
const companyPermission = await permissionRepository.findOne({
|
|
351
|
-
where: {
|
|
352
|
-
userId: user.id,
|
|
353
|
-
permissionType: PermissionType.COMPANY,
|
|
354
|
-
targetId: companyId
|
|
355
|
-
}
|
|
356
|
-
});
|
|
357
|
-
if (!companyPermission) {
|
|
358
|
-
throw new UnauthorizedException('No access to this company');
|
|
359
|
-
}
|
|
360
|
-
const company = await companyRepository.findOne({
|
|
361
|
-
where: {
|
|
362
|
-
id: companyId,
|
|
363
|
-
isActive: true
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
if (!company) {
|
|
367
|
-
throw new UnauthorizedException('Company not found or inactive');
|
|
368
|
-
}
|
|
369
|
-
// Validate branch access
|
|
370
|
-
const branchPermission = await permissionRepository.findOne({
|
|
371
|
-
where: {
|
|
372
|
-
userId: user.id,
|
|
373
|
-
permissionType: PermissionType.BRANCH,
|
|
374
|
-
targetId: branchId
|
|
375
|
-
}
|
|
376
|
-
});
|
|
377
|
-
if (!branchPermission) {
|
|
378
|
-
throw new UnauthorizedException('No access to this branch');
|
|
379
|
-
}
|
|
380
|
-
const branch = await branchRepository.findOne({
|
|
381
|
-
where: {
|
|
382
|
-
id: branchId,
|
|
383
|
-
companyId,
|
|
384
|
-
isActive: true
|
|
385
|
-
}
|
|
386
|
-
});
|
|
387
|
-
if (!branch) {
|
|
388
|
-
throw new UnauthorizedException('Branch not found or inactive');
|
|
389
|
-
}
|
|
390
|
-
return this.generateLoginResponseWithCompany(user, company, branch);
|
|
362
|
+
return this.validateAndLoginWithCompany(session.userId, companyId, branchId);
|
|
391
363
|
}
|
|
392
364
|
/** Switch company/branch after login */ async switchCompany(userId, companyId, branchId) {
|
|
393
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) {
|
|
394
369
|
const userRepository = await this.getUserRepository();
|
|
395
370
|
const user = await userRepository.findOne({
|
|
396
371
|
where: {
|
|
@@ -407,14 +382,14 @@ export class AuthenticationService {
|
|
|
407
382
|
throw new BadRequestException('Repositories not available');
|
|
408
383
|
}
|
|
409
384
|
// Validate company access
|
|
410
|
-
const
|
|
385
|
+
const hasCompanyAccess = await permissionRepository.findOne({
|
|
411
386
|
where: {
|
|
412
387
|
userId,
|
|
413
388
|
permissionType: PermissionType.COMPANY,
|
|
414
389
|
targetId: companyId
|
|
415
390
|
}
|
|
416
391
|
});
|
|
417
|
-
if (!
|
|
392
|
+
if (!hasCompanyAccess) {
|
|
418
393
|
throw new UnauthorizedException('No access to this company');
|
|
419
394
|
}
|
|
420
395
|
const company = await companyRepository.findOne({
|
|
@@ -426,14 +401,15 @@ export class AuthenticationService {
|
|
|
426
401
|
if (!company) {
|
|
427
402
|
throw new UnauthorizedException('Company not found or inactive');
|
|
428
403
|
}
|
|
429
|
-
|
|
404
|
+
// Validate branch access
|
|
405
|
+
const hasBranchAccess = await permissionRepository.findOne({
|
|
430
406
|
where: {
|
|
431
407
|
userId,
|
|
432
408
|
permissionType: PermissionType.BRANCH,
|
|
433
409
|
targetId: branchId
|
|
434
410
|
}
|
|
435
411
|
});
|
|
436
|
-
if (!
|
|
412
|
+
if (!hasBranchAccess) {
|
|
437
413
|
throw new UnauthorizedException('No access to this branch');
|
|
438
414
|
}
|
|
439
415
|
const branch = await branchRepository.findOne({
|
|
@@ -454,6 +430,9 @@ export class AuthenticationService {
|
|
|
454
430
|
const payload = this.jwtService.verify(refreshToken, {
|
|
455
431
|
secret: this.authConfig.getRefreshTokenSecret()
|
|
456
432
|
});
|
|
433
|
+
if (await this.isTokenRevoked(payload.id, payload.iat)) {
|
|
434
|
+
throw new UnauthorizedException('Token has been revoked');
|
|
435
|
+
}
|
|
457
436
|
const userRepository = await this.getUserRepository();
|
|
458
437
|
const user = await userRepository.findOne({
|
|
459
438
|
where: {
|
|
@@ -463,7 +442,7 @@ export class AuthenticationService {
|
|
|
463
442
|
if (!user || !user.isActive) {
|
|
464
443
|
throw new UnauthorizedException('Invalid refresh token');
|
|
465
444
|
}
|
|
466
|
-
|
|
445
|
+
return this.generateTokens({
|
|
467
446
|
id: user.id,
|
|
468
447
|
email: user.email,
|
|
469
448
|
type: 'access',
|
|
@@ -473,44 +452,9 @@ export class AuthenticationService {
|
|
|
473
452
|
},
|
|
474
453
|
...payload.branchId && {
|
|
475
454
|
branchId: payload.branchId
|
|
476
|
-
},
|
|
477
|
-
...payload.companyLogoId && {
|
|
478
|
-
companyLogoId: payload.companyLogoId
|
|
479
|
-
},
|
|
480
|
-
...payload.branchLogoId && {
|
|
481
|
-
branchLogoId: payload.branchLogoId
|
|
482
|
-
}
|
|
483
|
-
};
|
|
484
|
-
const refreshPayload = {
|
|
485
|
-
id: user.id,
|
|
486
|
-
email: user.email,
|
|
487
|
-
type: 'refresh',
|
|
488
|
-
profilePictureId: user.profilePictureId ?? undefined,
|
|
489
|
-
...payload.companyId && {
|
|
490
|
-
companyId: payload.companyId
|
|
491
|
-
},
|
|
492
|
-
...payload.branchId && {
|
|
493
|
-
branchId: payload.branchId
|
|
494
|
-
},
|
|
495
|
-
...payload.companyLogoId && {
|
|
496
|
-
companyLogoId: payload.companyLogoId
|
|
497
|
-
},
|
|
498
|
-
...payload.branchLogoId && {
|
|
499
|
-
branchLogoId: payload.branchLogoId
|
|
500
455
|
}
|
|
501
|
-
};
|
|
502
|
-
return {
|
|
503
|
-
accessToken: this.jwtService.sign(accessPayload, {
|
|
504
|
-
secret: this.authConfig.getJwtSecret(),
|
|
505
|
-
expiresIn: this.authConfig.getJwtExpiration()
|
|
506
|
-
}),
|
|
507
|
-
refreshToken: this.jwtService.sign(refreshPayload, {
|
|
508
|
-
secret: this.authConfig.getRefreshTokenSecret(),
|
|
509
|
-
expiresIn: this.authConfig.getRefreshTokenExpiration()
|
|
510
|
-
})
|
|
511
|
-
};
|
|
456
|
+
});
|
|
512
457
|
} catch (error) {
|
|
513
|
-
// Log error details for debugging (not exposed to client)
|
|
514
458
|
ErrorHandler.logError(this.logger, error, 'refreshToken');
|
|
515
459
|
throw new UnauthorizedException('Invalid or expired refresh token');
|
|
516
460
|
}
|
|
@@ -522,11 +466,8 @@ export class AuthenticationService {
|
|
|
522
466
|
id: userId
|
|
523
467
|
}
|
|
524
468
|
});
|
|
525
|
-
if (!user)
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
const isPasswordValid = await bcrypt.compare(currentPassword, user.password || '');
|
|
529
|
-
if (!isPasswordValid) {
|
|
469
|
+
if (!user) throw new UnauthorizedException('User not found');
|
|
470
|
+
if (!await bcrypt.compare(currentPassword, user.password || '')) {
|
|
530
471
|
throw new UnauthorizedException('Current password is incorrect');
|
|
531
472
|
}
|
|
532
473
|
user.password = await bcrypt.hash(newPassword, BCRYPT_SALT_ROUNDS);
|
|
@@ -534,8 +475,9 @@ export class AuthenticationService {
|
|
|
534
475
|
}
|
|
535
476
|
/** Logout - Revokes all tokens. Stores revocation timestamp in cache. */ async logout(userId) {
|
|
536
477
|
if (!this.cacheManager) {
|
|
537
|
-
// Cache
|
|
538
|
-
|
|
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.');
|
|
539
481
|
}
|
|
540
482
|
const revokedAt = Math.floor(Date.now() / 1000); // Unix timestamp in seconds
|
|
541
483
|
const cacheKey = `${TOKEN_REVOKED_PREFIX}:${userId}`;
|
|
@@ -543,6 +485,7 @@ export class AuthenticationService {
|
|
|
543
485
|
const refreshTokenExpiry = this.authConfig.getRefreshTokenExpiration();
|
|
544
486
|
const ttlMs = this.parseExpiryToMs(refreshTokenExpiry);
|
|
545
487
|
await this.cacheManager.set(cacheKey, revokedAt, ttlMs);
|
|
488
|
+
this.logger.log(`User ${userId} logged out - all tokens revoked`);
|
|
546
489
|
}
|
|
547
490
|
/** Check if a token is revoked (issued before user's revocation timestamp) */ async isTokenRevoked(userId, tokenIssuedAt) {
|
|
548
491
|
if (!this.cacheManager || !tokenIssuedAt) {
|
|
@@ -660,54 +603,28 @@ export class AuthenticationService {
|
|
|
660
603
|
id: userId
|
|
661
604
|
}
|
|
662
605
|
});
|
|
663
|
-
if (!user)
|
|
664
|
-
throw new UnauthorizedException('User not found');
|
|
665
|
-
}
|
|
666
|
-
// Build response (no tokens)
|
|
606
|
+
if (!user) throw new UnauthorizedException('User not found');
|
|
667
607
|
const response = {
|
|
668
|
-
user:
|
|
669
|
-
id: user.id,
|
|
670
|
-
email: user.email,
|
|
671
|
-
name: user.name ?? undefined,
|
|
672
|
-
phone: user.phone ?? undefined,
|
|
673
|
-
profilePictureId: user.profilePictureId ?? undefined
|
|
674
|
-
}
|
|
608
|
+
user: this.toUserInfo(user)
|
|
675
609
|
};
|
|
676
|
-
|
|
677
|
-
if (this.authConfig.isCompanyFeatureEnabled()) {
|
|
678
|
-
// When company feature is enabled, companyId MUST be present
|
|
610
|
+
if (this.isCompanyFeatureEnabled()) {
|
|
679
611
|
if (!companyId) {
|
|
680
|
-
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.');
|
|
681
613
|
}
|
|
682
614
|
const company = await this.getCompanyById(companyId);
|
|
683
|
-
if (!company)
|
|
684
|
-
|
|
685
|
-
}
|
|
686
|
-
response.company = {
|
|
687
|
-
id: company.id,
|
|
688
|
-
name: company.name,
|
|
689
|
-
slug: company.slug,
|
|
690
|
-
logoId: company.logoId ?? null
|
|
691
|
-
};
|
|
692
|
-
// 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);
|
|
693
617
|
if (branchId) {
|
|
694
618
|
const branch = await this.getBranchById(branchId);
|
|
695
|
-
if (!branch)
|
|
696
|
-
|
|
697
|
-
}
|
|
698
|
-
response.branch = {
|
|
699
|
-
id: branch.id,
|
|
700
|
-
name: branch.name,
|
|
701
|
-
slug: branch.slug,
|
|
702
|
-
logoId: branch.logoId ?? null
|
|
703
|
-
};
|
|
619
|
+
if (!branch) throw new UnauthorizedException('Branch not found.');
|
|
620
|
+
response.branch = this.toBranchInfo(branch);
|
|
704
621
|
}
|
|
705
622
|
}
|
|
706
623
|
return response;
|
|
707
624
|
}
|
|
708
625
|
// Helper Methods
|
|
709
626
|
ensureCompanyFeatureEnabled() {
|
|
710
|
-
if (!this.
|
|
627
|
+
if (!this.isCompanyFeatureEnabled()) {
|
|
711
628
|
throw new BadRequestException('Company feature is not enabled');
|
|
712
629
|
}
|
|
713
630
|
}
|
|
@@ -718,11 +635,7 @@ export class AuthenticationService {
|
|
|
718
635
|
email
|
|
719
636
|
}
|
|
720
637
|
});
|
|
721
|
-
if (!user) {
|
|
722
|
-
throw new UnauthorizedException('Invalid credentials');
|
|
723
|
-
}
|
|
724
|
-
const isPasswordValid = await bcrypt.compare(password, user.password || '');
|
|
725
|
-
if (!isPasswordValid) {
|
|
638
|
+
if (!user || !await bcrypt.compare(password, user.password || '')) {
|
|
726
639
|
throw new UnauthorizedException('Invalid credentials');
|
|
727
640
|
}
|
|
728
641
|
user.lastLoginAt = new Date();
|
|
@@ -730,84 +643,77 @@ export class AuthenticationService {
|
|
|
730
643
|
return user;
|
|
731
644
|
}
|
|
732
645
|
// Response Generators
|
|
733
|
-
|
|
734
|
-
|
|
646
|
+
/** Create user info object */ toUserInfo(user) {
|
|
647
|
+
return {
|
|
735
648
|
id: user.id,
|
|
736
649
|
email: user.email,
|
|
737
|
-
|
|
650
|
+
name: user.name ?? undefined,
|
|
651
|
+
phone: user.phone ?? null,
|
|
652
|
+
profilePictureId: user.profilePictureId ?? null
|
|
653
|
+
};
|
|
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
|
|
738
669
|
};
|
|
739
|
-
|
|
670
|
+
}
|
|
671
|
+
/** Generate JWT tokens */ generateTokens(payload) {
|
|
672
|
+
const accessToken = this.jwtService.sign({
|
|
673
|
+
...payload,
|
|
674
|
+
type: 'access'
|
|
675
|
+
}, {
|
|
740
676
|
secret: this.authConfig.getJwtSecret(),
|
|
741
677
|
expiresIn: this.authConfig.getJwtExpiration()
|
|
742
678
|
});
|
|
743
|
-
const
|
|
744
|
-
|
|
745
|
-
email: user.email,
|
|
679
|
+
const refreshToken = this.jwtService.sign({
|
|
680
|
+
...payload,
|
|
746
681
|
type: 'refresh'
|
|
747
|
-
}
|
|
748
|
-
const refreshToken = this.jwtService.sign(refreshPayload, {
|
|
682
|
+
}, {
|
|
749
683
|
secret: this.authConfig.getRefreshTokenSecret(),
|
|
750
684
|
expiresIn: this.authConfig.getRefreshTokenExpiration()
|
|
751
685
|
});
|
|
752
686
|
return {
|
|
753
687
|
accessToken,
|
|
754
|
-
refreshToken
|
|
755
|
-
user: {
|
|
756
|
-
id: user.id,
|
|
757
|
-
email: user.email,
|
|
758
|
-
name: user.name ?? undefined,
|
|
759
|
-
phone: user.phone ?? null,
|
|
760
|
-
profilePictureId: user.profilePictureId ?? null
|
|
761
|
-
},
|
|
762
|
-
companyFeatureEnabled
|
|
688
|
+
refreshToken
|
|
763
689
|
};
|
|
764
690
|
}
|
|
765
|
-
|
|
766
|
-
const
|
|
691
|
+
generateLoginResponse(user) {
|
|
692
|
+
const { accessToken, refreshToken } = this.generateTokens({
|
|
767
693
|
id: user.id,
|
|
768
694
|
email: user.email,
|
|
769
|
-
type: 'access'
|
|
770
|
-
companyId: company.id,
|
|
771
|
-
branchId: branch?.id
|
|
772
|
-
};
|
|
773
|
-
const accessToken = this.jwtService.sign(payload, {
|
|
774
|
-
secret: this.authConfig.getJwtSecret(),
|
|
775
|
-
expiresIn: this.authConfig.getJwtExpiration()
|
|
695
|
+
type: 'access'
|
|
776
696
|
});
|
|
777
|
-
|
|
697
|
+
return {
|
|
698
|
+
accessToken,
|
|
699
|
+
refreshToken,
|
|
700
|
+
user: this.toUserInfo(user)
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
generateLoginResponseWithCompany(user, company, branch) {
|
|
704
|
+
const { accessToken, refreshToken } = this.generateTokens({
|
|
778
705
|
id: user.id,
|
|
779
706
|
email: user.email,
|
|
780
|
-
type: '
|
|
707
|
+
type: 'access',
|
|
781
708
|
companyId: company.id,
|
|
782
709
|
branchId: branch?.id
|
|
783
|
-
};
|
|
784
|
-
const refreshToken = this.jwtService.sign(refreshPayload, {
|
|
785
|
-
secret: this.authConfig.getRefreshTokenSecret(),
|
|
786
|
-
expiresIn: this.authConfig.getRefreshTokenExpiration()
|
|
787
710
|
});
|
|
788
711
|
return {
|
|
789
712
|
accessToken,
|
|
790
713
|
refreshToken,
|
|
791
|
-
user:
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
name: user.name ?? undefined,
|
|
795
|
-
phone: user.phone ?? null,
|
|
796
|
-
profilePictureId: user.profilePictureId ?? null
|
|
797
|
-
},
|
|
798
|
-
company: {
|
|
799
|
-
id: company.id,
|
|
800
|
-
name: company.name,
|
|
801
|
-
slug: company.slug,
|
|
802
|
-
logoId: company.logoId ?? null
|
|
803
|
-
},
|
|
804
|
-
branch: branch ? {
|
|
805
|
-
id: branch.id,
|
|
806
|
-
name: branch.name,
|
|
807
|
-
slug: branch.slug,
|
|
808
|
-
logoId: branch.logoId ?? null
|
|
809
|
-
} : undefined,
|
|
810
|
-
companyFeatureEnabled: true
|
|
714
|
+
user: this.toUserInfo(user),
|
|
715
|
+
company: this.toCompanyInfo(company),
|
|
716
|
+
branch: branch ? this.toBranchInfo(branch) : undefined
|
|
811
717
|
};
|
|
812
718
|
}
|
|
813
719
|
async generateCompanySelectionResponse(user, companies, branches) {
|
|
@@ -815,103 +721,42 @@ export class AuthenticationService {
|
|
|
815
721
|
throw new BadRequestException('Session service not available');
|
|
816
722
|
}
|
|
817
723
|
const sessionId = await this.sessionService.createSession(user.id);
|
|
818
|
-
// Build companies with their branches
|
|
819
724
|
const companiesWithBranches = companies.map((company)=>({
|
|
820
|
-
|
|
821
|
-
name: company.name,
|
|
822
|
-
slug: company.slug,
|
|
725
|
+
...this.toCompanyInfo(company),
|
|
823
726
|
isActive: company.isActive,
|
|
824
|
-
logoId: company.logoId ?? null,
|
|
825
727
|
branches: branches.filter((b)=>b.companyId === company.id).map((b)=>({
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
slug: b.slug,
|
|
829
|
-
isActive: b.isActive,
|
|
830
|
-
logoId: b.logoId ?? null
|
|
728
|
+
...this.toBranchInfo(b),
|
|
729
|
+
isActive: b.isActive
|
|
831
730
|
}))
|
|
832
731
|
}));
|
|
833
732
|
return {
|
|
834
733
|
requiresSelection: true,
|
|
835
734
|
sessionId,
|
|
836
735
|
expiresAt: new Date(Date.now() + 5 * 60 * 1000),
|
|
837
|
-
user:
|
|
838
|
-
id: user.id,
|
|
839
|
-
email: user.email,
|
|
840
|
-
name: user.name ?? undefined,
|
|
841
|
-
phone: user.phone ?? null,
|
|
842
|
-
profilePictureId: user.profilePictureId ?? null
|
|
843
|
-
},
|
|
736
|
+
user: this.toUserInfo(user),
|
|
844
737
|
companies: companiesWithBranches
|
|
845
738
|
};
|
|
846
739
|
}
|
|
847
|
-
|
|
848
|
-
const userInfo = {
|
|
849
|
-
id: user.id,
|
|
850
|
-
email: user.email,
|
|
851
|
-
name: user.name ?? undefined,
|
|
852
|
-
phone: user.phone ?? null,
|
|
853
|
-
profilePictureId: user.profilePictureId ?? null
|
|
854
|
-
};
|
|
855
|
-
if (requiresEmailVerification) {
|
|
856
|
-
return {
|
|
857
|
-
user: userInfo,
|
|
858
|
-
companyFeatureEnabled,
|
|
859
|
-
requiresEmailVerification: true
|
|
860
|
-
};
|
|
861
|
-
}
|
|
862
|
-
const loginResponse = this.generateLoginResponse(user, companyFeatureEnabled);
|
|
740
|
+
generateRegistrationResponse(user) {
|
|
863
741
|
return {
|
|
864
|
-
...
|
|
742
|
+
...this.generateLoginResponse(user),
|
|
865
743
|
requiresEmailVerification: false
|
|
866
744
|
};
|
|
867
745
|
}
|
|
868
|
-
|
|
869
|
-
const userInfo = {
|
|
870
|
-
id: user.id,
|
|
871
|
-
email: user.email,
|
|
872
|
-
name: user.name ?? undefined,
|
|
873
|
-
phone: user.phone ?? null,
|
|
874
|
-
profilePictureId: user.profilePictureId ?? null
|
|
875
|
-
};
|
|
876
|
-
const companyInfo = {
|
|
877
|
-
id: company.id,
|
|
878
|
-
name: company.name,
|
|
879
|
-
slug: company.slug,
|
|
880
|
-
logoId: company.logoId ?? null
|
|
881
|
-
};
|
|
882
|
-
const branchInfo = branch ? {
|
|
883
|
-
id: branch.id,
|
|
884
|
-
name: branch.name,
|
|
885
|
-
slug: branch.slug,
|
|
886
|
-
logoId: branch.logoId ?? null
|
|
887
|
-
} : undefined;
|
|
888
|
-
if (requiresEmailVerification) {
|
|
889
|
-
return {
|
|
890
|
-
user: userInfo,
|
|
891
|
-
company: companyInfo,
|
|
892
|
-
branch: branchInfo,
|
|
893
|
-
companyFeatureEnabled: true,
|
|
894
|
-
requiresEmailVerification: true
|
|
895
|
-
};
|
|
896
|
-
}
|
|
897
|
-
const loginResponse = this.generateLoginResponseWithCompany(user, company, branch);
|
|
746
|
+
generateRegistrationResponseWithCompany(user, company, branch) {
|
|
898
747
|
return {
|
|
899
|
-
...
|
|
748
|
+
...this.generateLoginResponseWithCompany(user, company, branch),
|
|
900
749
|
requiresEmailVerification: false
|
|
901
750
|
};
|
|
902
751
|
}
|
|
903
752
|
// NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
|
|
904
|
-
constructor(jwtService, authConfig, cacheManager, dataSourceProvider, sessionService, authEmailService
|
|
753
|
+
constructor(jwtService, authConfig, cacheManager, dataSourceProvider, sessionService, authEmailService){
|
|
905
754
|
_define_property(this, "jwtService", void 0);
|
|
906
755
|
_define_property(this, "authConfig", void 0);
|
|
907
756
|
_define_property(this, "cacheManager", void 0);
|
|
908
757
|
_define_property(this, "dataSourceProvider", void 0);
|
|
909
758
|
_define_property(this, "sessionService", void 0);
|
|
910
759
|
_define_property(this, "authEmailService", void 0);
|
|
911
|
-
_define_property(this, "injectedUserRepository", void 0);
|
|
912
|
-
_define_property(this, "injectedPermissionRepository", void 0);
|
|
913
|
-
_define_property(this, "injectedCompanyRepository", void 0);
|
|
914
|
-
_define_property(this, "injectedBranchRepository", void 0);
|
|
915
760
|
_define_property(this, "logger", void 0);
|
|
916
761
|
this.jwtService = jwtService;
|
|
917
762
|
this.authConfig = authConfig;
|
|
@@ -919,10 +764,6 @@ export class AuthenticationService {
|
|
|
919
764
|
this.dataSourceProvider = dataSourceProvider;
|
|
920
765
|
this.sessionService = sessionService;
|
|
921
766
|
this.authEmailService = authEmailService;
|
|
922
|
-
this.injectedUserRepository = injectedUserRepository;
|
|
923
|
-
this.injectedPermissionRepository = injectedPermissionRepository;
|
|
924
|
-
this.injectedCompanyRepository = injectedCompanyRepository;
|
|
925
|
-
this.injectedBranchRepository = injectedBranchRepository;
|
|
926
767
|
this.logger = new Logger(AuthenticationService.name);
|
|
927
768
|
}
|
|
928
769
|
}
|
|
@@ -940,14 +781,6 @@ AuthenticationService = _ts_decorate([
|
|
|
940
781
|
_ts_param(4, Inject(CompanySelectionSessionService)),
|
|
941
782
|
_ts_param(5, Optional()),
|
|
942
783
|
_ts_param(5, Inject(AuthEmailService)),
|
|
943
|
-
_ts_param(6, Optional()),
|
|
944
|
-
_ts_param(6, InjectRepository(User)),
|
|
945
|
-
_ts_param(7, Optional()),
|
|
946
|
-
_ts_param(7, InjectRepository(UserCompanyPermission)),
|
|
947
|
-
_ts_param(8, Optional()),
|
|
948
|
-
_ts_param(8, InjectRepository(Company)),
|
|
949
|
-
_ts_param(9, Optional()),
|
|
950
|
-
_ts_param(9, InjectRepository(CompanyBranch)),
|
|
951
784
|
_ts_metadata("design:type", Function),
|
|
952
785
|
_ts_metadata("design:paramtypes", [
|
|
953
786
|
typeof JwtService === "undefined" ? Object : JwtService,
|
|
@@ -955,10 +788,6 @@ AuthenticationService = _ts_decorate([
|
|
|
955
788
|
typeof HybridCache === "undefined" ? Object : HybridCache,
|
|
956
789
|
typeof AuthDataSourceProvider === "undefined" ? Object : AuthDataSourceProvider,
|
|
957
790
|
typeof CompanySelectionSessionService === "undefined" ? Object : CompanySelectionSessionService,
|
|
958
|
-
typeof AuthEmailService === "undefined" ? Object : AuthEmailService
|
|
959
|
-
typeof Repository === "undefined" ? Object : Repository,
|
|
960
|
-
typeof Repository === "undefined" ? Object : Repository,
|
|
961
|
-
typeof Repository === "undefined" ? Object : Repository,
|
|
962
|
-
typeof Repository === "undefined" ? Object : Repository
|
|
791
|
+
typeof AuthEmailService === "undefined" ? Object : AuthEmailService
|
|
963
792
|
])
|
|
964
793
|
], AuthenticationService);
|