@diagramers/cli 1.0.23 → 1.0.24
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/dist/services/template-updater.d.ts +2 -0
- package/dist/services/template-updater.d.ts.map +1 -1
- package/dist/services/template-updater.js +66 -14
- package/dist/services/template-updater.js.map +1 -1
- package/package.json +5 -1
- package/templates/api/certs/auth-app-cert.json +13 -0
- package/templates/api/main.ts +10 -0
- package/templates/api/package.json +70 -0
- package/templates/api/src/assets/css/email-template.css +8 -0
- package/templates/api/src/assets/images/logo_large.png +0 -0
- package/templates/api/src/assets/keys/certificate.pem +22 -0
- package/templates/api/src/assets/keys/private-key.pem +28 -0
- package/templates/api/src/config/config-interface.ts +191 -0
- package/templates/api/src/config/development.ts +145 -0
- package/templates/api/src/config/index.ts +59 -0
- package/templates/api/src/config/production.ts +145 -0
- package/templates/api/src/config/staging.ts +144 -0
- package/templates/api/src/config/uat.ts +144 -0
- package/templates/api/src/controllers/account-controller.ts +162 -0
- package/templates/api/src/entities/audit.ts +12 -0
- package/templates/api/src/entities/base-entity.ts +10 -0
- package/templates/api/src/entities/user.ts +71 -0
- package/templates/api/src/helpers/FrameworkHelper.ts +157 -0
- package/templates/api/src/helpers/auth.ts +971 -0
- package/templates/api/src/helpers/cronHelper.ts +170 -0
- package/templates/api/src/helpers/dbcontext.ts +83 -0
- package/templates/api/src/helpers/encryptionHelper.ts +76 -0
- package/templates/api/src/helpers/enums.ts +258 -0
- package/templates/api/src/helpers/handle-response.ts +49 -0
- package/templates/api/src/helpers/httpHelper.ts +75 -0
- package/templates/api/src/helpers/mailer.ts +152 -0
- package/templates/api/src/helpers/result.ts +47 -0
- package/templates/api/src/helpers/string-helper.ts +27 -0
- package/templates/api/src/routes/account-routes.ts +37 -0
- package/templates/api/src/routes/auth-routes.ts +286 -0
- package/templates/api/src/routes/index.ts +92 -0
- package/templates/api/src/schemas/audit.ts +36 -0
- package/templates/api/src/schemas/otp.ts +52 -0
- package/templates/api/src/schemas/session.ts +57 -0
- package/templates/api/src/schemas/user.ts +125 -0
- package/templates/api/src/server/index.ts +86 -0
- package/templates/api/src/server/socket-server-provider.ts +209 -0
- package/templates/api/src/services/account-service.ts +243 -0
- package/templates/api/src/services/audit-service.ts +56 -0
- package/templates/api/tsconfig.json +16 -0
- package/templates/api/webpack.config.js +66 -0
- package/scripts/publish.sh +0 -58
- package/scripts/setup.sh +0 -38
- package/scripts/version.sh +0 -80
- package/src/commands/api.ts +0 -76
- package/src/commands/extend.ts +0 -35
- package/src/commands/init.ts +0 -32
- package/src/commands/update.ts +0 -25
- package/src/config/template-config.ts +0 -111
- package/src/index.ts +0 -41
- package/src/services/api-generator.ts +0 -378
- package/src/services/project-extender.ts +0 -330
- package/src/services/project-initializer.ts +0 -335
- package/src/services/project-updater.ts +0 -117
- package/src/services/relation-generator.ts +0 -203
- package/src/services/table-generator.ts +0 -114
- package/src/services/template-processor.ts +0 -166
- package/src/services/template-updater.ts +0 -184
- package/tsconfig.json +0 -19
@@ -0,0 +1,971 @@
|
|
1
|
+
import firebase from 'firebase/compat/app';
|
2
|
+
import * as firebaseAdmin from 'firebase-admin'
|
3
|
+
import 'firebase/compat/auth';
|
4
|
+
import { initializeApp, App } from 'firebase-admin/app';
|
5
|
+
import { Auth as IAuth, getAuth } from 'firebase-admin/auth';
|
6
|
+
import { getStorage, getDownloadURL, Storage } from 'firebase-admin/storage';
|
7
|
+
import { Config } from '../config';
|
8
|
+
import { AuditMessageType, ResponseCode, Status, AuthProvider, AuthMethod } from './enums';
|
9
|
+
import { Result } from './result';
|
10
|
+
const path = require('path');
|
11
|
+
import { Http2ServerRequest } from 'http2';
|
12
|
+
import { IUser, AuthProviderInfo, UserSession, OTPCode } from '../entities/user';
|
13
|
+
import mime = require('mime');
|
14
|
+
import { UUID, ObjectId } from 'bson';
|
15
|
+
import * as jwt from 'jsonwebtoken';
|
16
|
+
import * as crypto from 'crypto';
|
17
|
+
import { v4 as uuidv4 } from 'uuid';
|
18
|
+
import dbcontext from './dbcontext';
|
19
|
+
import { MailHelper } from './mailer';
|
20
|
+
import { EncryptionHelper } from './encryptionHelper';
|
21
|
+
import { frameworkHelper } from './FrameworkHelper';
|
22
|
+
|
23
|
+
export class AuthHelper {
|
24
|
+
|
25
|
+
app: App;
|
26
|
+
auth: IAuth;
|
27
|
+
authProvider: any
|
28
|
+
appProvider: any;
|
29
|
+
result: Result;
|
30
|
+
className = 'helpers/auth';
|
31
|
+
storageProvider: Storage;
|
32
|
+
defaultBucketName = 'gs://sendifier-web-app';
|
33
|
+
private encryptionHelper: EncryptionHelper;
|
34
|
+
private frameworkHelper: any;
|
35
|
+
|
36
|
+
constructor(result: Result) {
|
37
|
+
try {
|
38
|
+
this.result = result;
|
39
|
+
this.encryptionHelper = new EncryptionHelper();
|
40
|
+
this.frameworkHelper = frameworkHelper;
|
41
|
+
|
42
|
+
// Initialize Firebase if enabled
|
43
|
+
if (Config.props.auth.firebase.enabled) {
|
44
|
+
var certPath = path.resolve(Config.props.auth.firebase.private_key_path);
|
45
|
+
result.addMessage(AuditMessageType.trace, this.className, this.className + '- ctor', `certification path ${certPath}`)
|
46
|
+
var cred = firebaseAdmin.credential.cert(certPath);
|
47
|
+
if (firebaseAdmin.apps.length > 0) {
|
48
|
+
this.app = firebaseAdmin.apps[0];
|
49
|
+
}
|
50
|
+
else {
|
51
|
+
firebaseAdmin.initializeApp({
|
52
|
+
credential: cred
|
53
|
+
});
|
54
|
+
console.log('firebase config is', Config.props.auth.firebase)
|
55
|
+
this.app = firebaseAdmin.app();
|
56
|
+
}
|
57
|
+
this.auth = getAuth(this.app);
|
58
|
+
this.storageProvider = firebaseAdmin.storage(this.app);
|
59
|
+
}
|
60
|
+
|
61
|
+
}
|
62
|
+
catch (ex) {
|
63
|
+
console.log(ex);
|
64
|
+
// this.result?.addException(this.className, this.className + '- ctor', ex);
|
65
|
+
// this.result.Data = null;
|
66
|
+
// this.result.Status = ResponseCode.Unauthorized;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
async GetUser(uid: string):
|
71
|
+
Promise<Result> {
|
72
|
+
try {
|
73
|
+
var result = await this.auth.getUser(uid);
|
74
|
+
return new Result(result, ResponseCode.Ok, null, ['firebase user exists']);
|
75
|
+
}
|
76
|
+
catch (ex) {
|
77
|
+
return new Result(null, ResponseCode.Error, [ex], [ex.errorInfo.message]);
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
async CreateUser(user: any):
|
82
|
+
Promise<Result> {
|
83
|
+
try {
|
84
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'CreateUser', 'Started')
|
85
|
+
var response = await this.auth.createUser(user);
|
86
|
+
this.result.Data = response;
|
87
|
+
this.result.Status = ResponseCode.Ok;
|
88
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'CreateUser', 'Finished');
|
89
|
+
return this.result;
|
90
|
+
}
|
91
|
+
catch (ex) {
|
92
|
+
this.result.addException(this.className, 'CreateUser', ex);
|
93
|
+
return this.result;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
GetUserProfile(req): any {
|
98
|
+
try {
|
99
|
+
console.log("authHelper*GetUserProfile** GetUserProfile**** ", req.headers["user-profile"]);
|
100
|
+
var userProfileHeaderStringified = req.headers["user-profile"].toString();
|
101
|
+
console.log("*auth**GetUserProfile*** Start checking auth for user**** ", userProfileHeaderStringified)
|
102
|
+
var userProfileHeader: any = null;
|
103
|
+
if (userProfileHeaderStringified)
|
104
|
+
userProfileHeader = JSON.parse(userProfileHeaderStringified);
|
105
|
+
else
|
106
|
+
userProfileHeader = null;
|
107
|
+
return userProfileHeader;
|
108
|
+
}
|
109
|
+
catch (ex) {
|
110
|
+
console.log("authHelper*GetUserProfile** exception**** " + JSON.stringify(ex));
|
111
|
+
return null;
|
112
|
+
}
|
113
|
+
}
|
114
|
+
|
115
|
+
async CheckAuth(req: Http2ServerRequest, res) {
|
116
|
+
console.log("authHelper*CheckAuth** requestHeaders**** ", req.headers);
|
117
|
+
var userProfileHeaderStringified = req.headers["user-profile"].toString();
|
118
|
+
console.log("*auth**CheckAuth*** Start checking auth for user**** ", userProfileHeaderStringified)
|
119
|
+
var userProfileHeader: any = null;
|
120
|
+
if (userProfileHeaderStringified)
|
121
|
+
userProfileHeader = JSON.parse(userProfileHeaderStringified);
|
122
|
+
else
|
123
|
+
userProfileHeader = null;
|
124
|
+
if (userProfileHeader) {
|
125
|
+
var verifyToken = await this.VerifyToken(userProfileHeader.token);
|
126
|
+
if (verifyToken.Status == ResponseCode.Ok) {
|
127
|
+
var result = new Result(verifyToken.Data, ResponseCode.Ok);
|
128
|
+
return result;
|
129
|
+
}
|
130
|
+
else {
|
131
|
+
var result = new Result(null, ResponseCode.Unauthorized);
|
132
|
+
res.json(result);
|
133
|
+
}
|
134
|
+
}
|
135
|
+
else {
|
136
|
+
var result = new Result(null, ResponseCode.Unauthorized);
|
137
|
+
res.json(result);
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
async VerifyToken(token: string) {
|
142
|
+
try {
|
143
|
+
return new Result(true, ResponseCode.Ok);
|
144
|
+
}
|
145
|
+
catch (ex) {
|
146
|
+
console.log("authhelper*verifytoken** exception****", ex);
|
147
|
+
return new Result(ex.message, ResponseCode.Unauthorized);
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
async RemoveUser(userEmail: string) {
|
152
|
+
try {
|
153
|
+
console.log("authhelper*RemoveUser** delete user with email****", userEmail);
|
154
|
+
var result = await this.auth.getUserByEmail(userEmail);
|
155
|
+
await this.auth.deleteUser(result.uid);
|
156
|
+
console.log("authhelper*RemoveUser** delete user done****", result);
|
157
|
+
return new Result(result, ResponseCode.Ok, null, ['User deleted successfully']);
|
158
|
+
}
|
159
|
+
catch (ex) {
|
160
|
+
console.log("authhelper*RemoveUser** exception****", ex);
|
161
|
+
return new Result(ex.message, ResponseCode.Error, [ex], [ex.message]);
|
162
|
+
}
|
163
|
+
}
|
164
|
+
|
165
|
+
async UploadFileByPath(path: string = null, name: string = null, bucket: string = null) {
|
166
|
+
try {
|
167
|
+
if (!bucket)
|
168
|
+
bucket = this.defaultBucketName;
|
169
|
+
var storage = getStorage(this.app).bucket(bucket);
|
170
|
+
return await storage.upload(path);
|
171
|
+
}
|
172
|
+
catch (ex) {
|
173
|
+
this.result.addException(this.className, 'UploadFile', ex);
|
174
|
+
this.result.Data = null;
|
175
|
+
this.result.Status = ResponseCode.Error;
|
176
|
+
return this.result;
|
177
|
+
}
|
178
|
+
}
|
179
|
+
|
180
|
+
|
181
|
+
async UploadFileByContent(fileBase64: string = null, name: string = null, path: string = null, contentType = null): Promise<Result> {
|
182
|
+
try {
|
183
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'UploadFileByContent', 'Started');
|
184
|
+
|
185
|
+
const storageBucket = this.storageProvider.bucket(this.defaultBucketName);
|
186
|
+
const file = storageBucket.file(`${path}/${new Date().getTime()}_${name}`);
|
187
|
+
const uuid = new UUID();
|
188
|
+
const stream = file.createWriteStream({
|
189
|
+
resumable: true,
|
190
|
+
|
191
|
+
metadata: {
|
192
|
+
contentType: contentType,
|
193
|
+
contentDisposition: 'inline', // Allows the file to be viewed in the browser
|
194
|
+
firebaseStorageDownloadTokens: uuid,
|
195
|
+
}
|
196
|
+
});
|
197
|
+
|
198
|
+
await new Promise<any>((resolve, reject) => {
|
199
|
+
// Handle errors during the upload
|
200
|
+
stream.on('error', (err) => {
|
201
|
+
this.result.addException(this.className, 'UploadFileByContent', `Error during upload: ${err}`);
|
202
|
+
this.result.Data = null;
|
203
|
+
this.result.Status = ResponseCode.Error;
|
204
|
+
reject(this.result);
|
205
|
+
});
|
206
|
+
|
207
|
+
// Handle the upload completion
|
208
|
+
stream.on('finish', async () => {
|
209
|
+
try {
|
210
|
+
await file.generateSignedPostPolicyV2({
|
211
|
+
expires: '03-09-2025', // Set an expiration date for the URL
|
212
|
+
})
|
213
|
+
const downloadUrl = await getDownloadURL(file);
|
214
|
+
// const [downloadUrl] = await file.getSignedUrl({
|
215
|
+
// action: 'read',
|
216
|
+
// expires: '03-09-2025'
|
217
|
+
// });
|
218
|
+
|
219
|
+
if (downloadUrl) {
|
220
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'UploadFileByContent', 'Media uploaded');
|
221
|
+
this.result.Data = downloadUrl;
|
222
|
+
this.result.Status = ResponseCode.Ok;
|
223
|
+
resolve(this.result);
|
224
|
+
} else {
|
225
|
+
this.result.addMessage(AuditMessageType.error, this.className, 'UploadFileByContent', 'Download URL is null.');
|
226
|
+
this.result.Data = null;
|
227
|
+
this.result.Status = ResponseCode.Error;
|
228
|
+
reject(this.result);
|
229
|
+
}
|
230
|
+
} catch (error) {
|
231
|
+
this.result.addException(this.className, 'UploadFileByContent', `Error getting signed URL: ${error}`);
|
232
|
+
this.result.Data = null;
|
233
|
+
this.result.Status = ResponseCode.Error;
|
234
|
+
reject(this.result);
|
235
|
+
}
|
236
|
+
});
|
237
|
+
|
238
|
+
// Write the buffer to the stream
|
239
|
+
stream.end(fileBase64);
|
240
|
+
});
|
241
|
+
|
242
|
+
return this.result;
|
243
|
+
} catch (ex) {
|
244
|
+
this.result.addException(this.className, 'UploadFileByContent', `Error during file upload: ${ex}`);
|
245
|
+
this.result.Data = null;
|
246
|
+
this.result.Status = ResponseCode.Error;
|
247
|
+
return this.result;
|
248
|
+
}
|
249
|
+
}
|
250
|
+
|
251
|
+
async UploadFile(fileBase64: string, name, path, contentType): Promise<Result> {
|
252
|
+
try {
|
253
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'UploadFile', `Started`);
|
254
|
+
const storageBucket = this.storageProvider.bucket(this.defaultBucketName);
|
255
|
+
const audioBuffer = Buffer.from(fileBase64, 'base64');
|
256
|
+
|
257
|
+
const audioFile = storageBucket.file(`${this.result.user._defaultCompanyId}/${path}/${name}`);
|
258
|
+
await audioFile.save(audioBuffer, { contentType: contentType, resumable: true });
|
259
|
+
const url = await getDownloadURL(audioFile);
|
260
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'UploadFile', `Finished`);
|
261
|
+
this.result.Data = url;
|
262
|
+
this.result.Status = ResponseCode.Ok;
|
263
|
+
return this.result
|
264
|
+
}
|
265
|
+
catch (ex) {
|
266
|
+
this.result.addException(this.className, 'UploadFile', ex);
|
267
|
+
this.result.Data = null;
|
268
|
+
this.result.Status = ResponseCode.Error;
|
269
|
+
return this.result;
|
270
|
+
}
|
271
|
+
}
|
272
|
+
|
273
|
+
async UploadFileSigned(fileBase64: string, name: string, path: string, contentType: string): Promise<Result> {
|
274
|
+
try {
|
275
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'UploadFile', `Started`);
|
276
|
+
const storageBucket = this.storageProvider.bucket(this.defaultBucketName);
|
277
|
+
const audioBuffer = Buffer.from(fileBase64, 'base64');
|
278
|
+
|
279
|
+
const audioFile = storageBucket.file(`${this.result.user._defaultCompanyId}/${path}/${name}`);
|
280
|
+
await audioFile.save(audioBuffer, { contentType: 'audio/ogg; codecs=opus' });
|
281
|
+
|
282
|
+
type action = "read"
|
283
|
+
// Correcting the action type to match the GetSignedUrlConfig expected type
|
284
|
+
|
285
|
+
var url = await audioFile.getSignedUrl({ action: 'read', expires: '03-12-2094' })
|
286
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'UploadFile', `Finished`);
|
287
|
+
this.result.Data = url[0];
|
288
|
+
this.result.Status = ResponseCode.Ok;
|
289
|
+
return this.result;
|
290
|
+
} catch (ex) {
|
291
|
+
console.error(`Error in UploadFile: ${ex}`);
|
292
|
+
this.result.addException(this.className, 'UploadFile', ex);
|
293
|
+
this.result.Data = null;
|
294
|
+
this.result.Status = ResponseCode.Error;
|
295
|
+
return this.result;
|
296
|
+
}
|
297
|
+
}
|
298
|
+
|
299
|
+
// ===== MULTI-PROVIDER AUTHENTICATION METHODS =====
|
300
|
+
|
301
|
+
// Internal Authentication
|
302
|
+
async registerWithPassword(userData: any): Promise<Result> {
|
303
|
+
try {
|
304
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'RegisterWithPassword', 'Started');
|
305
|
+
|
306
|
+
// Validate password strength
|
307
|
+
const passwordValidation = this.validatePassword(userData.password);
|
308
|
+
if (!passwordValidation.isValid) {
|
309
|
+
this.result.Status = ResponseCode.Error;
|
310
|
+
this.result.Errors.push(...passwordValidation.errors);
|
311
|
+
return this.result;
|
312
|
+
}
|
313
|
+
|
314
|
+
// Check if user already exists
|
315
|
+
const existingUser = await dbcontext.UserEntity.findOne({ email: userData.email.toLowerCase() });
|
316
|
+
if (existingUser) {
|
317
|
+
this.result.Status = ResponseCode.Duplicate;
|
318
|
+
this.result.Errors.push('User with this email already exists');
|
319
|
+
return this.result;
|
320
|
+
}
|
321
|
+
|
322
|
+
// Create user with internal auth provider
|
323
|
+
const authProviderInfo: AuthProviderInfo = {
|
324
|
+
provider: AuthProvider.INTERNAL,
|
325
|
+
is_verified: false,
|
326
|
+
last_used: new Date(),
|
327
|
+
created_at: new Date()
|
328
|
+
};
|
329
|
+
|
330
|
+
const userEntity = new dbcontext.UserEntity({
|
331
|
+
_id: new ObjectId(),
|
332
|
+
name: userData.name,
|
333
|
+
email: userData.email.toLowerCase(),
|
334
|
+
username: userData.email.toLowerCase(),
|
335
|
+
emailVerified: false,
|
336
|
+
phone: userData.phone || null,
|
337
|
+
phoneVerified: false,
|
338
|
+
auth_providers: [authProviderInfo],
|
339
|
+
primary_auth_provider: AuthProvider.INTERNAL,
|
340
|
+
hashedPassword: this.encryptionHelper.encryptUserLogin(userData.password),
|
341
|
+
identity: uuidv4(),
|
342
|
+
invitationCode: this.frameworkHelper.generateRandomString(6),
|
343
|
+
status: 2, // PendingConfirmation
|
344
|
+
userType: "NOR"
|
345
|
+
});
|
346
|
+
|
347
|
+
const createdUser = await dbcontext.UserEntity.create(userEntity);
|
348
|
+
|
349
|
+
if (createdUser) {
|
350
|
+
// Send email verification if required
|
351
|
+
if (Config.props.auth.internal.require_email_verification) {
|
352
|
+
await this.sendEmailVerification(createdUser);
|
353
|
+
}
|
354
|
+
|
355
|
+
this.result.Data = { user_id: createdUser._id, message: 'User registered successfully' };
|
356
|
+
this.result.Status = ResponseCode.Ok;
|
357
|
+
} else {
|
358
|
+
this.result.Status = ResponseCode.Error;
|
359
|
+
this.result.Errors.push('Failed to create user');
|
360
|
+
}
|
361
|
+
|
362
|
+
return this.result;
|
363
|
+
} catch (ex) {
|
364
|
+
this.result.addException(this.className, 'RegisterWithPassword', ex);
|
365
|
+
return this.result;
|
366
|
+
}
|
367
|
+
}
|
368
|
+
|
369
|
+
async loginWithPassword(email: string, password: string, ipAddress: string, userAgent: string): Promise<Result> {
|
370
|
+
try {
|
371
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'LoginWithPassword', 'Started');
|
372
|
+
|
373
|
+
const encryptedPassword = this.encryptionHelper.encryptUserLogin(password);
|
374
|
+
const user = await dbcontext.UserEntity.findOne({
|
375
|
+
email: email.toLowerCase(),
|
376
|
+
hashedPassword: encryptedPassword
|
377
|
+
});
|
378
|
+
|
379
|
+
if (!user) {
|
380
|
+
this.result.Status = ResponseCode.Unauthorized;
|
381
|
+
this.result.Errors.push('Invalid credentials');
|
382
|
+
return this.result;
|
383
|
+
}
|
384
|
+
|
385
|
+
// Check if user is active
|
386
|
+
if (user.status !== 1) {
|
387
|
+
this.result.Status = ResponseCode.Unauthorized;
|
388
|
+
this.result.Errors.push('Account is not active');
|
389
|
+
return this.result;
|
390
|
+
}
|
391
|
+
|
392
|
+
// Create session and generate tokens
|
393
|
+
const sessionResult = await this.createSession(user, AuthProvider.INTERNAL, AuthMethod.PASSWORD, ipAddress, userAgent);
|
394
|
+
if (sessionResult.Status !== ResponseCode.Ok) {
|
395
|
+
return sessionResult;
|
396
|
+
}
|
397
|
+
|
398
|
+
this.result.Data = {
|
399
|
+
user: this.sanitizeUserData(user),
|
400
|
+
session: sessionResult.Data,
|
401
|
+
message: 'Login successful'
|
402
|
+
};
|
403
|
+
this.result.Status = ResponseCode.Ok;
|
404
|
+
|
405
|
+
return this.result;
|
406
|
+
} catch (ex) {
|
407
|
+
this.result.addException(this.className, 'LoginWithPassword', ex);
|
408
|
+
return this.result;
|
409
|
+
}
|
410
|
+
}
|
411
|
+
|
412
|
+
// Firebase Authentication
|
413
|
+
async authenticateWithFirebase(firebaseToken: string, ipAddress: string, userAgent: string): Promise<Result> {
|
414
|
+
try {
|
415
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'AuthenticateWithFirebase', 'Started');
|
416
|
+
|
417
|
+
if (!Config.props.auth.firebase.enabled) {
|
418
|
+
this.result.Status = ResponseCode.Error;
|
419
|
+
this.result.Errors.push('Firebase authentication is not enabled');
|
420
|
+
return this.result;
|
421
|
+
}
|
422
|
+
|
423
|
+
// Verify Firebase token
|
424
|
+
const decodedToken = await this.auth.verifyIdToken(firebaseToken);
|
425
|
+
|
426
|
+
// Find or create user
|
427
|
+
let user = await dbcontext.UserEntity.findOne({
|
428
|
+
'auth_providers.provider': AuthProvider.FIREBASE,
|
429
|
+
'auth_providers.provider_user_id': decodedToken.uid
|
430
|
+
});
|
431
|
+
|
432
|
+
if (!user) {
|
433
|
+
// Create new user with Firebase auth
|
434
|
+
const authProviderInfo: AuthProviderInfo = {
|
435
|
+
provider: AuthProvider.FIREBASE,
|
436
|
+
provider_user_id: decodedToken.uid,
|
437
|
+
is_verified: decodedToken.email_verified || false,
|
438
|
+
last_used: new Date(),
|
439
|
+
created_at: new Date(),
|
440
|
+
provider_metadata: {
|
441
|
+
email: decodedToken.email,
|
442
|
+
name: decodedToken.name,
|
443
|
+
picture: decodedToken.picture
|
444
|
+
}
|
445
|
+
};
|
446
|
+
|
447
|
+
user = new dbcontext.UserEntity({
|
448
|
+
_id: new ObjectId(),
|
449
|
+
name: decodedToken.name || decodedToken.email,
|
450
|
+
email: decodedToken.email,
|
451
|
+
username: decodedToken.email,
|
452
|
+
emailVerified: decodedToken.email_verified || false,
|
453
|
+
auth_providers: [authProviderInfo],
|
454
|
+
primary_auth_provider: AuthProvider.FIREBASE,
|
455
|
+
firebaseUID: decodedToken.uid,
|
456
|
+
identity: uuidv4(),
|
457
|
+
status: 1, // Active
|
458
|
+
userType: "NOR"
|
459
|
+
});
|
460
|
+
|
461
|
+
await dbcontext.UserEntity.create(user);
|
462
|
+
} else {
|
463
|
+
// Update last used
|
464
|
+
await dbcontext.UserEntity.updateOne(
|
465
|
+
{ _id: user._id, 'auth_providers.provider': AuthProvider.FIREBASE },
|
466
|
+
{ $set: { 'auth_providers.$.last_used': new Date() } }
|
467
|
+
);
|
468
|
+
}
|
469
|
+
|
470
|
+
// Create session
|
471
|
+
const sessionResult = await this.createSession(user, AuthProvider.FIREBASE, AuthMethod.OAUTH, ipAddress, userAgent);
|
472
|
+
if (sessionResult.Status !== ResponseCode.Ok) {
|
473
|
+
return sessionResult;
|
474
|
+
}
|
475
|
+
|
476
|
+
this.result.Data = {
|
477
|
+
user: this.sanitizeUserData(user),
|
478
|
+
session: sessionResult.Data,
|
479
|
+
message: 'Firebase authentication successful'
|
480
|
+
};
|
481
|
+
this.result.Status = ResponseCode.Ok;
|
482
|
+
|
483
|
+
return this.result;
|
484
|
+
} catch (ex) {
|
485
|
+
this.result.addException(this.className, 'AuthenticateWithFirebase', ex);
|
486
|
+
return this.result;
|
487
|
+
}
|
488
|
+
}
|
489
|
+
|
490
|
+
// OAuth Authentication
|
491
|
+
async authenticateWithOAuth(provider: string, accessToken: string, ipAddress: string, userAgent: string): Promise<Result> {
|
492
|
+
try {
|
493
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'AuthenticateWithOAuth', `Started with ${provider}`);
|
494
|
+
|
495
|
+
if (!Config.props.auth.oauth.enabled_providers.includes(provider as any)) {
|
496
|
+
this.result.Status = ResponseCode.Error;
|
497
|
+
this.result.Errors.push(`${provider} OAuth is not enabled`);
|
498
|
+
return this.result;
|
499
|
+
}
|
500
|
+
|
501
|
+
// Get user info from OAuth provider
|
502
|
+
const userInfo = await this.getOAuthUserInfo(provider, accessToken);
|
503
|
+
if (!userInfo) {
|
504
|
+
this.result.Status = ResponseCode.Unauthorized;
|
505
|
+
this.result.Errors.push('Failed to get user info from OAuth provider');
|
506
|
+
return this.result;
|
507
|
+
}
|
508
|
+
|
509
|
+
// Find or create user
|
510
|
+
let user = await dbcontext.UserEntity.findOne({
|
511
|
+
'auth_providers.provider': provider,
|
512
|
+
'auth_providers.provider_user_id': userInfo.id
|
513
|
+
});
|
514
|
+
|
515
|
+
if (!user) {
|
516
|
+
// Create new user with OAuth auth
|
517
|
+
const authProviderInfo: AuthProviderInfo = {
|
518
|
+
provider: provider as AuthProvider,
|
519
|
+
provider_user_id: userInfo.id,
|
520
|
+
provider_access_token: accessToken,
|
521
|
+
is_verified: true,
|
522
|
+
last_used: new Date(),
|
523
|
+
created_at: new Date(),
|
524
|
+
provider_metadata: userInfo
|
525
|
+
};
|
526
|
+
|
527
|
+
user = new dbcontext.UserEntity({
|
528
|
+
_id: new ObjectId(),
|
529
|
+
name: userInfo.name,
|
530
|
+
email: userInfo.email,
|
531
|
+
username: userInfo.email,
|
532
|
+
emailVerified: userInfo.email_verified || true,
|
533
|
+
auth_providers: [authProviderInfo],
|
534
|
+
primary_auth_provider: provider as AuthProvider,
|
535
|
+
identity: uuidv4(),
|
536
|
+
status: 1, // Active
|
537
|
+
userType: "NOR"
|
538
|
+
});
|
539
|
+
|
540
|
+
await dbcontext.UserEntity.create(user);
|
541
|
+
} else {
|
542
|
+
// Update access token and last used
|
543
|
+
await dbcontext.UserEntity.updateOne(
|
544
|
+
{ _id: user._id, 'auth_providers.provider': provider },
|
545
|
+
{
|
546
|
+
$set: {
|
547
|
+
'auth_providers.$.provider_access_token': accessToken,
|
548
|
+
'auth_providers.$.last_used': new Date()
|
549
|
+
}
|
550
|
+
}
|
551
|
+
);
|
552
|
+
}
|
553
|
+
|
554
|
+
// Create session
|
555
|
+
const sessionResult = await this.createSession(user, provider as AuthProvider, AuthMethod.OAUTH, ipAddress, userAgent);
|
556
|
+
if (sessionResult.Status !== ResponseCode.Ok) {
|
557
|
+
return sessionResult;
|
558
|
+
}
|
559
|
+
|
560
|
+
this.result.Data = {
|
561
|
+
user: this.sanitizeUserData(user),
|
562
|
+
session: sessionResult.Data,
|
563
|
+
message: `${provider} authentication successful`
|
564
|
+
};
|
565
|
+
this.result.Status = ResponseCode.Ok;
|
566
|
+
|
567
|
+
return this.result;
|
568
|
+
} catch (ex) {
|
569
|
+
this.result.addException(this.className, 'AuthenticateWithOAuth', ex);
|
570
|
+
return this.result;
|
571
|
+
}
|
572
|
+
}
|
573
|
+
|
574
|
+
// SMS OTP Authentication
|
575
|
+
async sendSMSOTP(phone: string, purpose: string): Promise<Result> {
|
576
|
+
try {
|
577
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'SendSMSOTP', 'Started');
|
578
|
+
|
579
|
+
if (!Config.props.auth.sms_otp.enabled) {
|
580
|
+
this.result.Status = ResponseCode.Error;
|
581
|
+
this.result.Errors.push('SMS OTP is not enabled');
|
582
|
+
return this.result;
|
583
|
+
}
|
584
|
+
|
585
|
+
// Generate OTP
|
586
|
+
const otp = this.generateOTP(Config.props.auth.sms_otp.otp_length);
|
587
|
+
const expiresAt = new Date(Date.now() + Config.props.auth.sms_otp.otp_expiry_minutes * 60 * 1000);
|
588
|
+
|
589
|
+
// Find user by phone
|
590
|
+
const user = await dbcontext.UserEntity.findOne({ phone });
|
591
|
+
if (!user) {
|
592
|
+
this.result.Status = ResponseCode.NotExist;
|
593
|
+
this.result.Errors.push('User not found with this phone number');
|
594
|
+
return this.result;
|
595
|
+
}
|
596
|
+
|
597
|
+
// Create OTP record
|
598
|
+
const otpEntity = new dbcontext.OTPEntity({
|
599
|
+
code: otp,
|
600
|
+
user_id: user._id,
|
601
|
+
auth_provider: AuthProvider.SMS_OTP,
|
602
|
+
purpose: purpose,
|
603
|
+
expires_at: expiresAt,
|
604
|
+
max_attempts: Config.props.auth.sms_otp.max_attempts
|
605
|
+
});
|
606
|
+
|
607
|
+
await dbcontext.OTPEntity.create(otpEntity);
|
608
|
+
|
609
|
+
// Send SMS
|
610
|
+
await this.sendSMS(phone, `Your OTP is: ${otp}. Valid for ${Config.props.auth.sms_otp.otp_expiry_minutes} minutes.`);
|
611
|
+
|
612
|
+
this.result.Data = { message: 'OTP sent successfully' };
|
613
|
+
this.result.Status = ResponseCode.Ok;
|
614
|
+
|
615
|
+
return this.result;
|
616
|
+
} catch (ex) {
|
617
|
+
this.result.addException(this.className, 'SendSMSOTP', ex);
|
618
|
+
return this.result;
|
619
|
+
}
|
620
|
+
}
|
621
|
+
|
622
|
+
async verifySMSOTP(phone: string, otp: string, ipAddress: string, userAgent: string): Promise<Result> {
|
623
|
+
try {
|
624
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'VerifySMSOTP', 'Started');
|
625
|
+
|
626
|
+
// Find user by phone
|
627
|
+
const user = await dbcontext.UserEntity.findOne({ phone });
|
628
|
+
if (!user) {
|
629
|
+
this.result.Status = ResponseCode.NotExist;
|
630
|
+
this.result.Errors.push('User not found');
|
631
|
+
return this.result;
|
632
|
+
}
|
633
|
+
|
634
|
+
// Find and verify OTP
|
635
|
+
const otpRecord = await dbcontext.OTPEntity.findOne({
|
636
|
+
user_id: user._id,
|
637
|
+
auth_provider: AuthProvider.SMS_OTP,
|
638
|
+
code: otp,
|
639
|
+
is_used: false,
|
640
|
+
expires_at: { $gt: new Date() }
|
641
|
+
});
|
642
|
+
|
643
|
+
if (!otpRecord) {
|
644
|
+
this.result.Status = ResponseCode.Unauthorized;
|
645
|
+
this.result.Errors.push('Invalid or expired OTP');
|
646
|
+
return this.result;
|
647
|
+
}
|
648
|
+
|
649
|
+
// Check attempts
|
650
|
+
if (otpRecord.attempts >= otpRecord.max_attempts) {
|
651
|
+
this.result.Status = ResponseCode.Unauthorized;
|
652
|
+
this.result.Errors.push('Maximum OTP attempts exceeded');
|
653
|
+
return this.result;
|
654
|
+
}
|
655
|
+
|
656
|
+
// Mark OTP as used
|
657
|
+
await dbcontext.OTPEntity.updateOne(
|
658
|
+
{ _id: otpRecord._id },
|
659
|
+
{ $set: { is_used: true, attempts: otpRecord.attempts + 1 } }
|
660
|
+
);
|
661
|
+
|
662
|
+
// Update user phone verification
|
663
|
+
await dbcontext.UserEntity.updateOne(
|
664
|
+
{ _id: user._id },
|
665
|
+
{ $set: { phoneVerified: true } }
|
666
|
+
);
|
667
|
+
|
668
|
+
// Create session
|
669
|
+
const sessionResult = await this.createSession(user, AuthProvider.SMS_OTP, AuthMethod.OTP, ipAddress, userAgent);
|
670
|
+
if (sessionResult.Status !== ResponseCode.Ok) {
|
671
|
+
return sessionResult;
|
672
|
+
}
|
673
|
+
|
674
|
+
this.result.Data = {
|
675
|
+
user: this.sanitizeUserData(user),
|
676
|
+
session: sessionResult.Data,
|
677
|
+
message: 'SMS OTP verification successful'
|
678
|
+
};
|
679
|
+
this.result.Status = ResponseCode.Ok;
|
680
|
+
|
681
|
+
return this.result;
|
682
|
+
} catch (ex) {
|
683
|
+
this.result.addException(this.className, 'VerifySMSOTP', ex);
|
684
|
+
return this.result;
|
685
|
+
}
|
686
|
+
}
|
687
|
+
|
688
|
+
// Email OTP Authentication
|
689
|
+
async sendEmailOTP(email: string, purpose: string): Promise<Result> {
|
690
|
+
try {
|
691
|
+
this.result.addMessage(AuditMessageType.info, this.className, 'SendEmailOTP', 'Started');
|
692
|
+
|
693
|
+
if (!Config.props.auth.email_otp.enabled) {
|
694
|
+
this.result.Status = ResponseCode.Error;
|
695
|
+
this.result.Errors.push('Email OTP is not enabled');
|
696
|
+
return this.result;
|
697
|
+
}
|
698
|
+
|
699
|
+
// Generate OTP
|
700
|
+
const otp = this.generateOTP(Config.props.auth.email_otp.otp_length);
|
701
|
+
const expiresAt = new Date(Date.now() + Config.props.auth.email_otp.otp_expiry_minutes * 60 * 1000);
|
702
|
+
|
703
|
+
// Find user by email
|
704
|
+
const user = await dbcontext.UserEntity.findOne({ email: email.toLowerCase() });
|
705
|
+
if (!user) {
|
706
|
+
this.result.Status = ResponseCode.NotExist;
|
707
|
+
this.result.Errors.push('User not found with this email');
|
708
|
+
return this.result;
|
709
|
+
}
|
710
|
+
|
711
|
+
// Create OTP record
|
712
|
+
const otpEntity = new dbcontext.OTPEntity({
|
713
|
+
code: otp,
|
714
|
+
user_id: user._id,
|
715
|
+
auth_provider: AuthProvider.EMAIL_OTP,
|
716
|
+
purpose: purpose,
|
717
|
+
expires_at: expiresAt,
|
718
|
+
max_attempts: Config.props.auth.email_otp.max_attempts
|
719
|
+
});
|
720
|
+
|
721
|
+
await dbcontext.OTPEntity.create(otpEntity);
|
722
|
+
|
723
|
+
// Send email
|
724
|
+
const emailContent = MailHelper.GetEmailConfirmationTemplate(user.name, `Your OTP is: ${otp}. Valid for ${Config.props.auth.email_otp.otp_expiry_minutes} minutes.`, 'Verify OTP');
|
725
|
+
await MailHelper.SendEmail(email, emailContent, 'Email OTP Verification');
|
726
|
+
|
727
|
+
this.result.Data = { message: 'Email OTP sent successfully' };
|
728
|
+
this.result.Status = ResponseCode.Ok;
|
729
|
+
|
730
|
+
return this.result;
|
731
|
+
} catch (ex) {
|
732
|
+
this.result.addException(this.className, 'SendEmailOTP', ex);
|
733
|
+
return this.result;
|
734
|
+
}
|
735
|
+
}
|
736
|
+
|
737
|
+
// Session Management
|
738
|
+
async createSession(user: IUser, authProvider: AuthProvider, authMethod: AuthMethod, ipAddress: string, userAgent: string): Promise<Result> {
|
739
|
+
try {
|
740
|
+
// Check session limits
|
741
|
+
const activeSessions = await dbcontext.SessionEntity.countDocuments({
|
742
|
+
user_id: user._id,
|
743
|
+
is_active: true
|
744
|
+
});
|
745
|
+
|
746
|
+
if (activeSessions >= Config.props.auth.session.max_sessions_per_user && !Config.props.auth.session.concurrent_login_allowed) {
|
747
|
+
// Deactivate oldest session
|
748
|
+
const oldestSession = await dbcontext.SessionEntity.findOne({
|
749
|
+
user_id: user._id,
|
750
|
+
is_active: true
|
751
|
+
}).sort({ created_at: 1 });
|
752
|
+
|
753
|
+
if (oldestSession) {
|
754
|
+
await dbcontext.SessionEntity.updateOne(
|
755
|
+
{ _id: oldestSession._id },
|
756
|
+
{ $set: { is_active: false } }
|
757
|
+
);
|
758
|
+
}
|
759
|
+
}
|
760
|
+
|
761
|
+
// Create new session
|
762
|
+
const sessionId = uuidv4();
|
763
|
+
const expiresAt = new Date(Date.now() + Config.props.auth.session.session_timeout_minutes * 60 * 1000);
|
764
|
+
|
765
|
+
const sessionEntity = new dbcontext.SessionEntity({
|
766
|
+
session_id: sessionId,
|
767
|
+
user_id: user._id,
|
768
|
+
auth_provider: authProvider,
|
769
|
+
auth_method: authMethod,
|
770
|
+
ip_address: ipAddress,
|
771
|
+
user_agent: userAgent,
|
772
|
+
expires_at: expiresAt
|
773
|
+
});
|
774
|
+
|
775
|
+
await dbcontext.SessionEntity.create(sessionEntity);
|
776
|
+
|
777
|
+
// Generate JWT token
|
778
|
+
const token = jwt.sign(
|
779
|
+
{
|
780
|
+
user_id: user._id.toString(),
|
781
|
+
session_id: sessionId,
|
782
|
+
auth_provider: authProvider,
|
783
|
+
auth_method: authMethod
|
784
|
+
},
|
785
|
+
Config.props.auth.jwt.secret,
|
786
|
+
{
|
787
|
+
expiresIn: Config.props.auth.jwt.expires_in,
|
788
|
+
issuer: Config.props.auth.jwt.issuer,
|
789
|
+
audience: Config.props.auth.jwt.audience
|
790
|
+
} as any
|
791
|
+
);
|
792
|
+
|
793
|
+
const refreshToken = jwt.sign(
|
794
|
+
{
|
795
|
+
user_id: user._id.toString(),
|
796
|
+
session_id: sessionId,
|
797
|
+
type: 'refresh'
|
798
|
+
},
|
799
|
+
Config.props.auth.jwt.secret,
|
800
|
+
{
|
801
|
+
expiresIn: Config.props.auth.jwt.refresh_token_expires_in,
|
802
|
+
issuer: Config.props.auth.jwt.issuer,
|
803
|
+
audience: Config.props.auth.jwt.audience
|
804
|
+
} as any
|
805
|
+
);
|
806
|
+
|
807
|
+
return new Result({
|
808
|
+
session_id: sessionId,
|
809
|
+
token: token,
|
810
|
+
refresh_token: refreshToken,
|
811
|
+
expires_at: expiresAt
|
812
|
+
}, ResponseCode.Ok);
|
813
|
+
} catch (ex) {
|
814
|
+
return new Result(null, ResponseCode.Error, [ex], [ex.message]);
|
815
|
+
}
|
816
|
+
}
|
817
|
+
|
818
|
+
async verifySession(sessionId: string, token: string): Promise<Result> {
|
819
|
+
try {
|
820
|
+
// Verify JWT token
|
821
|
+
const decoded = jwt.verify(token, Config.props.auth.jwt.secret) as any;
|
822
|
+
|
823
|
+
// Check session
|
824
|
+
const session = await dbcontext.SessionEntity.findOne({
|
825
|
+
session_id: sessionId,
|
826
|
+
user_id: decoded.user_id,
|
827
|
+
is_active: true,
|
828
|
+
expires_at: { $gt: new Date() }
|
829
|
+
});
|
830
|
+
|
831
|
+
if (!session) {
|
832
|
+
return new Result(null, ResponseCode.Unauthorized, [], ['Invalid or expired session']);
|
833
|
+
}
|
834
|
+
|
835
|
+
// Update last activity
|
836
|
+
await dbcontext.SessionEntity.updateOne(
|
837
|
+
{ _id: session._id },
|
838
|
+
{ $set: { last_activity: new Date() } }
|
839
|
+
);
|
840
|
+
|
841
|
+
// Get user
|
842
|
+
const user = await dbcontext.UserEntity.findById(decoded.user_id);
|
843
|
+
if (!user) {
|
844
|
+
return new Result(null, ResponseCode.NotExist, [], ['User not found']);
|
845
|
+
}
|
846
|
+
|
847
|
+
return new Result({
|
848
|
+
user: this.sanitizeUserData(user),
|
849
|
+
session: session
|
850
|
+
}, ResponseCode.Ok);
|
851
|
+
} catch (ex) {
|
852
|
+
return new Result(null, ResponseCode.Unauthorized, [ex], [ex.message]);
|
853
|
+
}
|
854
|
+
}
|
855
|
+
|
856
|
+
async logout(sessionId: string): Promise<Result> {
|
857
|
+
try {
|
858
|
+
await dbcontext.SessionEntity.updateOne(
|
859
|
+
{ session_id: sessionId },
|
860
|
+
{ $set: { is_active: false } }
|
861
|
+
);
|
862
|
+
|
863
|
+
return new Result({ message: 'Logged out successfully' }, ResponseCode.Ok);
|
864
|
+
} catch (ex) {
|
865
|
+
return new Result(null, ResponseCode.Error, [ex], [ex.message]);
|
866
|
+
}
|
867
|
+
}
|
868
|
+
|
869
|
+
// Utility Methods
|
870
|
+
private validatePassword(password: string): { isValid: boolean; errors: string[] } {
|
871
|
+
const errors: string[] = [];
|
872
|
+
const config = Config.props.auth.internal;
|
873
|
+
|
874
|
+
if (password.length < config.password_min_length) {
|
875
|
+
errors.push(`Password must be at least ${config.password_min_length} characters long`);
|
876
|
+
}
|
877
|
+
|
878
|
+
if (config.password_require_uppercase && !/[A-Z]/.test(password)) {
|
879
|
+
errors.push('Password must contain at least one uppercase letter');
|
880
|
+
}
|
881
|
+
|
882
|
+
if (config.password_require_lowercase && !/[a-z]/.test(password)) {
|
883
|
+
errors.push('Password must contain at least one lowercase letter');
|
884
|
+
}
|
885
|
+
|
886
|
+
if (config.password_require_numbers && !/\d/.test(password)) {
|
887
|
+
errors.push('Password must contain at least one number');
|
888
|
+
}
|
889
|
+
|
890
|
+
if (config.password_require_special_chars && !/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
|
891
|
+
errors.push('Password must contain at least one special character');
|
892
|
+
}
|
893
|
+
|
894
|
+
return { isValid: errors.length === 0, errors };
|
895
|
+
}
|
896
|
+
|
897
|
+
private generateOTP(length: number): string {
|
898
|
+
return Math.random().toString().substr(2, length);
|
899
|
+
}
|
900
|
+
|
901
|
+
private sanitizeUserData(user: IUser): any {
|
902
|
+
return {
|
903
|
+
_id: user._id,
|
904
|
+
name: user.name,
|
905
|
+
email: user.email,
|
906
|
+
username: user.username,
|
907
|
+
emailVerified: user.emailVerified,
|
908
|
+
phone: user.phone,
|
909
|
+
phoneVerified: user.phoneVerified,
|
910
|
+
primary_auth_provider: user.primary_auth_provider,
|
911
|
+
auth_providers: user.auth_providers.map(provider => ({
|
912
|
+
provider: provider.provider,
|
913
|
+
is_verified: provider.is_verified,
|
914
|
+
last_used: provider.last_used
|
915
|
+
})),
|
916
|
+
userType: user.userType,
|
917
|
+
status: user.status,
|
918
|
+
createdAt: user.createdAt,
|
919
|
+
updatedAt: user.updatedAt
|
920
|
+
};
|
921
|
+
}
|
922
|
+
|
923
|
+
private async sendEmailVerification(user: IUser): Promise<void> {
|
924
|
+
const verificationLink = `${Config.props.base_site_url}/verify-email?token=${this.encryptionHelper.encryptUserLogin(user.invitationCode)}&email=${this.encryptionHelper.encryptUserLogin(user.email)}&usrStat=${this.encryptionHelper.encryptUserLogin(user.status)}`;
|
925
|
+
const emailContent = MailHelper.GetEmailConfirmationTemplate(user.name, verificationLink, 'Verify Email');
|
926
|
+
await MailHelper.SendEmail(user.email, emailContent, 'Email Verification');
|
927
|
+
}
|
928
|
+
|
929
|
+
private async getOAuthUserInfo(provider: string, accessToken: string): Promise<any> {
|
930
|
+
// Implementation would vary by provider
|
931
|
+
// This is a placeholder - you'd implement specific OAuth provider APIs
|
932
|
+
switch (provider) {
|
933
|
+
case 'google':
|
934
|
+
// Call Google OAuth API
|
935
|
+
break;
|
936
|
+
case 'facebook':
|
937
|
+
// Call Facebook OAuth API
|
938
|
+
break;
|
939
|
+
case 'github':
|
940
|
+
// Call GitHub OAuth API
|
941
|
+
break;
|
942
|
+
// ... other providers
|
943
|
+
}
|
944
|
+
return null;
|
945
|
+
}
|
946
|
+
|
947
|
+
private async sendSMS(phone: string, message: string): Promise<void> {
|
948
|
+
const config = Config.props.auth.sms_otp;
|
949
|
+
|
950
|
+
if (config.provider === 'twilio' && config.twilio) {
|
951
|
+
const twilio = require('twilio');
|
952
|
+
const client = twilio(config.twilio.account_sid, config.twilio.auth_token);
|
953
|
+
await client.messages.create({
|
954
|
+
body: message,
|
955
|
+
from: config.twilio.from_number,
|
956
|
+
to: phone
|
957
|
+
});
|
958
|
+
} else if (config.provider === 'aws_sns' && config.aws_sns) {
|
959
|
+
const AWS = require('aws-sdk');
|
960
|
+
const sns = new AWS.SNS({
|
961
|
+
accessKeyId: config.aws_sns.access_key_id,
|
962
|
+
secretAccessKey: config.aws_sns.secret_access_key,
|
963
|
+
region: config.aws_sns.region
|
964
|
+
});
|
965
|
+
await sns.publish({
|
966
|
+
Message: message,
|
967
|
+
PhoneNumber: phone
|
968
|
+
}).promise();
|
969
|
+
}
|
970
|
+
}
|
971
|
+
}
|