@flusys/nestjs-auth 7.0.2 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +67 -0
  2. package/cjs/config/auth.constants.js +8 -0
  3. package/cjs/config/event-actions.js +4 -2
  4. package/cjs/config/message-keys.js +27 -0
  5. package/cjs/controllers/index.js +2 -0
  6. package/cjs/controllers/social-auth-config.controller.js +107 -0
  7. package/cjs/controllers/social-auth.controller.js +175 -0
  8. package/cjs/controllers/social-auth.controller.spec.js +79 -0
  9. package/cjs/docs/auth-swagger.config.js +9 -1
  10. package/cjs/dtos/index.js +1 -0
  11. package/cjs/dtos/social-auth.dto.js +318 -0
  12. package/cjs/entities/index.js +3 -1
  13. package/cjs/entities/index.spec.js +12 -1
  14. package/cjs/entities/social-auth-config.entity.js +102 -0
  15. package/cjs/enums/index.js +1 -0
  16. package/cjs/enums/social-provider-type.enum.js +17 -0
  17. package/cjs/index.js +1 -0
  18. package/cjs/interfaces/index.js +1 -0
  19. package/cjs/interfaces/social-auth.interface.js +4 -0
  20. package/cjs/modules/auth.module.js +6 -0
  21. package/cjs/modules/auth.module.spec.js +15 -0
  22. package/cjs/providers/facebook.social-provider.js +73 -0
  23. package/cjs/providers/google.social-provider.js +68 -0
  24. package/cjs/providers/index.js +18 -0
  25. package/cjs/providers/linkedin.social-provider.js +66 -0
  26. package/cjs/providers/microsoft.social-provider.js +78 -0
  27. package/cjs/providers/oauth-http.util.js +107 -0
  28. package/cjs/providers/social-provider.registry.js +24 -0
  29. package/cjs/providers/social-provider.registry.spec.js +135 -0
  30. package/cjs/services/auth-config.service.js +3 -0
  31. package/cjs/services/auth-config.service.spec.js +3 -0
  32. package/cjs/services/auth-datasource.provider.js +4 -0
  33. package/cjs/services/auth-datasource.provider.spec.js +30 -0
  34. package/cjs/services/authentication.service.js +38 -8
  35. package/cjs/services/authentication.service.spec.js +58 -0
  36. package/cjs/services/index.js +2 -0
  37. package/cjs/services/social-auth-config.service.js +103 -0
  38. package/cjs/services/social-auth-config.service.spec.js +71 -0
  39. package/cjs/services/social-auth.service.js +310 -0
  40. package/cjs/services/social-auth.service.spec.js +310 -0
  41. package/config/auth.constants.d.ts +2 -0
  42. package/config/event-actions.d.ts +2 -0
  43. package/config/message-keys.d.ts +21 -0
  44. package/controllers/index.d.ts +2 -0
  45. package/controllers/social-auth-config.controller.d.ts +22 -0
  46. package/controllers/social-auth.controller.d.ts +11 -0
  47. package/dtos/index.d.ts +1 -0
  48. package/dtos/social-auth.dto.d.ts +42 -0
  49. package/entities/index.d.ts +3 -1
  50. package/entities/social-auth-config.entity.d.ts +9 -0
  51. package/enums/index.d.ts +1 -0
  52. package/enums/social-provider-type.enum.d.ts +6 -0
  53. package/fesm/config/auth.constants.js +3 -0
  54. package/fesm/config/event-actions.js +4 -2
  55. package/fesm/config/message-keys.js +21 -0
  56. package/fesm/controllers/index.js +2 -0
  57. package/fesm/controllers/social-auth-config.controller.js +97 -0
  58. package/fesm/controllers/social-auth.controller.js +165 -0
  59. package/fesm/controllers/social-auth.controller.spec.js +75 -0
  60. package/fesm/docs/auth-swagger.config.js +9 -1
  61. package/fesm/dtos/index.js +1 -0
  62. package/fesm/dtos/social-auth.dto.js +282 -0
  63. package/fesm/entities/index.js +4 -1
  64. package/fesm/entities/index.spec.js +12 -1
  65. package/fesm/entities/social-auth-config.entity.js +92 -0
  66. package/fesm/enums/index.js +1 -0
  67. package/fesm/enums/social-provider-type.enum.js +7 -0
  68. package/fesm/index.js +1 -0
  69. package/fesm/interfaces/index.js +1 -0
  70. package/fesm/interfaces/social-auth.interface.js +1 -0
  71. package/fesm/modules/auth.module.js +8 -2
  72. package/fesm/modules/auth.module.spec.js +17 -2
  73. package/fesm/providers/facebook.social-provider.js +63 -0
  74. package/fesm/providers/google.social-provider.js +58 -0
  75. package/fesm/providers/index.js +1 -0
  76. package/fesm/providers/linkedin.social-provider.js +56 -0
  77. package/fesm/providers/microsoft.social-provider.js +68 -0
  78. package/fesm/providers/oauth-http.util.js +77 -0
  79. package/fesm/providers/social-provider.registry.js +14 -0
  80. package/fesm/providers/social-provider.registry.spec.js +131 -0
  81. package/fesm/services/auth-config.service.js +3 -0
  82. package/fesm/services/auth-config.service.spec.js +3 -0
  83. package/fesm/services/auth-datasource.provider.js +4 -0
  84. package/fesm/services/auth-datasource.provider.spec.js +30 -0
  85. package/fesm/services/authentication.service.js +38 -8
  86. package/fesm/services/authentication.service.spec.js +58 -0
  87. package/fesm/services/index.js +2 -0
  88. package/fesm/services/social-auth-config.service.js +93 -0
  89. package/fesm/services/social-auth-config.service.spec.js +67 -0
  90. package/fesm/services/social-auth.service.js +300 -0
  91. package/fesm/services/social-auth.service.spec.js +265 -0
  92. package/index.d.ts +1 -0
  93. package/interfaces/index.d.ts +1 -0
  94. package/interfaces/social-auth.interface.d.ts +43 -0
  95. package/package.json +3 -3
  96. package/providers/facebook.social-provider.d.ts +10 -0
  97. package/providers/google.social-provider.d.ts +10 -0
  98. package/providers/index.d.ts +1 -0
  99. package/providers/linkedin.social-provider.d.ts +10 -0
  100. package/providers/microsoft.social-provider.d.ts +10 -0
  101. package/providers/oauth-http.util.d.ts +10 -0
  102. package/providers/social-provider.registry.d.ts +3 -0
  103. package/services/auth-config.service.d.ts +1 -0
  104. package/services/auth-datasource.provider.d.ts +1 -0
  105. package/services/authentication.service.d.ts +4 -0
  106. package/services/index.d.ts +2 -0
  107. package/services/social-auth-config.service.d.ts +21 -0
  108. package/services/social-auth.service.d.ts +24 -0
