@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.
- package/README.md +67 -0
- package/cjs/config/auth.constants.js +8 -0
- package/cjs/config/event-actions.js +4 -2
- package/cjs/config/message-keys.js +27 -0
- package/cjs/controllers/index.js +2 -0
- package/cjs/controllers/social-auth-config.controller.js +107 -0
- package/cjs/controllers/social-auth.controller.js +175 -0
- package/cjs/controllers/social-auth.controller.spec.js +79 -0
- package/cjs/docs/auth-swagger.config.js +9 -1
- package/cjs/dtos/index.js +1 -0
- package/cjs/dtos/social-auth.dto.js +318 -0
- package/cjs/entities/index.js +3 -1
- package/cjs/entities/index.spec.js +12 -1
- package/cjs/entities/social-auth-config.entity.js +102 -0
- package/cjs/enums/index.js +1 -0
- package/cjs/enums/social-provider-type.enum.js +17 -0
- package/cjs/index.js +1 -0
- package/cjs/interfaces/index.js +1 -0
- package/cjs/interfaces/social-auth.interface.js +4 -0
- package/cjs/modules/auth.module.js +6 -0
- package/cjs/modules/auth.module.spec.js +15 -0
- package/cjs/providers/facebook.social-provider.js +73 -0
- package/cjs/providers/google.social-provider.js +68 -0
- package/cjs/providers/index.js +18 -0
- package/cjs/providers/linkedin.social-provider.js +66 -0
- package/cjs/providers/microsoft.social-provider.js +78 -0
- package/cjs/providers/oauth-http.util.js +107 -0
- package/cjs/providers/social-provider.registry.js +24 -0
- package/cjs/providers/social-provider.registry.spec.js +135 -0
- package/cjs/services/auth-config.service.js +3 -0
- package/cjs/services/auth-config.service.spec.js +3 -0
- package/cjs/services/auth-datasource.provider.js +4 -0
- package/cjs/services/auth-datasource.provider.spec.js +30 -0
- package/cjs/services/authentication.service.js +38 -8
- package/cjs/services/authentication.service.spec.js +58 -0
- package/cjs/services/index.js +2 -0
- package/cjs/services/social-auth-config.service.js +103 -0
- package/cjs/services/social-auth-config.service.spec.js +71 -0
- package/cjs/services/social-auth.service.js +310 -0
- package/cjs/services/social-auth.service.spec.js +310 -0
- package/config/auth.constants.d.ts +2 -0
- package/config/event-actions.d.ts +2 -0
- package/config/message-keys.d.ts +21 -0
- package/controllers/index.d.ts +2 -0
- package/controllers/social-auth-config.controller.d.ts +22 -0
- package/controllers/social-auth.controller.d.ts +11 -0
- package/dtos/index.d.ts +1 -0
- package/dtos/social-auth.dto.d.ts +42 -0
- package/entities/index.d.ts +3 -1
- package/entities/social-auth-config.entity.d.ts +9 -0
- package/enums/index.d.ts +1 -0
- package/enums/social-provider-type.enum.d.ts +6 -0
- package/fesm/config/auth.constants.js +3 -0
- package/fesm/config/event-actions.js +4 -2
- package/fesm/config/message-keys.js +21 -0
- package/fesm/controllers/index.js +2 -0
- package/fesm/controllers/social-auth-config.controller.js +97 -0
- package/fesm/controllers/social-auth.controller.js +165 -0
- package/fesm/controllers/social-auth.controller.spec.js +75 -0
- package/fesm/docs/auth-swagger.config.js +9 -1
- package/fesm/dtos/index.js +1 -0
- package/fesm/dtos/social-auth.dto.js +282 -0
- package/fesm/entities/index.js +4 -1
- package/fesm/entities/index.spec.js +12 -1
- package/fesm/entities/social-auth-config.entity.js +92 -0
- package/fesm/enums/index.js +1 -0
- package/fesm/enums/social-provider-type.enum.js +7 -0
- package/fesm/index.js +1 -0
- package/fesm/interfaces/index.js +1 -0
- package/fesm/interfaces/social-auth.interface.js +1 -0
- package/fesm/modules/auth.module.js +8 -2
- package/fesm/modules/auth.module.spec.js +17 -2
- package/fesm/providers/facebook.social-provider.js +63 -0
- package/fesm/providers/google.social-provider.js +58 -0
- package/fesm/providers/index.js +1 -0
- package/fesm/providers/linkedin.social-provider.js +56 -0
- package/fesm/providers/microsoft.social-provider.js +68 -0
- package/fesm/providers/oauth-http.util.js +77 -0
- package/fesm/providers/social-provider.registry.js +14 -0
- package/fesm/providers/social-provider.registry.spec.js +131 -0
- package/fesm/services/auth-config.service.js +3 -0
- package/fesm/services/auth-config.service.spec.js +3 -0
- package/fesm/services/auth-datasource.provider.js +4 -0
- package/fesm/services/auth-datasource.provider.spec.js +30 -0
- package/fesm/services/authentication.service.js +38 -8
- package/fesm/services/authentication.service.spec.js +58 -0
- package/fesm/services/index.js +2 -0
- package/fesm/services/social-auth-config.service.js +93 -0
- package/fesm/services/social-auth-config.service.spec.js +67 -0
- package/fesm/services/social-auth.service.js +300 -0
- package/fesm/services/social-auth.service.spec.js +265 -0
- package/index.d.ts +1 -0
- package/interfaces/index.d.ts +1 -0
- package/interfaces/social-auth.interface.d.ts +43 -0
- package/package.json +3 -3
- package/providers/facebook.social-provider.d.ts +10 -0
- package/providers/google.social-provider.d.ts +10 -0
- package/providers/index.d.ts +1 -0
- package/providers/linkedin.social-provider.d.ts +10 -0
- package/providers/microsoft.social-provider.d.ts +10 -0
- package/providers/oauth-http.util.d.ts +10 -0
- package/providers/social-provider.registry.d.ts +3 -0
- package/services/auth-config.service.d.ts +1 -0
- package/services/auth-datasource.provider.d.ts +1 -0
- package/services/authentication.service.d.ts +4 -0
- package/services/index.d.ts +2 -0
- package/services/social-auth-config.service.d.ts +21 -0
- package/services/social-auth.service.d.ts +24 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
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;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
function _ts_param(paramIndex, decorator) {
|
|
24
|
+
return function(target, key) {
|
|
25
|
+
decorator(target, key, paramIndex);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
import { createApiController } from '@flusys/nestjs-shared/classes';
|
|
29
|
+
import { SOCIAL_AUTH_CONFIG_PERMISSIONS } from '@flusys/nestjs-shared/constants';
|
|
30
|
+
import { Controller, Inject } from '@nestjs/common';
|
|
31
|
+
import { ApiTags } from '@nestjs/swagger';
|
|
32
|
+
import { CreateSocialAuthConfigDto, SocialAuthConfigResponseDto, UpdateSocialAuthConfigDto } from '../dtos/social-auth.dto';
|
|
33
|
+
import { SocialAuthConfigService } from '../services/social-auth-config.service';
|
|
34
|
+
export class SocialAuthConfigController extends createApiController(CreateSocialAuthConfigDto, UpdateSocialAuthConfigDto, SocialAuthConfigResponseDto, {
|
|
35
|
+
entityName: 'social.config',
|
|
36
|
+
security: {
|
|
37
|
+
insert: {
|
|
38
|
+
level: 'permission',
|
|
39
|
+
permissions: [
|
|
40
|
+
SOCIAL_AUTH_CONFIG_PERMISSIONS.CREATE
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
bulkUpsert: {
|
|
44
|
+
level: 'permission',
|
|
45
|
+
permissions: [
|
|
46
|
+
SOCIAL_AUTH_CONFIG_PERMISSIONS.CREATE,
|
|
47
|
+
SOCIAL_AUTH_CONFIG_PERMISSIONS.UPDATE
|
|
48
|
+
],
|
|
49
|
+
operator: 'AND'
|
|
50
|
+
},
|
|
51
|
+
getById: {
|
|
52
|
+
level: 'permission',
|
|
53
|
+
permissions: [
|
|
54
|
+
SOCIAL_AUTH_CONFIG_PERMISSIONS.READ
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
getAll: {
|
|
58
|
+
level: 'permission',
|
|
59
|
+
permissions: [
|
|
60
|
+
SOCIAL_AUTH_CONFIG_PERMISSIONS.READ
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
update: {
|
|
64
|
+
level: 'permission',
|
|
65
|
+
permissions: [
|
|
66
|
+
SOCIAL_AUTH_CONFIG_PERMISSIONS.UPDATE
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
delete: {
|
|
70
|
+
level: 'permission',
|
|
71
|
+
permissions: [
|
|
72
|
+
SOCIAL_AUTH_CONFIG_PERMISSIONS.DELETE
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
enabledEndpoints: [
|
|
77
|
+
'insert',
|
|
78
|
+
'bulkUpsert',
|
|
79
|
+
'getById',
|
|
80
|
+
'getAll',
|
|
81
|
+
'update',
|
|
82
|
+
'delete'
|
|
83
|
+
]
|
|
84
|
+
}) {
|
|
85
|
+
constructor(socialAuthConfigService){
|
|
86
|
+
super(socialAuthConfigService), _define_property(this, "socialAuthConfigService", void 0), this.socialAuthConfigService = socialAuthConfigService;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
SocialAuthConfigController = _ts_decorate([
|
|
90
|
+
ApiTags('Social Auth Config'),
|
|
91
|
+
Controller('auth/social-config'),
|
|
92
|
+
_ts_param(0, Inject(SocialAuthConfigService)),
|
|
93
|
+
_ts_metadata("design:type", Function),
|
|
94
|
+
_ts_metadata("design:paramtypes", [
|
|
95
|
+
typeof SocialAuthConfigService === "undefined" ? Object : SocialAuthConfigService
|
|
96
|
+
])
|
|
97
|
+
], SocialAuthConfigController);
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
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;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
function _ts_param(paramIndex, decorator) {
|
|
24
|
+
return function(target, key) {
|
|
25
|
+
decorator(target, key, paramIndex);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
import { Body, Controller, HttpCode, HttpStatus, Inject, Post, UseInterceptors } from '@nestjs/common';
|
|
29
|
+
import { ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
|
30
|
+
import { Throttle } from '@nestjs/throttler';
|
|
31
|
+
import { AUTH_MESSAGES, SOCIAL_AUTH_MESSAGES } from '../config';
|
|
32
|
+
import { SocialAuthorizeDto, SocialAuthorizeResponseDto, SocialCallbackDto, SocialProviderInfoDto, SocialProvidersDto } from '../dtos/social-auth.dto';
|
|
33
|
+
import { SetToken } from '../interceptors/set-token.interceptor';
|
|
34
|
+
import { SocialAuthService } from '../services/social-auth.service';
|
|
35
|
+
export class SocialAuthController {
|
|
36
|
+
async getProviders(dto) {
|
|
37
|
+
const data = await this.socialAuthService.listEnabledProviders(dto.redirectUri);
|
|
38
|
+
return {
|
|
39
|
+
success: true,
|
|
40
|
+
message: 'Social providers retrieved successfully',
|
|
41
|
+
messageKey: SOCIAL_AUTH_MESSAGES.PROVIDERS_SUCCESS,
|
|
42
|
+
data
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
async authorize(dto) {
|
|
46
|
+
const data = await this.socialAuthService.createAuthorization(dto.provider, dto.redirectUri);
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
message: 'Authorization URL generated successfully',
|
|
50
|
+
messageKey: SOCIAL_AUTH_MESSAGES.AUTHORIZE_SUCCESS,
|
|
51
|
+
data
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async callback(dto) {
|
|
55
|
+
const result = await this.socialAuthService.completeLogin(dto.code, dto.state, dto.redirectUri);
|
|
56
|
+
if ('requiresSelection' in result) {
|
|
57
|
+
return {
|
|
58
|
+
success: true,
|
|
59
|
+
message: 'Company selection required',
|
|
60
|
+
messageKey: AUTH_MESSAGES.LOGIN_REQUIRES_SELECTION,
|
|
61
|
+
data: result
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
success: true,
|
|
66
|
+
message: 'Login successful',
|
|
67
|
+
messageKey: SOCIAL_AUTH_MESSAGES.LOGIN_SUCCESS,
|
|
68
|
+
data: result
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
constructor(socialAuthService){
|
|
72
|
+
_define_property(this, "socialAuthService", void 0);
|
|
73
|
+
this.socialAuthService = socialAuthService;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
_ts_decorate([
|
|
77
|
+
Post('providers'),
|
|
78
|
+
HttpCode(HttpStatus.OK),
|
|
79
|
+
Throttle({
|
|
80
|
+
default: {
|
|
81
|
+
limit: 30,
|
|
82
|
+
ttl: 60000
|
|
83
|
+
}
|
|
84
|
+
}),
|
|
85
|
+
ApiOperation({
|
|
86
|
+
summary: 'List enabled social sign-in providers'
|
|
87
|
+
}),
|
|
88
|
+
ApiBody({
|
|
89
|
+
type: SocialProvidersDto
|
|
90
|
+
}),
|
|
91
|
+
ApiResponse({
|
|
92
|
+
status: 200,
|
|
93
|
+
type: [
|
|
94
|
+
SocialProviderInfoDto
|
|
95
|
+
]
|
|
96
|
+
}),
|
|
97
|
+
_ts_param(0, Body()),
|
|
98
|
+
_ts_metadata("design:type", Function),
|
|
99
|
+
_ts_metadata("design:paramtypes", [
|
|
100
|
+
typeof SocialProvidersDto === "undefined" ? Object : SocialProvidersDto
|
|
101
|
+
]),
|
|
102
|
+
_ts_metadata("design:returntype", Promise)
|
|
103
|
+
], SocialAuthController.prototype, "getProviders", null);
|
|
104
|
+
_ts_decorate([
|
|
105
|
+
Post('authorize'),
|
|
106
|
+
HttpCode(HttpStatus.OK),
|
|
107
|
+
Throttle({
|
|
108
|
+
default: {
|
|
109
|
+
limit: 10,
|
|
110
|
+
ttl: 60000
|
|
111
|
+
}
|
|
112
|
+
}),
|
|
113
|
+
ApiOperation({
|
|
114
|
+
summary: "Start a sign-in and get the provider's consent URL"
|
|
115
|
+
}),
|
|
116
|
+
ApiBody({
|
|
117
|
+
type: SocialAuthorizeDto
|
|
118
|
+
}),
|
|
119
|
+
ApiResponse({
|
|
120
|
+
status: 200,
|
|
121
|
+
type: SocialAuthorizeResponseDto
|
|
122
|
+
}),
|
|
123
|
+
_ts_param(0, Body()),
|
|
124
|
+
_ts_metadata("design:type", Function),
|
|
125
|
+
_ts_metadata("design:paramtypes", [
|
|
126
|
+
typeof SocialAuthorizeDto === "undefined" ? Object : SocialAuthorizeDto
|
|
127
|
+
]),
|
|
128
|
+
_ts_metadata("design:returntype", Promise)
|
|
129
|
+
], SocialAuthController.prototype, "authorize", null);
|
|
130
|
+
_ts_decorate([
|
|
131
|
+
Post('callback'),
|
|
132
|
+
HttpCode(HttpStatus.OK),
|
|
133
|
+
Throttle({
|
|
134
|
+
default: {
|
|
135
|
+
limit: 10,
|
|
136
|
+
ttl: 60000
|
|
137
|
+
}
|
|
138
|
+
}),
|
|
139
|
+
UseInterceptors(SetToken),
|
|
140
|
+
ApiOperation({
|
|
141
|
+
summary: 'Complete a social sign-in'
|
|
142
|
+
}),
|
|
143
|
+
ApiBody({
|
|
144
|
+
type: SocialCallbackDto
|
|
145
|
+
}),
|
|
146
|
+
ApiResponse({
|
|
147
|
+
status: 200,
|
|
148
|
+
description: 'Sign-in successful, or company selection is still required'
|
|
149
|
+
}),
|
|
150
|
+
_ts_param(0, Body()),
|
|
151
|
+
_ts_metadata("design:type", Function),
|
|
152
|
+
_ts_metadata("design:paramtypes", [
|
|
153
|
+
typeof SocialCallbackDto === "undefined" ? Object : SocialCallbackDto
|
|
154
|
+
]),
|
|
155
|
+
_ts_metadata("design:returntype", Promise)
|
|
156
|
+
], SocialAuthController.prototype, "callback", null);
|
|
157
|
+
SocialAuthController = _ts_decorate([
|
|
158
|
+
ApiTags('Social Authentication'),
|
|
159
|
+
Controller('auth/social'),
|
|
160
|
+
_ts_param(0, Inject(SocialAuthService)),
|
|
161
|
+
_ts_metadata("design:type", Function),
|
|
162
|
+
_ts_metadata("design:paramtypes", [
|
|
163
|
+
typeof SocialAuthService === "undefined" ? Object : SocialAuthService
|
|
164
|
+
])
|
|
165
|
+
], SocialAuthController);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { AUTH_MESSAGES, SOCIAL_AUTH_MESSAGES } from '../config';
|
|
2
|
+
import { SocialProviderTypeEnum } from '../enums/social-provider-type.enum';
|
|
3
|
+
import { SocialAuthController } from './social-auth.controller';
|
|
4
|
+
const REDIRECT_URI = 'http://localhost:2001/auth/social/callback';
|
|
5
|
+
describe('SocialAuthController', ()=>{
|
|
6
|
+
let controller;
|
|
7
|
+
let mockSocialAuthService;
|
|
8
|
+
beforeEach(()=>{
|
|
9
|
+
mockSocialAuthService = {
|
|
10
|
+
listEnabledProviders: jest.fn(),
|
|
11
|
+
createAuthorization: jest.fn(),
|
|
12
|
+
completeLogin: jest.fn()
|
|
13
|
+
};
|
|
14
|
+
controller = new SocialAuthController(mockSocialAuthService);
|
|
15
|
+
});
|
|
16
|
+
it('wraps the enabled provider list', async ()=>{
|
|
17
|
+
const providers = [
|
|
18
|
+
{
|
|
19
|
+
provider: SocialProviderTypeEnum.GOOGLE,
|
|
20
|
+
redirectUri: REDIRECT_URI
|
|
21
|
+
}
|
|
22
|
+
];
|
|
23
|
+
mockSocialAuthService.listEnabledProviders.mockResolvedValue(providers);
|
|
24
|
+
const result = await controller.getProviders({
|
|
25
|
+
redirectUri: REDIRECT_URI
|
|
26
|
+
});
|
|
27
|
+
expect(result.data).toBe(providers);
|
|
28
|
+
expect(result.messageKey).toBe(SOCIAL_AUTH_MESSAGES.PROVIDERS_SUCCESS);
|
|
29
|
+
expect(mockSocialAuthService.listEnabledProviders).toHaveBeenCalledWith(REDIRECT_URI);
|
|
30
|
+
});
|
|
31
|
+
it('wraps the authorization URL', async ()=>{
|
|
32
|
+
mockSocialAuthService.createAuthorization.mockResolvedValue({
|
|
33
|
+
provider: SocialProviderTypeEnum.GOOGLE,
|
|
34
|
+
authorizationUrl: 'https://provider.test/consent',
|
|
35
|
+
state: 'state-token'
|
|
36
|
+
});
|
|
37
|
+
const result = await controller.authorize({
|
|
38
|
+
provider: SocialProviderTypeEnum.GOOGLE,
|
|
39
|
+
redirectUri: REDIRECT_URI
|
|
40
|
+
});
|
|
41
|
+
expect(result.data?.authorizationUrl).toBe('https://provider.test/consent');
|
|
42
|
+
expect(result.messageKey).toBe(SOCIAL_AUTH_MESSAGES.AUTHORIZE_SUCCESS);
|
|
43
|
+
});
|
|
44
|
+
it('wraps a completed sign-in', async ()=>{
|
|
45
|
+
mockSocialAuthService.completeLogin.mockResolvedValue({
|
|
46
|
+
accessToken: 'a',
|
|
47
|
+
refreshToken: 'r'
|
|
48
|
+
});
|
|
49
|
+
const result = await controller.callback({
|
|
50
|
+
code: 'code',
|
|
51
|
+
state: 'state-token',
|
|
52
|
+
redirectUri: REDIRECT_URI
|
|
53
|
+
});
|
|
54
|
+
expect(result.messageKey).toBe(SOCIAL_AUTH_MESSAGES.LOGIN_SUCCESS);
|
|
55
|
+
expect(result.data).toEqual(expect.objectContaining({
|
|
56
|
+
accessToken: 'a'
|
|
57
|
+
}));
|
|
58
|
+
expect(mockSocialAuthService.completeLogin).toHaveBeenCalledWith('code', 'state-token', REDIRECT_URI);
|
|
59
|
+
});
|
|
60
|
+
it('wraps a sign-in that still needs a company, like the password login does', async ()=>{
|
|
61
|
+
mockSocialAuthService.completeLogin.mockResolvedValue({
|
|
62
|
+
requiresSelection: true,
|
|
63
|
+
sessionId: 'sess-1'
|
|
64
|
+
});
|
|
65
|
+
const result = await controller.callback({
|
|
66
|
+
code: 'code',
|
|
67
|
+
state: 'state-token',
|
|
68
|
+
redirectUri: REDIRECT_URI
|
|
69
|
+
});
|
|
70
|
+
expect(result.messageKey).toBe(AUTH_MESSAGES.LOGIN_REQUIRES_SELECTION);
|
|
71
|
+
expect(result.data).toEqual(expect.objectContaining({
|
|
72
|
+
requiresSelection: true
|
|
73
|
+
}));
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -319,6 +319,7 @@ function buildDescription(enableCompanyFeature, enableEmailVerification, isMulti
|
|
|
319
319
|
const features = [
|
|
320
320
|
'- User registration',
|
|
321
321
|
'- Login with email/password',
|
|
322
|
+
'- Social sign-in (Google, Facebook, LinkedIn, Microsoft)',
|
|
322
323
|
'- JWT token-based authentication',
|
|
323
324
|
...enableEmailVerification ? [
|
|
324
325
|
'- Email verification',
|
|
@@ -372,6 +373,13 @@ POST /auth/verify-email → { accessToken, refreshToken, user }
|
|
|
372
373
|
- \`POST /auth/resend-verification\` - Resend verification email
|
|
373
374
|
- \`POST /auth/verify-email\` - Verify email using token
|
|
374
375
|
` : '';
|
|
376
|
+
const socialSection = `
|
|
377
|
+
### Social Sign-In Endpoints
|
|
378
|
+
- \`POST /auth/social/providers\` - List the providers an administrator enabled
|
|
379
|
+
- \`POST /auth/social/authorize\` - Start a sign-in and get the provider's consent URL
|
|
380
|
+
- \`POST /auth/social/callback\` - Exchange the provider's code for a session
|
|
381
|
+
- \`POST /auth/social-config/*\` - Manage provider credentials
|
|
382
|
+
`;
|
|
375
383
|
const administrationSection = enableCompanyFeature ? `
|
|
376
384
|
### Administration Endpoints
|
|
377
385
|
- \`POST /administration/users/*\` - User management
|
|
@@ -412,7 +420,7 @@ Handles user authentication${enableCompanyFeature ? ', company/branch management
|
|
|
412
420
|
|
|
413
421
|
### Features
|
|
414
422
|
${features}
|
|
415
|
-
${loginFlowSection}${emailVerificationSection}${administrationSection}${registrationSection}`;
|
|
423
|
+
${loginFlowSection}${emailVerificationSection}${socialSection}${administrationSection}${registrationSection}`;
|
|
416
424
|
}
|
|
417
425
|
// ============================================================================
|
|
418
426
|
// Config factory
|
package/fesm/dtos/index.js
CHANGED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
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;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
import { IdentityResponseDto } from '@flusys/nestjs-shared/dtos';
|
|
24
|
+
import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
|
|
25
|
+
import { Expose, Transform } from 'class-transformer';
|
|
26
|
+
import { ArrayMaxSize, IsArray, IsBoolean, IsEnum, IsNotEmpty, IsObject, IsOptional, IsString, IsUrl, MaxLength } from 'class-validator';
|
|
27
|
+
import { SocialProviderTypeEnum } from '../enums/social-provider-type.enum';
|
|
28
|
+
export class CreateSocialAuthConfigDto {
|
|
29
|
+
constructor(){
|
|
30
|
+
_define_property(this, "provider", void 0);
|
|
31
|
+
_define_property(this, "isEnabled", void 0);
|
|
32
|
+
_define_property(this, "clientId", void 0);
|
|
33
|
+
_define_property(this, "clientSecret", void 0);
|
|
34
|
+
_define_property(this, "scopes", void 0);
|
|
35
|
+
_define_property(this, "extraConfig", void 0);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
_ts_decorate([
|
|
39
|
+
Expose(),
|
|
40
|
+
ApiProperty({
|
|
41
|
+
enum: SocialProviderTypeEnum,
|
|
42
|
+
example: SocialProviderTypeEnum.GOOGLE
|
|
43
|
+
}),
|
|
44
|
+
IsEnum(SocialProviderTypeEnum),
|
|
45
|
+
IsNotEmpty(),
|
|
46
|
+
_ts_metadata("design:type", typeof SocialProviderTypeEnum === "undefined" ? Object : SocialProviderTypeEnum)
|
|
47
|
+
], CreateSocialAuthConfigDto.prototype, "provider", void 0);
|
|
48
|
+
_ts_decorate([
|
|
49
|
+
Expose(),
|
|
50
|
+
ApiPropertyOptional({
|
|
51
|
+
example: true,
|
|
52
|
+
description: 'Offer this provider on the login page'
|
|
53
|
+
}),
|
|
54
|
+
IsBoolean(),
|
|
55
|
+
IsOptional(),
|
|
56
|
+
_ts_metadata("design:type", Boolean)
|
|
57
|
+
], CreateSocialAuthConfigDto.prototype, "isEnabled", void 0);
|
|
58
|
+
_ts_decorate([
|
|
59
|
+
Expose(),
|
|
60
|
+
ApiPropertyOptional({
|
|
61
|
+
example: '1234567890-abc.apps.googleusercontent.com'
|
|
62
|
+
}),
|
|
63
|
+
IsString(),
|
|
64
|
+
IsOptional(),
|
|
65
|
+
MaxLength(512),
|
|
66
|
+
_ts_metadata("design:type", String)
|
|
67
|
+
], CreateSocialAuthConfigDto.prototype, "clientId", void 0);
|
|
68
|
+
_ts_decorate([
|
|
69
|
+
Expose(),
|
|
70
|
+
ApiPropertyOptional({
|
|
71
|
+
example: 'GOCSPX-secret-value'
|
|
72
|
+
}),
|
|
73
|
+
IsString(),
|
|
74
|
+
IsOptional(),
|
|
75
|
+
_ts_metadata("design:type", String)
|
|
76
|
+
], CreateSocialAuthConfigDto.prototype, "clientSecret", void 0);
|
|
77
|
+
_ts_decorate([
|
|
78
|
+
Expose(),
|
|
79
|
+
ApiPropertyOptional({
|
|
80
|
+
type: [
|
|
81
|
+
String
|
|
82
|
+
],
|
|
83
|
+
example: [
|
|
84
|
+
'openid',
|
|
85
|
+
'email',
|
|
86
|
+
'profile'
|
|
87
|
+
],
|
|
88
|
+
description: "Overrides the provider's default scopes when set"
|
|
89
|
+
}),
|
|
90
|
+
IsArray(),
|
|
91
|
+
IsString({
|
|
92
|
+
each: true
|
|
93
|
+
}),
|
|
94
|
+
ArrayMaxSize(30),
|
|
95
|
+
IsOptional(),
|
|
96
|
+
_ts_metadata("design:type", Array)
|
|
97
|
+
], CreateSocialAuthConfigDto.prototype, "scopes", void 0);
|
|
98
|
+
_ts_decorate([
|
|
99
|
+
Expose(),
|
|
100
|
+
ApiPropertyOptional({
|
|
101
|
+
type: 'object',
|
|
102
|
+
additionalProperties: true,
|
|
103
|
+
description: "Provider specific extras, such as Microsoft's tenantId",
|
|
104
|
+
example: {
|
|
105
|
+
tenantId: 'common'
|
|
106
|
+
}
|
|
107
|
+
}),
|
|
108
|
+
IsObject(),
|
|
109
|
+
IsOptional(),
|
|
110
|
+
_ts_metadata("design:type", typeof Record === "undefined" ? Object : Record)
|
|
111
|
+
], CreateSocialAuthConfigDto.prototype, "extraConfig", void 0);
|
|
112
|
+
export class UpdateSocialAuthConfigDto extends PartialType(CreateSocialAuthConfigDto) {
|
|
113
|
+
constructor(...args){
|
|
114
|
+
super(...args), _define_property(this, "id", void 0);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
_ts_decorate([
|
|
118
|
+
Expose(),
|
|
119
|
+
ApiProperty({
|
|
120
|
+
example: '123e4567-e89b-12d3-a456-426614174000'
|
|
121
|
+
}),
|
|
122
|
+
IsString(),
|
|
123
|
+
IsNotEmpty(),
|
|
124
|
+
_ts_metadata("design:type", String)
|
|
125
|
+
], UpdateSocialAuthConfigDto.prototype, "id", void 0);
|
|
126
|
+
export class SocialAuthConfigResponseDto extends IdentityResponseDto {
|
|
127
|
+
constructor(...args){
|
|
128
|
+
super(...args), _define_property(this, "provider", void 0), _define_property(this, "isEnabled", void 0), _define_property(this, "clientId", void 0), _define_property(this, "hasClientSecret", void 0), _define_property(this, "scopes", void 0), _define_property(this, "extraConfig", void 0);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
_ts_decorate([
|
|
132
|
+
ApiProperty({
|
|
133
|
+
enum: SocialProviderTypeEnum,
|
|
134
|
+
example: SocialProviderTypeEnum.GOOGLE
|
|
135
|
+
}),
|
|
136
|
+
Expose(),
|
|
137
|
+
_ts_metadata("design:type", typeof SocialProviderTypeEnum === "undefined" ? Object : SocialProviderTypeEnum)
|
|
138
|
+
], SocialAuthConfigResponseDto.prototype, "provider", void 0);
|
|
139
|
+
_ts_decorate([
|
|
140
|
+
ApiProperty({
|
|
141
|
+
example: true
|
|
142
|
+
}),
|
|
143
|
+
Expose(),
|
|
144
|
+
_ts_metadata("design:type", Boolean)
|
|
145
|
+
], SocialAuthConfigResponseDto.prototype, "isEnabled", void 0);
|
|
146
|
+
_ts_decorate([
|
|
147
|
+
ApiPropertyOptional({
|
|
148
|
+
nullable: true
|
|
149
|
+
}),
|
|
150
|
+
Expose(),
|
|
151
|
+
_ts_metadata("design:type", Object)
|
|
152
|
+
], SocialAuthConfigResponseDto.prototype, "clientId", void 0);
|
|
153
|
+
_ts_decorate([
|
|
154
|
+
ApiProperty({
|
|
155
|
+
description: 'Whether a client secret is currently stored for this provider'
|
|
156
|
+
}),
|
|
157
|
+
Expose(),
|
|
158
|
+
Transform(({ obj })=>!!obj.clientSecret),
|
|
159
|
+
_ts_metadata("design:type", Boolean)
|
|
160
|
+
], SocialAuthConfigResponseDto.prototype, "hasClientSecret", void 0);
|
|
161
|
+
_ts_decorate([
|
|
162
|
+
ApiPropertyOptional({
|
|
163
|
+
type: [
|
|
164
|
+
String
|
|
165
|
+
],
|
|
166
|
+
nullable: true
|
|
167
|
+
}),
|
|
168
|
+
Expose(),
|
|
169
|
+
_ts_metadata("design:type", Object)
|
|
170
|
+
], SocialAuthConfigResponseDto.prototype, "scopes", void 0);
|
|
171
|
+
_ts_decorate([
|
|
172
|
+
ApiPropertyOptional({
|
|
173
|
+
type: 'object',
|
|
174
|
+
additionalProperties: true,
|
|
175
|
+
nullable: true
|
|
176
|
+
}),
|
|
177
|
+
Expose(),
|
|
178
|
+
_ts_metadata("design:type", Object)
|
|
179
|
+
], SocialAuthConfigResponseDto.prototype, "extraConfig", void 0);
|
|
180
|
+
export class SocialProviderInfoDto {
|
|
181
|
+
constructor(){
|
|
182
|
+
_define_property(this, "provider", void 0);
|
|
183
|
+
_define_property(this, "redirectUri", void 0);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
_ts_decorate([
|
|
187
|
+
ApiProperty({
|
|
188
|
+
enum: SocialProviderTypeEnum,
|
|
189
|
+
example: SocialProviderTypeEnum.GOOGLE
|
|
190
|
+
}),
|
|
191
|
+
Expose(),
|
|
192
|
+
_ts_metadata("design:type", typeof SocialProviderTypeEnum === "undefined" ? Object : SocialProviderTypeEnum)
|
|
193
|
+
], SocialProviderInfoDto.prototype, "provider", void 0);
|
|
194
|
+
_ts_decorate([
|
|
195
|
+
ApiProperty({
|
|
196
|
+
example: 'http://localhost:2001/auth/social/callback'
|
|
197
|
+
}),
|
|
198
|
+
Expose(),
|
|
199
|
+
_ts_metadata("design:type", String)
|
|
200
|
+
], SocialProviderInfoDto.prototype, "redirectUri", void 0);
|
|
201
|
+
export class SocialProvidersDto {
|
|
202
|
+
constructor(){
|
|
203
|
+
_define_property(this, "redirectUri", void 0);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
_ts_decorate([
|
|
207
|
+
ApiProperty({
|
|
208
|
+
example: 'http://localhost:2001/auth/social/callback',
|
|
209
|
+
description: 'Must match the URI registered with the provider and the one sent on callback'
|
|
210
|
+
}),
|
|
211
|
+
IsUrl({
|
|
212
|
+
require_tld: false
|
|
213
|
+
}),
|
|
214
|
+
IsNotEmpty(),
|
|
215
|
+
MaxLength(2048),
|
|
216
|
+
_ts_metadata("design:type", String)
|
|
217
|
+
], SocialProvidersDto.prototype, "redirectUri", void 0);
|
|
218
|
+
export class SocialAuthorizeDto extends SocialProvidersDto {
|
|
219
|
+
constructor(...args){
|
|
220
|
+
super(...args), _define_property(this, "provider", void 0);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
_ts_decorate([
|
|
224
|
+
ApiProperty({
|
|
225
|
+
enum: SocialProviderTypeEnum,
|
|
226
|
+
example: SocialProviderTypeEnum.GOOGLE
|
|
227
|
+
}),
|
|
228
|
+
IsEnum(SocialProviderTypeEnum),
|
|
229
|
+
IsNotEmpty(),
|
|
230
|
+
_ts_metadata("design:type", typeof SocialProviderTypeEnum === "undefined" ? Object : SocialProviderTypeEnum)
|
|
231
|
+
], SocialAuthorizeDto.prototype, "provider", void 0);
|
|
232
|
+
export class SocialAuthorizeResponseDto {
|
|
233
|
+
constructor(){
|
|
234
|
+
_define_property(this, "provider", void 0);
|
|
235
|
+
_define_property(this, "authorizationUrl", void 0);
|
|
236
|
+
_define_property(this, "state", void 0);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
_ts_decorate([
|
|
240
|
+
ApiProperty({
|
|
241
|
+
enum: SocialProviderTypeEnum
|
|
242
|
+
}),
|
|
243
|
+
Expose(),
|
|
244
|
+
_ts_metadata("design:type", typeof SocialProviderTypeEnum === "undefined" ? Object : SocialProviderTypeEnum)
|
|
245
|
+
], SocialAuthorizeResponseDto.prototype, "provider", void 0);
|
|
246
|
+
_ts_decorate([
|
|
247
|
+
ApiProperty({
|
|
248
|
+
description: "The provider's consent screen to redirect the browser to"
|
|
249
|
+
}),
|
|
250
|
+
Expose(),
|
|
251
|
+
_ts_metadata("design:type", String)
|
|
252
|
+
], SocialAuthorizeResponseDto.prototype, "authorizationUrl", void 0);
|
|
253
|
+
_ts_decorate([
|
|
254
|
+
ApiProperty({
|
|
255
|
+
description: 'Opaque CSRF token, echoed back by the provider'
|
|
256
|
+
}),
|
|
257
|
+
Expose(),
|
|
258
|
+
_ts_metadata("design:type", String)
|
|
259
|
+
], SocialAuthorizeResponseDto.prototype, "state", void 0);
|
|
260
|
+
export class SocialCallbackDto extends SocialProvidersDto {
|
|
261
|
+
constructor(...args){
|
|
262
|
+
super(...args), _define_property(this, "code", void 0), _define_property(this, "state", void 0);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
_ts_decorate([
|
|
266
|
+
ApiProperty({
|
|
267
|
+
description: 'Authorization code returned by the provider'
|
|
268
|
+
}),
|
|
269
|
+
IsString(),
|
|
270
|
+
IsNotEmpty(),
|
|
271
|
+
MaxLength(4096),
|
|
272
|
+
_ts_metadata("design:type", String)
|
|
273
|
+
], SocialCallbackDto.prototype, "code", void 0);
|
|
274
|
+
_ts_decorate([
|
|
275
|
+
ApiProperty({
|
|
276
|
+
description: 'The state issued by /auth/social/authorize'
|
|
277
|
+
}),
|
|
278
|
+
IsString(),
|
|
279
|
+
IsNotEmpty(),
|
|
280
|
+
MaxLength(128),
|
|
281
|
+
_ts_metadata("design:type", String)
|
|
282
|
+
], SocialCallbackDto.prototype, "state", void 0);
|
package/fesm/entities/index.js
CHANGED
|
@@ -2,17 +2,20 @@ export * from './app-user.entity';
|
|
|
2
2
|
export * from './company-branch.entity';
|
|
3
3
|
export * from './company.entity';
|
|
4
4
|
export * from './pending-registration.entity';
|
|
5
|
+
export * from './social-auth-config.entity';
|
|
5
6
|
export { PermissionType, UserCompanyPermission } from './user-company-permission.entity';
|
|
6
7
|
export * from './user-token.entity';
|
|
7
8
|
import { AppUser } from './app-user.entity';
|
|
8
9
|
import { CompanyBranch } from './company-branch.entity';
|
|
9
10
|
import { Company } from './company.entity';
|
|
10
11
|
import { PendingRegistration } from './pending-registration.entity';
|
|
12
|
+
import { SocialAuthConfig } from './social-auth-config.entity';
|
|
11
13
|
import { UserCompanyPermission } from './user-company-permission.entity';
|
|
12
14
|
import { UserToken } from './user-token.entity';
|
|
13
15
|
const BaseEntities = [
|
|
14
16
|
AppUser,
|
|
15
|
-
UserToken
|
|
17
|
+
UserToken,
|
|
18
|
+
SocialAuthConfig
|
|
16
19
|
];
|
|
17
20
|
const EmailEntities = [
|
|
18
21
|
PendingRegistration
|