@@ -131,6 +131,7 @@ describe('AuthenticationService', ()=>{
131
131
  let mockUserEnricher;
132
132
  let companyFeatureEnabled;
133
133
  let emailVerificationEnabled;
134
+ let signUpEnabled;
134
135
  function buildService(overrides = {}) {
135
136
  return new _authenticationservice.AuthenticationService(mockJwtService, mockAuthConfig, 'cacheManager' in overrides ? overrides.cacheManager : mockCacheManager, 'dataSourceProvider' in overrides ? overrides.dataSourceProvider : mockDataSourceProvider, 'sessionService' in overrides ? overrides.sessionService : mockSessionService, 'authEmailService' in overrides ? overrides.authEmailService : mockAuthEmailService, 'userEnricher' in overrides ? overrides.userEnricher : mockUserEnricher, 'notificationAdapter' in overrides ? overrides.notificationAdapter : undefined);
136
137
  }
@@ -147,6 +148,7 @@ describe('AuthenticationService', ()=>{
147
148
  });
148
149
  companyFeatureEnabled = false;
149
150
  emailVerificationEnabled = false;
151
+ signUpEnabled = true;
150
152
  mockJwtService = {
151
153
  sign: jest.fn().mockReturnValue('signed-token'),
152
154
  verify: jest.fn()
@@ -154,6 +156,7 @@ describe('AuthenticationService', ()=>{
154
156
  mockAuthConfig = {
155
157
  isCompanyFeatureEnabled: jest.fn().mockImplementation(()=>companyFeatureEnabled),
156
158
  isEmailVerificationEnabled: jest.fn().mockImplementation(()=>emailVerificationEnabled),
159
+ isSignUpEnabled: jest.fn().mockImplementation(()=>signUpEnabled),
157
160
  getJwtSecret: jest.fn().mockReturnValue('jwt-secret'),
158
161
  getJwtExpiration: jest.fn().mockReturnValue('1h'),
159
162
  getRefreshTokenSecret: jest.fn().mockReturnValue('refresh-secret'),
@@ -167,6 +170,7 @@ describe('AuthenticationService', ()=>{
167
170
  mockDataSourceProvider = {
168
171
  isCompanyFeatureEnabled: jest.fn().mockImplementation(()=>companyFeatureEnabled),
169
172
  isEmailVerificationEnabled: jest.fn().mockImplementation(()=>emailVerificationEnabled),
173
+ isSignUpEnabled: jest.fn().mockImplementation(()=>signUpEnabled),
170
174
  getRepository: jest.fn().mockImplementation(async (entity)=>{
171
175
  if (entity === _appuserentity.AppUser) return userRepo;
172
176
  if (entity === _companyentity.Company) return companyRepo;
@@ -341,6 +345,20 @@ describe('AuthenticationService', ()=>{
341
345
  password: 'Password1!'
342
346
  })).rejects.toThrow(_common.BadRequestException);
343
347
  });
348
+ it('throws BadRequestException when the email belongs to a permanently deleted account', async ()=>{
349
+ userRepo.findOne.mockResolvedValue(buildUser({
350
+ deletedAt: new Date()
351
+ }));
352
+ const service = buildService();
353
+ await expect(service.register({
354
+ email: 'user@example.com',
355
+ name: 'Test',
356
+ password: 'Password1!'
357
+ })).rejects.toThrow(_common.BadRequestException);
358
+ expect(userRepo.findOne).toHaveBeenCalledWith(expect.objectContaining({
359
+ withDeleted: true
360
+ }));
361
+ });
344
362
  it('routes existing users to company registration when the company feature is enabled', async ()=>{
345
363
  companyFeatureEnabled = true;
346
364
  userRepo.findOne.mockResolvedValue(buildUser());
@@ -362,6 +380,16 @@ describe('AuthenticationService', ()=>{
362
380
  accessToken: expect.any(String)
363
381
  }));
364
382
  });
383
+ it('throws BadRequestException for a new user when sign-up is disabled', async ()=>{
384
+ signUpEnabled = false;
385
+ userRepo.findOne.mockResolvedValue(null);
386
+ const service = buildService();
387
+ await expect(service.register({
388
+ email: 'new@example.com',
389
+ name: 'New User',
390
+ password: 'Password1!'
391
+ })).rejects.toThrow(_common.BadRequestException);
392
+ });
365
393
  it('sends a verification email and returns requiresEmailVerification when email verification is enabled', async ()=>{
366
394
  emailVerificationEnabled = true;
367
395
  userRepo.findOne.mockResolvedValue(null);
@@ -868,6 +896,36 @@ describe('AuthenticationService', ()=>{
868
896
  })).rejects.toThrow(_common.BadRequestException);
869
897
  expect(queryRunner.rollbackTransaction).toHaveBeenCalled();
870
898
  });
899
+ it('throws BadRequestException when the email belongs to a permanently deleted account', async ()=>{
900
+ const queryRunner = {
901
+ connect: jest.fn(),
902
+ startTransaction: jest.fn(),
903
+ commitTransaction: jest.fn(),
904
+ rollbackTransaction: jest.fn(),
905
+ release: jest.fn(),
906
+ manager: {
907
+ findOne: jest.fn().mockResolvedValue(buildUser({
908
+ deletedAt: new Date()
909
+ })),
910
+ create: jest.fn(),
911
+ save: jest.fn()
912
+ }
913
+ };
914
+ mockDataSourceProvider.getDataSource.mockResolvedValue({
915
+ createQueryRunner: ()=>queryRunner
916
+ });
917
+ const service = buildService();
918
+ await expect(service.createUserFromPendingRegistration({
919
+ email: 'user@example.com',
920
+ name: 'Test',
921
+ passwordHash: 'hash'
922
+ })).rejects.toThrow(_common.BadRequestException);
923
+ expect(queryRunner.manager.findOne).toHaveBeenCalledWith(_appuserentity.AppUser, expect.objectContaining({
924
+ withDeleted: true
925
+ }));
926
+ expect(queryRunner.manager.create).not.toHaveBeenCalled();
927
+ expect(queryRunner.rollbackTransaction).toHaveBeenCalled();
928
+ });
871
929
  it('creates the user with the email marked as verified', async ()=>{
872
930
  const queryRunner = {
873
931
  connect: jest.fn(),
@@ -5,6 +5,8 @@ Object.defineProperty(exports, "__esModule", {
5
5
  _export_star(require("./authentication.service"), exports);
6
6
  _export_star(require("./auth-email.service"), exports);
7
7
  _export_star(require("./company-selection-session.service"), exports);
8
+ _export_star(require("./social-auth.service"), exports);
9
+ _export_star(require("./social-auth-config.service"), exports);
8
10
  _export_star(require("./user.service"), exports);
9
11
  _export_star(require("./company.service"), exports);
10
12
  _export_star(require("./branch.service"), exports);
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "SocialAuthConfigService", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return SocialAuthConfigService;
9
+ }
10
+ });
11
+ const _classes = require("@flusys/nestjs-shared/classes");
12
+ const _modules = require("@flusys/nestjs-shared/modules");
13
+ const _common = require("@nestjs/common");
14
+ const _typeorm = require("typeorm");
15
+ const _config = require("../config");
16
+ const _socialauthconfigentity = require("../entities/social-auth-config.entity");
17
+ const _authdatasourceprovider = require("./auth-datasource.provider");
18
+ function _define_property(obj, key, value) {
19
+ if (key in obj) {
20
+ Object.defineProperty(obj, key, {
21
+ value: value,
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true
25
+ });
26
+ } else {
27
+ obj[key] = value;
28
+ }
29
+ return obj;
30
+ }
31
+ function _ts_decorate(decorators, target, key, desc) {
32
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
33
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
34
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
35
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
36
+ }
37
+ function _ts_metadata(k, v) {
38
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
39
+ }
40
+ function _ts_param(paramIndex, decorator) {
41
+ return function(target, key) {
42
+ decorator(target, key, paramIndex);
43
+ };
44
+ }
45
+ let SocialAuthConfigService = class SocialAuthConfigService extends _classes.ApiService {
46
+ async beforeInsertOperation(dto, _user, _queryRunner) {
47
+ for (const entry of Array.isArray(dto) ? dto : [
48
+ dto
49
+ ]){
50
+ await this.ensureProviderUnclaimed(entry.provider);
51
+ }
52
+ }
53
+ async beforeUpdateOperation(dto, _user, _queryRunner) {
54
+ for (const entry of Array.isArray(dto) ? dto : [
55
+ dto
56
+ ]){
57
+ if (entry.provider) await this.ensureProviderUnclaimed(entry.provider, entry.id);
58
+ }
59
+ }
60
+ async ensureProviderUnclaimed(provider, exceptId) {
61
+ await this.ensureDataSourceRepository();
62
+ const existing = await this.repository.findOne({
63
+ where: {
64
+ provider,
65
+ deletedAt: (0, _typeorm.IsNull)(),
66
+ ...exceptId ? {
67
+ id: (0, _typeorm.Not)(exceptId)
68
+ } : {}
69
+ }
70
+ });
71
+ if (existing) {
72
+ throw new _common.ConflictException({
73
+ message: `Social provider "${provider}" is already configured`,
74
+ messageKey: _config.SOCIAL_AUTH_CONFIG_MESSAGES.PROVIDER_ALREADY_CONFIGURED,
75
+ messageVariables: {
76
+ provider
77
+ }
78
+ });
79
+ }
80
+ }
81
+ async getExtraManipulateQuery(query, filterDto, user) {
82
+ const result = await super.getExtraManipulateQuery(query, filterDto, user);
83
+ query.orderBy(`${this.entityName}.provider`, 'ASC');
84
+ return result;
85
+ }
86
+ constructor(cacheManager, utilsService, dataSourceProvider){
87
+ super(_config.AUTH_EVENT_ENTITIES.SOCIAL_AUTH_CONFIG, cacheManager, utilsService, SocialAuthConfigService.name, true, _config.AUTH_EVENT_MODULE, dataSourceProvider, _socialauthconfigentity.SocialAuthConfig), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService;
88
+ }
89
+ };
90
+ SocialAuthConfigService = _ts_decorate([
91
+ (0, _common.Injectable)({
92
+ scope: _common.Scope.REQUEST
93
+ }),
94
+ _ts_param(0, (0, _common.Inject)('CACHE_INSTANCE')),
95
+ _ts_param(1, (0, _common.Inject)(_modules.UtilsService)),
96
+ _ts_param(2, (0, _common.Inject)(_authdatasourceprovider.AuthDataSourceProvider)),
97
+ _ts_metadata("design:type", Function),
98
+ _ts_metadata("design:paramtypes", [
99
+ typeof _classes.HybridCache === "undefined" ? Object : _classes.HybridCache,
100
+ typeof _modules.UtilsService === "undefined" ? Object : _modules.UtilsService,
101
+ typeof _authdatasourceprovider.AuthDataSourceProvider === "undefined" ? Object : _authdatasourceprovider.AuthDataSourceProvider
102
+ ])
103
+ ], SocialAuthConfigService);
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ const _common = require("@nestjs/common");
6
+ const _repositorymock = require("@test-utils/mocks/repository.mock");
7
+ const _typeorm = require("typeorm");
8
+ const _socialprovidertypeenum = require("../enums/social-provider-type.enum");
9
+ const _socialauthconfigservice = require("./social-auth-config.service");
10
+ describe('SocialAuthConfigService', ()=>{
11
+ let repo;
12
+ let service;
13
+ beforeEach(()=>{
14
+ repo = (0, _repositorymock.createMockRepository)();
15
+ const dataSourceProvider = {
16
+ getRepository: jest.fn().mockResolvedValue(repo)
17
+ };
18
+ service = new _socialauthconfigservice.SocialAuthConfigService({
19
+ get: jest.fn(),
20
+ set: jest.fn(),
21
+ del: jest.fn()
22
+ }, {}, dataSourceProvider);
23
+ });
24
+ const beforeInsert = (dto)=>service.beforeInsertOperation(dto, null, {});
25
+ const beforeUpdate = (dto)=>service.beforeUpdateOperation(dto, null, {});
26
+ it('allows the first configuration of a provider', async ()=>{
27
+ repo.findOne.mockResolvedValue(null);
28
+ await expect(beforeInsert({
29
+ provider: _socialprovidertypeenum.SocialProviderTypeEnum.GOOGLE
30
+ })).resolves.toBeUndefined();
31
+ });
32
+ it('rejects a second row for a provider that is already configured', async ()=>{
33
+ repo.findOne.mockResolvedValue({
34
+ id: 'existing'
35
+ });
36
+ await expect(beforeInsert({
37
+ provider: _socialprovidertypeenum.SocialProviderTypeEnum.GOOGLE
38
+ })).rejects.toThrow(_common.ConflictException);
39
+ });
40
+ it('checks every provider in a batch insert', async ()=>{
41
+ repo.findOne.mockResolvedValue(null);
42
+ await beforeInsert([
43
+ {
44
+ provider: _socialprovidertypeenum.SocialProviderTypeEnum.GOOGLE
45
+ },
46
+ {
47
+ provider: _socialprovidertypeenum.SocialProviderTypeEnum.FACEBOOK
48
+ }
49
+ ]);
50
+ expect(repo.findOne).toHaveBeenCalledTimes(2);
51
+ });
52
+ it('lets a row keep its own provider on update', async ()=>{
53
+ repo.findOne.mockResolvedValue(null);
54
+ await beforeUpdate({
55
+ id: 'config-1',
56
+ provider: _socialprovidertypeenum.SocialProviderTypeEnum.GOOGLE
57
+ });
58
+ expect(repo.findOne).toHaveBeenCalledWith(expect.objectContaining({
59
+ where: expect.objectContaining({
60
+ id: (0, _typeorm.Not)('config-1')
61
+ })
62
+ }));
63
+ });
64
+ it('skips the check for an update that does not touch the provider', async ()=>{
65
+ await beforeUpdate({
66
+ id: 'config-1',
67
+ isEnabled: false
68
+ });
69
+ expect(repo.findOne).not.toHaveBeenCalled();
70
+ });
71
+ });
@@ -0,0 +1,310 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "SocialAuthService", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return SocialAuthService;
9
+ }
10
+ });
11
+ const _nestjsshared = require("@flusys/nestjs-shared");
12
+ const _classes = require("@flusys/nestjs-shared/classes");
13
+ const _constants = require("@flusys/nestjs-shared/constants");
14
+ const _common = require("@nestjs/common");
15
+ const _crypto = require("crypto");
16
+ const _typeorm = require("typeorm");
17
+ const _config = require("../config");
18
+ const _appuserentity = require("../entities/app-user.entity");
19
+ const _socialauthconfigentity = require("../entities/social-auth-config.entity");
20
+ const _socialprovidertypeenum = require("../enums/social-provider-type.enum");
21
+ const _socialproviderregistry = require("../providers/social-provider.registry");
22
+ const _authdatasourceprovider = require("./auth-datasource.provider");
23
+ const _authenticationservice = require("./authentication.service");
24
+ function _define_property(obj, key, value) {
25
+ if (key in obj) {
26
+ Object.defineProperty(obj, key, {
27
+ value: value,
28
+ enumerable: true,
29
+ configurable: true,
30
+ writable: true
31
+ });
32
+ } else {
33
+ obj[key] = value;
34
+ }
35
+ return obj;
36
+ }
37
+ function _ts_decorate(decorators, target, key, desc) {
38
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
39
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
40
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
41
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
42
+ }
43
+ function _ts_metadata(k, v) {
44
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
45
+ }
46
+ function _ts_param(paramIndex, decorator) {
47
+ return function(target, key) {
48
+ decorator(target, key, paramIndex);
49
+ };
50
+ }
51
+ let SocialAuthService = class SocialAuthService {
52
+ async listEnabledProviders(redirectUri) {
53
+ const repository = await this.dataSourceProvider.getRepository(_socialauthconfigentity.SocialAuthConfig);
54
+ const configs = await repository.find({
55
+ where: {
56
+ isEnabled: true,
57
+ deletedAt: (0, _typeorm.IsNull)()
58
+ }
59
+ });
60
+ return configs.filter((config)=>!!config.clientId && !!config.clientSecret).map((config)=>({
61
+ provider: config.provider,
62
+ redirectUri
63
+ }));
64
+ }
65
+ async createAuthorization(provider, redirectUri) {
66
+ const config = await this.getUsableConfig(provider);
67
+ const state = (0, _crypto.randomBytes)(24).toString('hex');
68
+ await this.getCache().set(this.stateKey(state), {
69
+ provider,
70
+ redirectUri
71
+ }, _config.SOCIAL_STATE_TTL_MS);
72
+ return {
73
+ provider,
74
+ authorizationUrl: (0, _socialproviderregistry.getSocialProvider)(provider).buildAuthorizationUrl(this.toCredentials(config, redirectUri), state),
75
+ state
76
+ };
77
+ }
78
+ async completeLogin(code, state, redirectUri) {
79
+ const provider = await this.consumeState(state, redirectUri);
80
+ const config = await this.getUsableConfig(provider);
81
+ const profile = await (0, _socialproviderregistry.getSocialProvider)(provider).fetchProfile(this.toCredentials(config, redirectUri), code);
82
+ if (!profile.providerUserId) {
83
+ throw new _common.UnauthorizedException({
84
+ message: `${provider} did not return an account identifier`,
85
+ messageKey: _config.SOCIAL_AUTH_MESSAGES.PROFILE_INCOMPLETE,
86
+ messageVariables: {
87
+ provider
88
+ }
89
+ });
90
+ }
91
+ const user = await this.resolveUser(provider, profile);
92
+ const response = await this.authService.issueLoginForUser(user);
93
+ this.publishSocialEvent(_config.AUTH_EVENT_ENTITIES.SESSION, _config.AUTH_EVENT_ACTIONS.SOCIAL_LOGGED_IN, user, provider);
94
+ return response;
95
+ }
96
+ async resolveUser(provider, profile) {
97
+ this.ensureVerifiedEmail(provider, profile);
98
+ const userRepository = await this.dataSourceProvider.getRepository(_appuserentity.AppUser);
99
+ const existing = await userRepository.findOne({
100
+ where: {
101
+ email: profile.email
102
+ },
103
+ withDeleted: true
104
+ });
105
+ if (existing?.deletedAt) {
106
+ throw new _common.UnauthorizedException({
107
+ message: 'This account has been permanently deleted. Please contact your system administrator.',
108
+ messageKey: _config.SOCIAL_AUTH_MESSAGES.ACCOUNT_DELETED
109
+ });
110
+ }
111
+ if (existing) {
112
+ // The provider already proved ownership of this email (ensureVerifiedEmail above),
113
+ // so a social login carries at least as much proof as clicking a verification link.
114
+ if (!existing.emailVerified) {
115
+ existing.emailVerified = true;
116
+ await userRepository.save(existing);
117
+ }
118
+ return existing;
119
+ }
120
+ const user = await this.registerUser(provider, userRepository, profile);
121
+ this.publishSocialEvent(_config.AUTH_EVENT_ENTITIES.USER, _config.AUTH_EVENT_ACTIONS.REGISTERED, user, provider);
122
+ return user;
123
+ }
124
+ ensureVerifiedEmail(provider, profile) {
125
+ if (!profile.email) {
126
+ throw new _common.UnauthorizedException({
127
+ message: `${provider} did not share an email address`,
128
+ messageKey: _config.SOCIAL_AUTH_MESSAGES.EMAIL_REQUIRED,
129
+ messageVariables: {
130
+ provider
131
+ }
132
+ });
133
+ }
134
+ if (!profile.emailVerified) {
135
+ throw new _common.UnauthorizedException({
136
+ message: `${provider} has not verified this email address`,
137
+ messageKey: _config.SOCIAL_AUTH_MESSAGES.EMAIL_NOT_VERIFIED,
138
+ messageVariables: {
139
+ provider
140
+ }
141
+ });
142
+ }
143
+ }
144
+ async registerUser(provider, userRepository, profile) {
145
+ if (!this.dataSourceProvider.isSignUpEnabled()) {
146
+ throw new _common.UnauthorizedException({
147
+ message: `No account is linked to this ${provider} profile`,
148
+ messageKey: _config.SOCIAL_AUTH_MESSAGES.ACCOUNT_NOT_LINKED,
149
+ messageVariables: {
150
+ provider
151
+ }
152
+ });
153
+ }
154
+ if (this.dataSourceProvider.isCompanyFeatureEnabled()) {
155
+ throw new _common.UnauthorizedException({
156
+ message: `No account is linked to this ${provider} profile. Please sign up manually and join or create a company first, then social sign-in will work.`,
157
+ messageKey: _config.SOCIAL_AUTH_MESSAGES.COMPANY_REQUIRED,
158
+ messageVariables: {
159
+ provider
160
+ }
161
+ });
162
+ }
163
+ return userRepository.save(userRepository.create({
164
+ email: profile.email,
165
+ name: profile.name,
166
+ password: null,
167
+ isActive: true,
168
+ emailVerified: true
169
+ }));
170
+ }
171
+ async getUsableConfig(provider) {
172
+ const repository = await this.dataSourceProvider.getRepository(_socialauthconfigentity.SocialAuthConfig);
173
+ const config = await repository.findOne({
174
+ where: {
175
+ provider,
176
+ deletedAt: (0, _typeorm.IsNull)()
177
+ }
178
+ });
179
+ if (!config?.isEnabled) {
180
+ throw new _common.BadRequestException({
181
+ message: `Social sign-in with ${provider} is not enabled`,
182
+ messageKey: _config.SOCIAL_AUTH_MESSAGES.PROVIDER_DISABLED,
183
+ messageVariables: {
184
+ provider
185
+ }
186
+ });
187
+ }
188
+ if (!config.clientId || !config.clientSecret) {
189
+ throw new _common.BadRequestException({
190
+ message: `Social sign-in with ${provider} is missing its client credentials`,
191
+ messageKey: _config.SOCIAL_AUTH_MESSAGES.PROVIDER_NOT_CONFIGURED,
192
+ messageVariables: {
193
+ provider
194
+ }
195
+ });
196
+ }
197
+ return config;
198
+ }
199
+ toCredentials(config, redirectUri) {
200
+ return {
201
+ clientId: config.clientId,
202
+ clientSecret: config.clientSecret,
203
+ redirectUri,
204
+ scopes: config.scopes,
205
+ extraConfig: config.extraConfig
206
+ };
207
+ }
208
+ async consumeState(state, redirectUri) {
209
+ const key = this.stateKey(state);
210
+ const stored = await this.getCache().get(key);
211
+ await this.getCache().del(key);
212
+ if (!stored) {
213
+ throw new _common.UnauthorizedException({
214
+ message: 'Social sign-in session is invalid or has expired',
215
+ messageKey: _config.SOCIAL_AUTH_MESSAGES.STATE_INVALID
216
+ });
217
+ }
218
+ if (stored.redirectUri !== redirectUri) {
219
+ throw new _common.UnauthorizedException({
220
+ message: 'Redirect URI does not match the one the sign-in started with',
221
+ messageKey: _config.SOCIAL_AUTH_MESSAGES.REDIRECT_URI_MISMATCH
222
+ });
223
+ }
224
+ return stored.provider;
225
+ }
226
+ stateKey(state) {
227
+ return `${_config.SOCIAL_STATE_PREFIX}:${state}`;
228
+ }
229
+ getCache() {
230
+ if (!this.cacheManager) {
231
+ throw new _common.InternalServerErrorException({
232
+ message: 'Cache is required to hold the social sign-in state',
233
+ messageKey: _constants.SYSTEM_MESSAGES.SERVICE_NOT_AVAILABLE,
234
+ messageVariables: {
235
+ provider: 'CACHE_INSTANCE',
236
+ available: ''
237
+ }
238
+ });
239
+ }
240
+ return this.cacheManager;
241
+ }
242
+ publishSocialEvent(entity, action, user, provider) {
243
+ void (0, _classes.publishDomainEvent)({
244
+ module: _config.AUTH_EVENT_MODULE,
245
+ entity,
246
+ action,
247
+ ids: [
248
+ user.id
249
+ ],
250
+ metadata: {
251
+ email: user.email,
252
+ provider
253
+ },
254
+ user: {
255
+ id: user.id,
256
+ email: user.email
257
+ }
258
+ });
259
+ }
260
+ constructor(authService, dataSourceProvider, cacheManager){
261
+ _define_property(this, "authService", void 0);
262
+ _define_property(this, "dataSourceProvider", void 0);
263
+ _define_property(this, "cacheManager", void 0);
264
+ this.authService = authService;
265
+ this.dataSourceProvider = dataSourceProvider;
266
+ this.cacheManager = cacheManager;
267
+ }
268
+ };
269
+ _ts_decorate([
270
+ (0, _nestjsshared.LogAction)({
271
+ action: 'auth.social.authorize',
272
+ module: 'auth',
273
+ includeParams: false
274
+ }),
275
+ _ts_metadata("design:type", Function),
276
+ _ts_metadata("design:paramtypes", [
277
+ typeof _socialprovidertypeenum.SocialProviderTypeEnum === "undefined" ? Object : _socialprovidertypeenum.SocialProviderTypeEnum,
278
+ String
279
+ ]),
280
+ _ts_metadata("design:returntype", Promise)
281
+ ], SocialAuthService.prototype, "createAuthorization", null);
282
+ _ts_decorate([
283
+ (0, _nestjsshared.LogAction)({
284
+ action: 'auth.social.login',
285
+ module: 'auth',
286
+ includeParams: false
287
+ }),
288
+ _ts_metadata("design:type", Function),
289
+ _ts_metadata("design:paramtypes", [
290
+ String,
291
+ String,
292
+ String
293
+ ]),
294
+ _ts_metadata("design:returntype", Promise)
295
+ ], SocialAuthService.prototype, "completeLogin", null);
296
+ SocialAuthService = _ts_decorate([
297
+ (0, _common.Injectable)({
298
+ scope: _common.Scope.REQUEST
299
+ }),
300
+ _ts_param(0, (0, _common.Inject)(_authenticationservice.AuthenticationService)),
301
+ _ts_param(1, (0, _common.Inject)(_authdatasourceprovider.AuthDataSourceProvider)),
302
+ _ts_param(2, (0, _common.Optional)()),
303
+ _ts_param(2, (0, _common.Inject)('CACHE_INSTANCE')),
304
+ _ts_metadata("design:type", Function),
305
+ _ts_metadata("design:paramtypes", [
306
+ typeof _authenticationservice.AuthenticationService === "undefined" ? Object : _authenticationservice.AuthenticationService,
307
+ typeof _authdatasourceprovider.AuthDataSourceProvider === "undefined" ? Object : _authdatasourceprovider.AuthDataSourceProvider,
308
+ typeof _classes.HybridCache === "undefined" ? Object : _classes.HybridCache
309
+ ])
310
+ ], SocialAuthService);