strapi-plugin-firebase-authentication 1.1.12 → 1.2.1
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/_chunks/{App-BqjE8BHb.js → App-CAgq2cOo.js} +369 -288
- package/dist/_chunks/{App-BY1gNGKH.mjs → App-CyeC5fLT.mjs} +291 -210
- package/dist/_chunks/api-BL-wXuSb.js +56776 -0
- package/dist/_chunks/api-Bjer83Qp.mjs +56759 -0
- package/dist/_chunks/index-Bg-lGlji.mjs +2758 -0
- package/dist/_chunks/{index-C4t4JZZ_.js → index-CTenjpqN.js} +285 -172
- package/dist/_chunks/{index-D8pv1Q6h.mjs → index-Kidqhaeq.mjs} +268 -155
- package/dist/_chunks/index-Rervtuh1.js +2757 -0
- package/dist/admin/index.js +1 -3
- package/dist/admin/index.mjs +2 -4
- package/dist/admin/src/components/user-management/ResendVerification/ResendVerification.d.ts +9 -0
- package/dist/admin/src/components/user-management/index.d.ts +1 -0
- package/dist/admin/src/pages/Settings/api.d.ts +4 -0
- package/dist/admin/src/pages/utils/api.d.ts +2 -1
- package/dist/server/index.js +1008 -332
- package/dist/server/index.mjs +1008 -332
- package/dist/server/src/config/index.d.ts +1 -1
- package/dist/server/src/content-types/index.d.ts +16 -0
- package/dist/server/src/controllers/firebaseController.d.ts +16 -1
- package/dist/server/src/controllers/index.d.ts +3 -0
- package/dist/server/src/controllers/userController.d.ts +1 -0
- package/dist/server/src/index.d.ts +51 -5
- package/dist/server/src/policies/index.d.ts +3 -1
- package/dist/server/src/policies/is-authenticated.d.ts +6 -0
- package/dist/server/src/routes/content-api.d.ts +10 -2
- package/dist/server/src/routes/index.d.ts +10 -2
- package/dist/server/src/services/emailService.d.ts +10 -0
- package/dist/server/src/services/firebaseService.d.ts +21 -1
- package/dist/server/src/services/index.d.ts +18 -1
- package/dist/server/src/services/settingsService.d.ts +2 -0
- package/dist/server/src/services/tokenService.d.ts +21 -0
- package/dist/server/src/services/userService.d.ts +5 -0
- package/dist/server/src/templates/defaults/email-verification.d.ts +2 -0
- package/dist/server/src/templates/defaults/index.d.ts +1 -0
- package/dist/server/src/templates/types.d.ts +3 -1
- package/package.json +8 -8
- package/dist/_chunks/api-DQCdqlCd.js +0 -35
- package/dist/_chunks/api-D_4cdJU5.mjs +0 -36
- package/dist/_chunks/index-DlPxMuSK.js +0 -814
- package/dist/_chunks/index-DtGfwf9S.mjs +0 -815
package/dist/server/index.mjs
CHANGED
|
@@ -198,7 +198,7 @@ const register = ({ strapi: strapi2 }) => {
|
|
|
198
198
|
const config$1 = {
|
|
199
199
|
default: ({ env: env2 }) => ({
|
|
200
200
|
firebaseJsonEncryptionKey: env2("FIREBASE_JSON_ENCRYPTION_KEY", "your-key-here"),
|
|
201
|
-
emailRequired:
|
|
201
|
+
emailRequired: env2.bool("FIREBASE_EMAIL_REQUIRED", false),
|
|
202
202
|
emailPattern: "{randomString}@phone-user.firebase.local"
|
|
203
203
|
}),
|
|
204
204
|
validator(config2) {
|
|
@@ -289,6 +289,15 @@ const attributes$1 = {
|
|
|
289
289
|
minimum: 1,
|
|
290
290
|
maximum: 72,
|
|
291
291
|
description: "How long the magic link remains valid (in hours)"
|
|
292
|
+
},
|
|
293
|
+
emailVerificationUrl: {
|
|
294
|
+
type: "string",
|
|
295
|
+
"default": "http://localhost:3000/verify-email",
|
|
296
|
+
description: "URL where users will be redirected to verify their email"
|
|
297
|
+
},
|
|
298
|
+
emailVerificationEmailSubject: {
|
|
299
|
+
type: "string",
|
|
300
|
+
"default": "Verify Your Email"
|
|
292
301
|
}
|
|
293
302
|
};
|
|
294
303
|
const firebaseAuthenticationConfiguration = {
|
|
@@ -339,6 +348,13 @@ const attributes = {
|
|
|
339
348
|
},
|
|
340
349
|
resetTokenExpiresAt: {
|
|
341
350
|
type: "datetime"
|
|
351
|
+
},
|
|
352
|
+
verificationTokenHash: {
|
|
353
|
+
type: "string",
|
|
354
|
+
"private": true
|
|
355
|
+
},
|
|
356
|
+
verificationTokenExpiresAt: {
|
|
357
|
+
type: "datetime"
|
|
342
358
|
}
|
|
343
359
|
};
|
|
344
360
|
const firebaseUserData = {
|
|
@@ -353,238 +369,6 @@ const contentTypes = {
|
|
|
353
369
|
"firebase-authentication-configuration": { schema: firebaseAuthenticationConfiguration },
|
|
354
370
|
"firebase-user-data": { schema: firebaseUserData }
|
|
355
371
|
};
|
|
356
|
-
const pluginName = "firebase-authentication";
|
|
357
|
-
const PLUGIN_NAME = "firebase-authentication";
|
|
358
|
-
const PLUGIN_UID = `plugin::${PLUGIN_NAME}`;
|
|
359
|
-
const CONFIG_CONTENT_TYPE = `${PLUGIN_UID}.firebase-authentication-configuration`;
|
|
360
|
-
const DEFAULT_PASSWORD_RESET_URL = "http://localhost:3000/reset-password";
|
|
361
|
-
const DEFAULT_PASSWORD_REGEX = "^.{6,}$";
|
|
362
|
-
const DEFAULT_PASSWORD_MESSAGE = "Password must be at least 6 characters long";
|
|
363
|
-
const DEFAULT_RESET_EMAIL_SUBJECT = "Reset Your Password";
|
|
364
|
-
const ERROR_MESSAGES = {
|
|
365
|
-
FIREBASE_NOT_INITIALIZED: "Firebase is not initialized. Please upload Firebase service account configuration via Settings → Firebase Authentication.",
|
|
366
|
-
INVALID_JSON: "Invalid JSON format. Please ensure you copied the entire JSON content correctly.",
|
|
367
|
-
MISSING_DATA: "data is missing",
|
|
368
|
-
SOMETHING_WENT_WRONG: "Something went wrong",
|
|
369
|
-
AUTHENTICATION_FAILED: "Authentication failed",
|
|
370
|
-
TOKEN_MISSING: "idToken is missing!",
|
|
371
|
-
EMAIL_PASSWORD_REQUIRED: "Email and password are required",
|
|
372
|
-
PASSWORD_REQUIRED: "Password is required",
|
|
373
|
-
AUTHORIZATION_REQUIRED: "Authorization token is required",
|
|
374
|
-
INVALID_TOKEN: "Invalid or expired token",
|
|
375
|
-
USER_NOT_FOUND: "User not found",
|
|
376
|
-
USER_NO_EMAIL: "User does not have an email address",
|
|
377
|
-
FIREBASE_LINK_FAILED: "Failed to generate Firebase reset link",
|
|
378
|
-
CONFIG_NOT_FOUND: "No config found",
|
|
379
|
-
INVALID_SERVICE_ACCOUNT: "Invalid service account JSON",
|
|
380
|
-
WEB_API_NOT_CONFIGURED: "Email/password authentication is not available. Web API Key is not configured.",
|
|
381
|
-
RESET_URL_NOT_CONFIGURED: "Password reset URL is not configured",
|
|
382
|
-
RESET_URL_MUST_BE_HTTPS: "Password reset URL must use HTTPS in production",
|
|
383
|
-
RESET_URL_INVALID_FORMAT: "Password reset URL is not a valid URL format",
|
|
384
|
-
USER_NOT_LINKED_FIREBASE: "User is not linked to Firebase authentication",
|
|
385
|
-
OVERRIDE_USER_ID_REQUIRED: "Override user ID is required",
|
|
386
|
-
EITHER_EMAIL_OR_PHONE_REQUIRED: "Either email or phoneNumber is required",
|
|
387
|
-
DELETION_NO_CONFIG: "No Firebase configs exists for deletion"
|
|
388
|
-
};
|
|
389
|
-
const SUCCESS_MESSAGES = {
|
|
390
|
-
FIREBASE_INITIALIZED: "Firebase successfully initialized",
|
|
391
|
-
FIREBASE_CONFIG_DELETED: "Firebase config deleted and reinitialized",
|
|
392
|
-
PASSWORD_RESET_EMAIL_SENT: "If an account with that email exists, a password reset link has been sent.",
|
|
393
|
-
SERVER_RESTARTING: "SERVER IS RESTARTING"
|
|
394
|
-
};
|
|
395
|
-
const CONFIG_KEYS = {
|
|
396
|
-
ENCRYPTION_KEY: `${PLUGIN_UID}.FIREBASE_JSON_ENCRYPTION_KEY`
|
|
397
|
-
};
|
|
398
|
-
const REQUIRED_FIELDS = {
|
|
399
|
-
SERVICE_ACCOUNT: ["private_key", "client_email", "project_id", "type"],
|
|
400
|
-
WEB_CONFIG: ["apiKey", "authDomain"]
|
|
401
|
-
// These indicate wrong JSON type
|
|
402
|
-
};
|
|
403
|
-
const VALIDATION_MESSAGES = {
|
|
404
|
-
INVALID_SERVICE_ACCOUNT: "Invalid Service Account JSON. Missing required fields:",
|
|
405
|
-
WRONG_JSON_TYPE: "You uploaded a Web App Config (SDK snippet) instead of a Service Account JSON. Please go to Firebase Console → Service Accounts tab → Generate New Private Key to download the correct file.",
|
|
406
|
-
SERVICE_ACCOUNT_HELP: "Please download the correct JSON from Firebase Console → Service Accounts → Generate New Private Key. Do NOT use the Web App Config (SDK snippet) - that is a different file!"
|
|
407
|
-
};
|
|
408
|
-
const firebaseController = {
|
|
409
|
-
async validateToken(ctx) {
|
|
410
|
-
strapi.log.debug("validateToken called");
|
|
411
|
-
try {
|
|
412
|
-
const { idToken, profileMetaData } = ctx.request.body || {};
|
|
413
|
-
const populate2 = ctx.request.query.populate || [];
|
|
414
|
-
if (!idToken) {
|
|
415
|
-
return ctx.badRequest(ERROR_MESSAGES.TOKEN_MISSING);
|
|
416
|
-
}
|
|
417
|
-
const result = await strapi.plugin(pluginName).service("firebaseService").validateFirebaseToken(idToken, profileMetaData, populate2);
|
|
418
|
-
ctx.body = result;
|
|
419
|
-
} catch (error2) {
|
|
420
|
-
strapi.log.error(`validateToken controller error: ${error2.message}`);
|
|
421
|
-
if (error2.name === "ValidationError") {
|
|
422
|
-
return ctx.badRequest(error2.message);
|
|
423
|
-
}
|
|
424
|
-
if (error2.name === "UnauthorizedError") {
|
|
425
|
-
return ctx.unauthorized(error2.message);
|
|
426
|
-
}
|
|
427
|
-
throw error2;
|
|
428
|
-
}
|
|
429
|
-
},
|
|
430
|
-
async deleteByEmail(email2) {
|
|
431
|
-
const user = await strapi.firebase.auth().getUserByEmail(email2);
|
|
432
|
-
await strapi.plugin(pluginName).service("firebaseService").delete(user.toJSON().uid);
|
|
433
|
-
return user.toJSON();
|
|
434
|
-
},
|
|
435
|
-
async overrideAccess(ctx) {
|
|
436
|
-
try {
|
|
437
|
-
const { overrideUserId } = ctx.request.body || {};
|
|
438
|
-
const populate2 = ctx.request.query.populate || [];
|
|
439
|
-
const result = await strapi.plugin(pluginName).service("firebaseService").overrideFirebaseAccess(overrideUserId, populate2);
|
|
440
|
-
ctx.body = result;
|
|
441
|
-
} catch (error2) {
|
|
442
|
-
if (error2.name === "ValidationError") {
|
|
443
|
-
return ctx.badRequest(error2.message);
|
|
444
|
-
}
|
|
445
|
-
if (error2.name === "NotFoundError") {
|
|
446
|
-
return ctx.notFound(error2.message);
|
|
447
|
-
}
|
|
448
|
-
throw error2;
|
|
449
|
-
}
|
|
450
|
-
},
|
|
451
|
-
/**
|
|
452
|
-
* Controller method for email/password authentication
|
|
453
|
-
* Handles the `/api/firebase-authentication/emailLogin` endpoint
|
|
454
|
-
*
|
|
455
|
-
* @param ctx - Koa context object
|
|
456
|
-
* @returns Promise that sets ctx.body with user data and JWT or error message
|
|
457
|
-
*
|
|
458
|
-
* @remarks
|
|
459
|
-
* This controller acts as a proxy to Firebase's Identity Toolkit API,
|
|
460
|
-
* allowing users to authenticate with email/password and receive a Strapi JWT.
|
|
461
|
-
*
|
|
462
|
-
* HTTP Status Codes:
|
|
463
|
-
* - `400`: Validation errors (missing credentials, invalid email/password)
|
|
464
|
-
* - `500`: Server errors (missing configuration, Firebase API issues)
|
|
465
|
-
*/
|
|
466
|
-
async emailLogin(ctx) {
|
|
467
|
-
strapi.log.debug("emailLogin controller");
|
|
468
|
-
try {
|
|
469
|
-
const { email: email2, password } = ctx.request.body || {};
|
|
470
|
-
const populate2 = ctx.request.query.populate || [];
|
|
471
|
-
const result = await strapi.plugin(pluginName).service("firebaseService").emailLogin(email2, password, populate2);
|
|
472
|
-
ctx.body = result;
|
|
473
|
-
} catch (error2) {
|
|
474
|
-
strapi.log.error("emailLogin controller error:", error2);
|
|
475
|
-
if (error2.name === "ValidationError") {
|
|
476
|
-
ctx.status = 400;
|
|
477
|
-
} else if (error2.name === "ApplicationError") {
|
|
478
|
-
ctx.status = 500;
|
|
479
|
-
} else {
|
|
480
|
-
ctx.status = 500;
|
|
481
|
-
}
|
|
482
|
-
ctx.body = { error: error2.message };
|
|
483
|
-
}
|
|
484
|
-
},
|
|
485
|
-
/**
|
|
486
|
-
* Forgot password - sends reset email
|
|
487
|
-
* POST /api/firebase-authentication/forgotPassword
|
|
488
|
-
* Public endpoint - no authentication required
|
|
489
|
-
*/
|
|
490
|
-
async forgotPassword(ctx) {
|
|
491
|
-
strapi.log.debug("forgotPassword endpoint called");
|
|
492
|
-
try {
|
|
493
|
-
const { email: email2 } = ctx.request.body || {};
|
|
494
|
-
ctx.body = await strapi.plugin(pluginName).service("firebaseService").forgotPassword(email2);
|
|
495
|
-
} catch (error2) {
|
|
496
|
-
strapi.log.error("forgotPassword controller error:", error2);
|
|
497
|
-
if (error2.name === "ValidationError") {
|
|
498
|
-
ctx.status = 400;
|
|
499
|
-
} else if (error2.name === "NotFoundError") {
|
|
500
|
-
ctx.status = 404;
|
|
501
|
-
} else {
|
|
502
|
-
ctx.status = 500;
|
|
503
|
-
}
|
|
504
|
-
ctx.body = { error: error2.message };
|
|
505
|
-
}
|
|
506
|
-
},
|
|
507
|
-
/**
|
|
508
|
-
* Reset password - authenticated password change
|
|
509
|
-
* POST /api/firebase-authentication/resetPassword
|
|
510
|
-
* Public endpoint - requires valid JWT in Authorization header
|
|
511
|
-
* Used for admin-initiated resets or user self-service password changes
|
|
512
|
-
* NOT used for forgot password email flow (which uses Firebase's hosted UI)
|
|
513
|
-
*/
|
|
514
|
-
async resetPassword(ctx) {
|
|
515
|
-
strapi.log.debug("resetPassword endpoint called");
|
|
516
|
-
try {
|
|
517
|
-
const { password } = ctx.request.body || {};
|
|
518
|
-
const token = ctx.request.headers.authorization?.replace("Bearer ", "");
|
|
519
|
-
const populate2 = ctx.request.query.populate || [];
|
|
520
|
-
ctx.body = await strapi.plugin(pluginName).service("firebaseService").resetPassword(password, token, populate2);
|
|
521
|
-
} catch (error2) {
|
|
522
|
-
strapi.log.error("resetPassword controller error:", error2);
|
|
523
|
-
if (error2.name === "ValidationError" || error2.name === "UnauthorizedError") {
|
|
524
|
-
ctx.status = 401;
|
|
525
|
-
} else {
|
|
526
|
-
ctx.status = 500;
|
|
527
|
-
}
|
|
528
|
-
ctx.body = { error: error2.message };
|
|
529
|
-
}
|
|
530
|
-
},
|
|
531
|
-
async requestMagicLink(ctx) {
|
|
532
|
-
try {
|
|
533
|
-
const { email: email2 } = ctx.request.body || {};
|
|
534
|
-
const result = await strapi.plugin("firebase-authentication").service("firebaseService").requestMagicLink(email2);
|
|
535
|
-
ctx.body = result;
|
|
536
|
-
} catch (error2) {
|
|
537
|
-
if (error2.name === "ValidationError" || error2.name === "ApplicationError") {
|
|
538
|
-
throw error2;
|
|
539
|
-
}
|
|
540
|
-
strapi.log.error("requestMagicLink controller error:", error2);
|
|
541
|
-
ctx.body = {
|
|
542
|
-
success: false,
|
|
543
|
-
message: "An error occurred while processing your request"
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
},
|
|
547
|
-
/**
|
|
548
|
-
* Reset password using custom JWT token
|
|
549
|
-
* POST /api/firebase-authentication/resetPasswordWithToken
|
|
550
|
-
* Public endpoint - token provides authentication
|
|
551
|
-
*
|
|
552
|
-
* @param ctx - Koa context with { token, newPassword } in body
|
|
553
|
-
* @returns { success: true, message: "Password has been reset successfully" }
|
|
554
|
-
*/
|
|
555
|
-
async resetPasswordWithToken(ctx) {
|
|
556
|
-
strapi.log.debug("resetPasswordWithToken endpoint called");
|
|
557
|
-
try {
|
|
558
|
-
const { token, newPassword } = ctx.request.body || {};
|
|
559
|
-
if (!token) {
|
|
560
|
-
ctx.status = 400;
|
|
561
|
-
ctx.body = { error: "Token is required" };
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
if (!newPassword) {
|
|
565
|
-
ctx.status = 400;
|
|
566
|
-
ctx.body = { error: "New password is required" };
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
const result = await strapi.plugin(pluginName).service("userService").resetPasswordWithToken(token, newPassword);
|
|
570
|
-
ctx.body = result;
|
|
571
|
-
} catch (error2) {
|
|
572
|
-
strapi.log.error("resetPasswordWithToken controller error:", error2);
|
|
573
|
-
if (error2.name === "ValidationError") {
|
|
574
|
-
ctx.status = 400;
|
|
575
|
-
ctx.body = { error: error2.message };
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
if (error2.name === "NotFoundError") {
|
|
579
|
-
ctx.status = 404;
|
|
580
|
-
ctx.body = { error: error2.message };
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
ctx.status = 500;
|
|
584
|
-
ctx.body = { error: "An error occurred while resetting your password" };
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
};
|
|
588
372
|
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
|
|
589
373
|
function getDefaultExportFromCjs(x) {
|
|
590
374
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
@@ -599,7 +383,7 @@ var lodash = { exports: {} };
|
|
|
599
383
|
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
|
600
384
|
*/
|
|
601
385
|
lodash.exports;
|
|
602
|
-
(function(module, exports) {
|
|
386
|
+
(function(module, exports$1) {
|
|
603
387
|
(function() {
|
|
604
388
|
var undefined$1;
|
|
605
389
|
var VERSION = "4.17.21";
|
|
@@ -927,7 +711,7 @@ lodash.exports;
|
|
|
927
711
|
var freeGlobal2 = typeof commonjsGlobal == "object" && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
|
|
928
712
|
var freeSelf2 = typeof self == "object" && self && self.Object === Object && self;
|
|
929
713
|
var root2 = freeGlobal2 || freeSelf2 || Function("return this")();
|
|
930
|
-
var freeExports = exports && !exports.nodeType && exports;
|
|
714
|
+
var freeExports = exports$1 && !exports$1.nodeType && exports$1;
|
|
931
715
|
var freeModule = freeExports && true && module && !module.nodeType && module;
|
|
932
716
|
var moduleExports = freeModule && freeModule.exports === freeExports;
|
|
933
717
|
var freeProcess = moduleExports && freeGlobal2.process;
|
|
@@ -6157,7 +5941,7 @@ var lodash_min = { exports: {} };
|
|
|
6157
5941
|
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
|
6158
5942
|
*/
|
|
6159
5943
|
lodash_min.exports;
|
|
6160
|
-
(function(module, exports) {
|
|
5944
|
+
(function(module, exports$1) {
|
|
6161
5945
|
(function() {
|
|
6162
5946
|
function n(n2, t3, r2) {
|
|
6163
5947
|
switch (r2.length) {
|
|
@@ -6590,7 +6374,7 @@ lodash_min.exports;
|
|
|
6590
6374
|
"œ": "oe",
|
|
6591
6375
|
"ʼn": "'n",
|
|
6592
6376
|
"ſ": "s"
|
|
6593
|
-
}, Hr = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }, Jr = { "&": "&", "<": "<", ">": ">", """: '"', "'": "'" }, Yr = { "\\": "\\", "'": "'", "\n": "n", "\r": "r", "\u2028": "u2028", "\u2029": "u2029" }, Qr = parseFloat, Xr = parseInt, ne = "object" == typeof commonjsGlobal && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal, te = "object" == typeof self && self && self.Object === Object && self, re2 = ne || te || Function("return this")(), ee = exports && !exports.nodeType && exports, ue = ee && true && module && !module.nodeType && module, ie = ue && ue.exports === ee, oe = ie && ne.process, fe = function() {
|
|
6377
|
+
}, Hr = { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }, Jr = { "&": "&", "<": "<", ">": ">", """: '"', "'": "'" }, Yr = { "\\": "\\", "'": "'", "\n": "n", "\r": "r", "\u2028": "u2028", "\u2029": "u2029" }, Qr = parseFloat, Xr = parseInt, ne = "object" == typeof commonjsGlobal && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal, te = "object" == typeof self && self && self.Object === Object && self, re2 = ne || te || Function("return this")(), ee = exports$1 && !exports$1.nodeType && exports$1, ue = ee && true && module && !module.nodeType && module, ie = ue && ue.exports === ee, oe = ie && ne.process, fe = function() {
|
|
6594
6378
|
try {
|
|
6595
6379
|
var n2 = ue && ue.require && ue.require("util").types;
|
|
6596
6380
|
return n2 ? n2 : oe && oe.binding && oe.binding("util");
|
|
@@ -9263,8 +9047,8 @@ lodash_min.exports;
|
|
|
9263
9047
|
})(lodash_min, lodash_min.exports);
|
|
9264
9048
|
var lodash_minExports = lodash_min.exports;
|
|
9265
9049
|
var _mapping = {};
|
|
9266
|
-
(function(exports) {
|
|
9267
|
-
exports.aliasToReal = {
|
|
9050
|
+
(function(exports$1) {
|
|
9051
|
+
exports$1.aliasToReal = {
|
|
9268
9052
|
// Lodash aliases.
|
|
9269
9053
|
"each": "forEach",
|
|
9270
9054
|
"eachRight": "forEachRight",
|
|
@@ -9329,7 +9113,7 @@ var _mapping = {};
|
|
|
9329
9113
|
"whereEq": "isMatch",
|
|
9330
9114
|
"zipObj": "zipObject"
|
|
9331
9115
|
};
|
|
9332
|
-
exports.aryMethod = {
|
|
9116
|
+
exports$1.aryMethod = {
|
|
9333
9117
|
"1": [
|
|
9334
9118
|
"assignAll",
|
|
9335
9119
|
"assignInAll",
|
|
@@ -9564,12 +9348,12 @@ var _mapping = {};
|
|
|
9564
9348
|
"updateWith"
|
|
9565
9349
|
]
|
|
9566
9350
|
};
|
|
9567
|
-
exports.aryRearg = {
|
|
9351
|
+
exports$1.aryRearg = {
|
|
9568
9352
|
"2": [1, 0],
|
|
9569
9353
|
"3": [2, 0, 1],
|
|
9570
9354
|
"4": [3, 2, 0, 1]
|
|
9571
9355
|
};
|
|
9572
|
-
exports.iterateeAry = {
|
|
9356
|
+
exports$1.iterateeAry = {
|
|
9573
9357
|
"dropRightWhile": 1,
|
|
9574
9358
|
"dropWhile": 1,
|
|
9575
9359
|
"every": 1,
|
|
@@ -9607,11 +9391,11 @@ var _mapping = {};
|
|
|
9607
9391
|
"times": 1,
|
|
9608
9392
|
"transform": 2
|
|
9609
9393
|
};
|
|
9610
|
-
exports.iterateeRearg = {
|
|
9394
|
+
exports$1.iterateeRearg = {
|
|
9611
9395
|
"mapKeys": [1],
|
|
9612
9396
|
"reduceRight": [1, 0]
|
|
9613
9397
|
};
|
|
9614
|
-
exports.methodRearg = {
|
|
9398
|
+
exports$1.methodRearg = {
|
|
9615
9399
|
"assignInAllWith": [1, 0],
|
|
9616
9400
|
"assignInWith": [1, 2, 0],
|
|
9617
9401
|
"assignAllWith": [1, 0],
|
|
@@ -9642,7 +9426,7 @@ var _mapping = {};
|
|
|
9642
9426
|
"xorWith": [1, 2, 0],
|
|
9643
9427
|
"zipWith": [1, 2, 0]
|
|
9644
9428
|
};
|
|
9645
|
-
exports.methodSpread = {
|
|
9429
|
+
exports$1.methodSpread = {
|
|
9646
9430
|
"assignAll": { "start": 0 },
|
|
9647
9431
|
"assignAllWith": { "start": 0 },
|
|
9648
9432
|
"assignInAll": { "start": 0 },
|
|
@@ -9658,7 +9442,7 @@ var _mapping = {};
|
|
|
9658
9442
|
"without": { "start": 1 },
|
|
9659
9443
|
"zipAll": { "start": 0 }
|
|
9660
9444
|
};
|
|
9661
|
-
exports.mutate = {
|
|
9445
|
+
exports$1.mutate = {
|
|
9662
9446
|
"array": {
|
|
9663
9447
|
"fill": true,
|
|
9664
9448
|
"pull": true,
|
|
@@ -9695,8 +9479,8 @@ var _mapping = {};
|
|
|
9695
9479
|
"updateWith": true
|
|
9696
9480
|
}
|
|
9697
9481
|
};
|
|
9698
|
-
exports.realToAlias = function() {
|
|
9699
|
-
var hasOwnProperty2 = Object.prototype.hasOwnProperty, object2 = exports.aliasToReal, result = {};
|
|
9482
|
+
exports$1.realToAlias = function() {
|
|
9483
|
+
var hasOwnProperty2 = Object.prototype.hasOwnProperty, object2 = exports$1.aliasToReal, result = {};
|
|
9700
9484
|
for (var key in object2) {
|
|
9701
9485
|
var value = object2[key];
|
|
9702
9486
|
if (hasOwnProperty2.call(result, value)) {
|
|
@@ -9707,7 +9491,7 @@ var _mapping = {};
|
|
|
9707
9491
|
}
|
|
9708
9492
|
return result;
|
|
9709
9493
|
}();
|
|
9710
|
-
exports.remap = {
|
|
9494
|
+
exports$1.remap = {
|
|
9711
9495
|
"assignAll": "assign",
|
|
9712
9496
|
"assignAllWith": "assignWith",
|
|
9713
9497
|
"assignInAll": "assignIn",
|
|
@@ -9741,7 +9525,7 @@ var _mapping = {};
|
|
|
9741
9525
|
"trimCharsStart": "trimStart",
|
|
9742
9526
|
"zipAll": "zip"
|
|
9743
9527
|
};
|
|
9744
|
-
exports.skipFixed = {
|
|
9528
|
+
exports$1.skipFixed = {
|
|
9745
9529
|
"castArray": true,
|
|
9746
9530
|
"flow": true,
|
|
9747
9531
|
"flowRight": true,
|
|
@@ -9750,7 +9534,7 @@ var _mapping = {};
|
|
|
9750
9534
|
"rearg": true,
|
|
9751
9535
|
"runInContext": true
|
|
9752
9536
|
};
|
|
9753
|
-
exports.skipRearg = {
|
|
9537
|
+
exports$1.skipRearg = {
|
|
9754
9538
|
"add": true,
|
|
9755
9539
|
"assign": true,
|
|
9756
9540
|
"assignIn": true,
|
|
@@ -10204,13 +9988,13 @@ const traverseEntity = async (visitor2, options2, entity) => {
|
|
|
10204
9988
|
if (fp.isNil(value) || fp.isNil(attribute)) {
|
|
10205
9989
|
continue;
|
|
10206
9990
|
}
|
|
10207
|
-
parent = {
|
|
10208
|
-
schema: schema2,
|
|
10209
|
-
key,
|
|
10210
|
-
attribute,
|
|
10211
|
-
path: newPath
|
|
10212
|
-
};
|
|
10213
9991
|
if (isRelationalAttribute(attribute)) {
|
|
9992
|
+
parent = {
|
|
9993
|
+
schema: schema2,
|
|
9994
|
+
key,
|
|
9995
|
+
attribute,
|
|
9996
|
+
path: newPath
|
|
9997
|
+
};
|
|
10214
9998
|
const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
|
|
10215
9999
|
const method = isMorphRelation ? traverseMorphRelationTarget : traverseRelationTarget(getModel(attribute.target));
|
|
10216
10000
|
if (fp.isArray(value)) {
|
|
@@ -10229,6 +10013,12 @@ const traverseEntity = async (visitor2, options2, entity) => {
|
|
|
10229
10013
|
continue;
|
|
10230
10014
|
}
|
|
10231
10015
|
if (isMediaAttribute(attribute)) {
|
|
10016
|
+
parent = {
|
|
10017
|
+
schema: schema2,
|
|
10018
|
+
key,
|
|
10019
|
+
attribute,
|
|
10020
|
+
path: newPath
|
|
10021
|
+
};
|
|
10232
10022
|
if (fp.isArray(value)) {
|
|
10233
10023
|
const res = new Array(value.length);
|
|
10234
10024
|
for (let i2 = 0; i2 < value.length; i2 += 1) {
|
|
@@ -10245,6 +10035,12 @@ const traverseEntity = async (visitor2, options2, entity) => {
|
|
|
10245
10035
|
continue;
|
|
10246
10036
|
}
|
|
10247
10037
|
if (attribute.type === "component") {
|
|
10038
|
+
parent = {
|
|
10039
|
+
schema: schema2,
|
|
10040
|
+
key,
|
|
10041
|
+
attribute,
|
|
10042
|
+
path: newPath
|
|
10043
|
+
};
|
|
10248
10044
|
const targetSchema = getModel(attribute.component);
|
|
10249
10045
|
if (fp.isArray(value)) {
|
|
10250
10046
|
const res = new Array(value.length);
|
|
@@ -10262,6 +10058,12 @@ const traverseEntity = async (visitor2, options2, entity) => {
|
|
|
10262
10058
|
continue;
|
|
10263
10059
|
}
|
|
10264
10060
|
if (attribute.type === "dynamiczone" && fp.isArray(value)) {
|
|
10061
|
+
parent = {
|
|
10062
|
+
schema: schema2,
|
|
10063
|
+
key,
|
|
10064
|
+
attribute,
|
|
10065
|
+
path: newPath
|
|
10066
|
+
};
|
|
10265
10067
|
const res = new Array(value.length);
|
|
10266
10068
|
for (let i2 = 0; i2 < value.length; i2 += 1) {
|
|
10267
10069
|
const arrayPath = {
|
|
@@ -10286,7 +10088,7 @@ const createVisitorUtils = ({ data }) => ({
|
|
|
10286
10088
|
});
|
|
10287
10089
|
fp.curry(traverseEntity);
|
|
10288
10090
|
var dist = { exports: {} };
|
|
10289
|
-
(function(module, exports) {
|
|
10091
|
+
(function(module, exports$1) {
|
|
10290
10092
|
!function(t2, n) {
|
|
10291
10093
|
module.exports = n(require$$0$2, crypto$1);
|
|
10292
10094
|
}(commonjsGlobal, function(t2, n) {
|
|
@@ -11834,9 +11636,9 @@ function stubFalse() {
|
|
|
11834
11636
|
}
|
|
11835
11637
|
var stubFalse_1 = stubFalse;
|
|
11836
11638
|
isBuffer$2.exports;
|
|
11837
|
-
(function(module, exports) {
|
|
11639
|
+
(function(module, exports$1) {
|
|
11838
11640
|
var root2 = _root, stubFalse2 = stubFalse_1;
|
|
11839
|
-
var freeExports = exports && !exports.nodeType && exports;
|
|
11641
|
+
var freeExports = exports$1 && !exports$1.nodeType && exports$1;
|
|
11840
11642
|
var freeModule = freeExports && true && module && !module.nodeType && module;
|
|
11841
11643
|
var moduleExports = freeModule && freeModule.exports === freeExports;
|
|
11842
11644
|
var Buffer2 = moduleExports ? root2.Buffer : void 0;
|
|
@@ -11863,9 +11665,9 @@ function baseUnary$1(func) {
|
|
|
11863
11665
|
var _baseUnary = baseUnary$1;
|
|
11864
11666
|
var _nodeUtil = { exports: {} };
|
|
11865
11667
|
_nodeUtil.exports;
|
|
11866
|
-
(function(module, exports) {
|
|
11668
|
+
(function(module, exports$1) {
|
|
11867
11669
|
var freeGlobal2 = _freeGlobal;
|
|
11868
|
-
var freeExports = exports && !exports.nodeType && exports;
|
|
11670
|
+
var freeExports = exports$1 && !exports$1.nodeType && exports$1;
|
|
11869
11671
|
var freeModule = freeExports && true && module && !module.nodeType && module;
|
|
11870
11672
|
var moduleExports = freeModule && freeModule.exports === freeExports;
|
|
11871
11673
|
var freeProcess = moduleExports && freeGlobal2.process;
|
|
@@ -14824,7 +14626,7 @@ function toIdentifier(str2) {
|
|
|
14824
14626
|
Object.defineProperty(func, "name", desc);
|
|
14825
14627
|
}
|
|
14826
14628
|
}
|
|
14827
|
-
function populateConstructorExports(exports, codes2, HttpError) {
|
|
14629
|
+
function populateConstructorExports(exports$1, codes2, HttpError) {
|
|
14828
14630
|
codes2.forEach(function forEachCode(code) {
|
|
14829
14631
|
var CodeError;
|
|
14830
14632
|
var name = toIdentifier2(statuses$1.message[code]);
|
|
@@ -14837,8 +14639,8 @@ function toIdentifier(str2) {
|
|
|
14837
14639
|
break;
|
|
14838
14640
|
}
|
|
14839
14641
|
if (CodeError) {
|
|
14840
|
-
exports[code] = CodeError;
|
|
14841
|
-
exports[name] = CodeError;
|
|
14642
|
+
exports$1[code] = CodeError;
|
|
14643
|
+
exports$1[name] = CodeError;
|
|
14842
14644
|
}
|
|
14843
14645
|
});
|
|
14844
14646
|
}
|
|
@@ -14860,7 +14662,7 @@ const formatYupErrors = (yupError) => ({
|
|
|
14860
14662
|
message: yupError.message
|
|
14861
14663
|
});
|
|
14862
14664
|
let ApplicationError$2 = class ApplicationError extends Error {
|
|
14863
|
-
constructor(message = "An application error
|
|
14665
|
+
constructor(message = "An application error occurred", details = {}) {
|
|
14864
14666
|
super();
|
|
14865
14667
|
this.name = "ApplicationError";
|
|
14866
14668
|
this.message = message;
|
|
@@ -18056,8 +17858,8 @@ pkgDir$1.exports.sync = (cwd2) => {
|
|
|
18056
17858
|
};
|
|
18057
17859
|
var pkgDirExports = pkgDir$1.exports;
|
|
18058
17860
|
var utils$8 = {};
|
|
18059
|
-
(function(exports) {
|
|
18060
|
-
exports.isInteger = (num) => {
|
|
17861
|
+
(function(exports$1) {
|
|
17862
|
+
exports$1.isInteger = (num) => {
|
|
18061
17863
|
if (typeof num === "number") {
|
|
18062
17864
|
return Number.isInteger(num);
|
|
18063
17865
|
}
|
|
@@ -18066,13 +17868,13 @@ var utils$8 = {};
|
|
|
18066
17868
|
}
|
|
18067
17869
|
return false;
|
|
18068
17870
|
};
|
|
18069
|
-
exports.find = (node, type2) => node.nodes.find((node2) => node2.type === type2);
|
|
18070
|
-
exports.exceedsLimit = (min, max, step = 1, limit) => {
|
|
17871
|
+
exports$1.find = (node, type2) => node.nodes.find((node2) => node2.type === type2);
|
|
17872
|
+
exports$1.exceedsLimit = (min, max, step = 1, limit) => {
|
|
18071
17873
|
if (limit === false) return false;
|
|
18072
|
-
if (!exports.isInteger(min) || !exports.isInteger(max)) return false;
|
|
17874
|
+
if (!exports$1.isInteger(min) || !exports$1.isInteger(max)) return false;
|
|
18073
17875
|
return (Number(max) - Number(min)) / Number(step) >= limit;
|
|
18074
17876
|
};
|
|
18075
|
-
exports.escapeNode = (block, n = 0, type2) => {
|
|
17877
|
+
exports$1.escapeNode = (block, n = 0, type2) => {
|
|
18076
17878
|
const node = block.nodes[n];
|
|
18077
17879
|
if (!node) return;
|
|
18078
17880
|
if (type2 && node.type === type2 || node.type === "open" || node.type === "close") {
|
|
@@ -18082,7 +17884,7 @@ var utils$8 = {};
|
|
|
18082
17884
|
}
|
|
18083
17885
|
}
|
|
18084
17886
|
};
|
|
18085
|
-
exports.encloseBrace = (node) => {
|
|
17887
|
+
exports$1.encloseBrace = (node) => {
|
|
18086
17888
|
if (node.type !== "brace") return false;
|
|
18087
17889
|
if (node.commas >> 0 + node.ranges >> 0 === 0) {
|
|
18088
17890
|
node.invalid = true;
|
|
@@ -18090,7 +17892,7 @@ var utils$8 = {};
|
|
|
18090
17892
|
}
|
|
18091
17893
|
return false;
|
|
18092
17894
|
};
|
|
18093
|
-
exports.isInvalidBrace = (block) => {
|
|
17895
|
+
exports$1.isInvalidBrace = (block) => {
|
|
18094
17896
|
if (block.type !== "brace") return false;
|
|
18095
17897
|
if (block.invalid === true || block.dollar) return true;
|
|
18096
17898
|
if (block.commas >> 0 + block.ranges >> 0 === 0) {
|
|
@@ -18103,18 +17905,18 @@ var utils$8 = {};
|
|
|
18103
17905
|
}
|
|
18104
17906
|
return false;
|
|
18105
17907
|
};
|
|
18106
|
-
exports.isOpenOrClose = (node) => {
|
|
17908
|
+
exports$1.isOpenOrClose = (node) => {
|
|
18107
17909
|
if (node.type === "open" || node.type === "close") {
|
|
18108
17910
|
return true;
|
|
18109
17911
|
}
|
|
18110
17912
|
return node.open === true || node.close === true;
|
|
18111
17913
|
};
|
|
18112
|
-
exports.reduce = (nodes) => nodes.reduce((acc, node) => {
|
|
17914
|
+
exports$1.reduce = (nodes) => nodes.reduce((acc, node) => {
|
|
18113
17915
|
if (node.type === "text") acc.push(node.value);
|
|
18114
17916
|
if (node.type === "range") node.type = "text";
|
|
18115
17917
|
return acc;
|
|
18116
17918
|
}, []);
|
|
18117
|
-
exports.flatten = (...args) => {
|
|
17919
|
+
exports$1.flatten = (...args) => {
|
|
18118
17920
|
const result = [];
|
|
18119
17921
|
const flat = (arr) => {
|
|
18120
17922
|
for (let i = 0; i < arr.length; i++) {
|
|
@@ -19216,7 +19018,7 @@ var constants$5 = {
|
|
|
19216
19018
|
return win32 === true ? WINDOWS_CHARS : POSIX_CHARS;
|
|
19217
19019
|
}
|
|
19218
19020
|
};
|
|
19219
|
-
(function(exports) {
|
|
19021
|
+
(function(exports$1) {
|
|
19220
19022
|
const path2 = require$$0__default;
|
|
19221
19023
|
const win32 = process.platform === "win32";
|
|
19222
19024
|
const {
|
|
@@ -19225,36 +19027,36 @@ var constants$5 = {
|
|
|
19225
19027
|
REGEX_SPECIAL_CHARS,
|
|
19226
19028
|
REGEX_SPECIAL_CHARS_GLOBAL
|
|
19227
19029
|
} = constants$5;
|
|
19228
|
-
exports.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val);
|
|
19229
|
-
exports.hasRegexChars = (str2) => REGEX_SPECIAL_CHARS.test(str2);
|
|
19230
|
-
exports.isRegexChar = (str2) => str2.length === 1 && exports.hasRegexChars(str2);
|
|
19231
|
-
exports.escapeRegex = (str2) => str2.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1");
|
|
19232
|
-
exports.toPosixSlashes = (str2) => str2.replace(REGEX_BACKSLASH, "/");
|
|
19233
|
-
exports.removeBackslashes = (str2) => {
|
|
19030
|
+
exports$1.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val);
|
|
19031
|
+
exports$1.hasRegexChars = (str2) => REGEX_SPECIAL_CHARS.test(str2);
|
|
19032
|
+
exports$1.isRegexChar = (str2) => str2.length === 1 && exports$1.hasRegexChars(str2);
|
|
19033
|
+
exports$1.escapeRegex = (str2) => str2.replace(REGEX_SPECIAL_CHARS_GLOBAL, "\\$1");
|
|
19034
|
+
exports$1.toPosixSlashes = (str2) => str2.replace(REGEX_BACKSLASH, "/");
|
|
19035
|
+
exports$1.removeBackslashes = (str2) => {
|
|
19234
19036
|
return str2.replace(REGEX_REMOVE_BACKSLASH, (match) => {
|
|
19235
19037
|
return match === "\\" ? "" : match;
|
|
19236
19038
|
});
|
|
19237
19039
|
};
|
|
19238
|
-
exports.supportsLookbehinds = () => {
|
|
19040
|
+
exports$1.supportsLookbehinds = () => {
|
|
19239
19041
|
const segs = process.version.slice(1).split(".").map(Number);
|
|
19240
19042
|
if (segs.length === 3 && segs[0] >= 9 || segs[0] === 8 && segs[1] >= 10) {
|
|
19241
19043
|
return true;
|
|
19242
19044
|
}
|
|
19243
19045
|
return false;
|
|
19244
19046
|
};
|
|
19245
|
-
exports.isWindows = (options2) => {
|
|
19047
|
+
exports$1.isWindows = (options2) => {
|
|
19246
19048
|
if (options2 && typeof options2.windows === "boolean") {
|
|
19247
19049
|
return options2.windows;
|
|
19248
19050
|
}
|
|
19249
19051
|
return win32 === true || path2.sep === "\\";
|
|
19250
19052
|
};
|
|
19251
|
-
exports.escapeLast = (input, char, lastIdx) => {
|
|
19053
|
+
exports$1.escapeLast = (input, char, lastIdx) => {
|
|
19252
19054
|
const idx = input.lastIndexOf(char, lastIdx);
|
|
19253
19055
|
if (idx === -1) return input;
|
|
19254
|
-
if (input[idx - 1] === "\\") return exports.escapeLast(input, char, idx - 1);
|
|
19056
|
+
if (input[idx - 1] === "\\") return exports$1.escapeLast(input, char, idx - 1);
|
|
19255
19057
|
return `${input.slice(0, idx)}\\${input.slice(idx)}`;
|
|
19256
19058
|
};
|
|
19257
|
-
exports.removePrefix = (input, state = {}) => {
|
|
19059
|
+
exports$1.removePrefix = (input, state = {}) => {
|
|
19258
19060
|
let output = input;
|
|
19259
19061
|
if (output.startsWith("./")) {
|
|
19260
19062
|
output = output.slice(2);
|
|
@@ -19262,7 +19064,7 @@ var constants$5 = {
|
|
|
19262
19064
|
}
|
|
19263
19065
|
return output;
|
|
19264
19066
|
};
|
|
19265
|
-
exports.wrapOutput = (input, state = {}, options2 = {}) => {
|
|
19067
|
+
exports$1.wrapOutput = (input, state = {}, options2 = {}) => {
|
|
19266
19068
|
const prepend = options2.contains ? "" : "^";
|
|
19267
19069
|
const append2 = options2.contains ? "" : "$";
|
|
19268
19070
|
let output = `${prepend}(?:${input})${append2}`;
|
|
@@ -22817,6 +22619,18 @@ function charFromCodepoint(c) {
|
|
|
22817
22619
|
(c - 65536 & 1023) + 56320
|
|
22818
22620
|
);
|
|
22819
22621
|
}
|
|
22622
|
+
function setProperty(object2, key, value) {
|
|
22623
|
+
if (key === "__proto__") {
|
|
22624
|
+
Object.defineProperty(object2, key, {
|
|
22625
|
+
configurable: true,
|
|
22626
|
+
enumerable: true,
|
|
22627
|
+
writable: true,
|
|
22628
|
+
value
|
|
22629
|
+
});
|
|
22630
|
+
} else {
|
|
22631
|
+
object2[key] = value;
|
|
22632
|
+
}
|
|
22633
|
+
}
|
|
22820
22634
|
var simpleEscapeCheck = new Array(256);
|
|
22821
22635
|
var simpleEscapeMap = new Array(256);
|
|
22822
22636
|
for (var i = 0; i < 256; i++) {
|
|
@@ -22923,7 +22737,7 @@ function mergeMappings(state, destination, source, overridableKeys) {
|
|
|
22923
22737
|
for (index2 = 0, quantity = sourceKeys.length; index2 < quantity; index2 += 1) {
|
|
22924
22738
|
key = sourceKeys[index2];
|
|
22925
22739
|
if (!_hasOwnProperty$1.call(destination, key)) {
|
|
22926
|
-
destination
|
|
22740
|
+
setProperty(destination, key, source[key]);
|
|
22927
22741
|
overridableKeys[key] = true;
|
|
22928
22742
|
}
|
|
22929
22743
|
}
|
|
@@ -22962,7 +22776,7 @@ function storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valu
|
|
|
22962
22776
|
state.position = startPos || state.position;
|
|
22963
22777
|
throwError(state, "duplicated mapping key");
|
|
22964
22778
|
}
|
|
22965
|
-
_result
|
|
22779
|
+
setProperty(_result, keyNode, valueNode);
|
|
22966
22780
|
delete overridableKeys[keyNode];
|
|
22967
22781
|
}
|
|
22968
22782
|
return _result;
|
|
@@ -28865,6 +28679,247 @@ _enum([
|
|
|
28865
28679
|
"published"
|
|
28866
28680
|
]).describe("Filter by publication status");
|
|
28867
28681
|
string().describe("Search query string");
|
|
28682
|
+
const pluginName = "firebase-authentication";
|
|
28683
|
+
const PLUGIN_NAME = "firebase-authentication";
|
|
28684
|
+
const PLUGIN_UID = `plugin::${PLUGIN_NAME}`;
|
|
28685
|
+
const CONFIG_CONTENT_TYPE = `${PLUGIN_UID}.firebase-authentication-configuration`;
|
|
28686
|
+
const DEFAULT_PASSWORD_RESET_URL = "http://localhost:3000/reset-password";
|
|
28687
|
+
const DEFAULT_PASSWORD_REGEX = "^.{6,}$";
|
|
28688
|
+
const DEFAULT_PASSWORD_MESSAGE = "Password must be at least 6 characters long";
|
|
28689
|
+
const DEFAULT_RESET_EMAIL_SUBJECT = "Reset Your Password";
|
|
28690
|
+
const ERROR_MESSAGES = {
|
|
28691
|
+
FIREBASE_NOT_INITIALIZED: "Firebase is not initialized. Please upload Firebase service account configuration via Settings → Firebase Authentication.",
|
|
28692
|
+
INVALID_JSON: "Invalid JSON format. Please ensure you copied the entire JSON content correctly.",
|
|
28693
|
+
MISSING_DATA: "data is missing",
|
|
28694
|
+
SOMETHING_WENT_WRONG: "Something went wrong",
|
|
28695
|
+
AUTHENTICATION_FAILED: "Authentication failed",
|
|
28696
|
+
TOKEN_MISSING: "idToken is missing!",
|
|
28697
|
+
EMAIL_PASSWORD_REQUIRED: "Email and password are required",
|
|
28698
|
+
PASSWORD_REQUIRED: "Password is required",
|
|
28699
|
+
AUTHORIZATION_REQUIRED: "Authorization token is required",
|
|
28700
|
+
INVALID_TOKEN: "Invalid or expired token",
|
|
28701
|
+
USER_NOT_FOUND: "User not found",
|
|
28702
|
+
USER_NO_EMAIL: "User does not have an email address",
|
|
28703
|
+
FIREBASE_LINK_FAILED: "Failed to generate Firebase reset link",
|
|
28704
|
+
CONFIG_NOT_FOUND: "No config found",
|
|
28705
|
+
INVALID_SERVICE_ACCOUNT: "Invalid service account JSON",
|
|
28706
|
+
WEB_API_NOT_CONFIGURED: "Email/password authentication is not available. Web API Key is not configured.",
|
|
28707
|
+
RESET_URL_NOT_CONFIGURED: "Password reset URL is not configured",
|
|
28708
|
+
RESET_URL_MUST_BE_HTTPS: "Password reset URL must use HTTPS in production",
|
|
28709
|
+
RESET_URL_INVALID_FORMAT: "Password reset URL is not a valid URL format",
|
|
28710
|
+
USER_NOT_LINKED_FIREBASE: "User is not linked to Firebase authentication",
|
|
28711
|
+
OVERRIDE_USER_ID_REQUIRED: "Override user ID is required",
|
|
28712
|
+
EITHER_EMAIL_OR_PHONE_REQUIRED: "Either email or phoneNumber is required",
|
|
28713
|
+
DELETION_NO_CONFIG: "No Firebase configs exists for deletion"
|
|
28714
|
+
};
|
|
28715
|
+
const SUCCESS_MESSAGES = {
|
|
28716
|
+
FIREBASE_INITIALIZED: "Firebase successfully initialized",
|
|
28717
|
+
FIREBASE_CONFIG_DELETED: "Firebase config deleted and reinitialized",
|
|
28718
|
+
PASSWORD_RESET_EMAIL_SENT: "If an account with that email exists, a password reset link has been sent.",
|
|
28719
|
+
SERVER_RESTARTING: "SERVER IS RESTARTING"
|
|
28720
|
+
};
|
|
28721
|
+
const CONFIG_KEYS = {
|
|
28722
|
+
ENCRYPTION_KEY: `${PLUGIN_UID}.FIREBASE_JSON_ENCRYPTION_KEY`
|
|
28723
|
+
};
|
|
28724
|
+
const REQUIRED_FIELDS = {
|
|
28725
|
+
SERVICE_ACCOUNT: ["private_key", "client_email", "project_id", "type"],
|
|
28726
|
+
WEB_CONFIG: ["apiKey", "authDomain"]
|
|
28727
|
+
// These indicate wrong JSON type
|
|
28728
|
+
};
|
|
28729
|
+
const VALIDATION_MESSAGES = {
|
|
28730
|
+
INVALID_SERVICE_ACCOUNT: "Invalid Service Account JSON. Missing required fields:",
|
|
28731
|
+
WRONG_JSON_TYPE: "You uploaded a Web App Config (SDK snippet) instead of a Service Account JSON. Please go to Firebase Console → Service Accounts tab → Generate New Private Key to download the correct file.",
|
|
28732
|
+
SERVICE_ACCOUNT_HELP: "Please download the correct JSON from Firebase Console → Service Accounts → Generate New Private Key. Do NOT use the Web App Config (SDK snippet) - that is a different file!"
|
|
28733
|
+
};
|
|
28734
|
+
const firebaseController = {
|
|
28735
|
+
async validateToken(ctx) {
|
|
28736
|
+
strapi.log.debug("validateToken called");
|
|
28737
|
+
try {
|
|
28738
|
+
const { idToken, profileMetaData } = ctx.request.body || {};
|
|
28739
|
+
const populate2 = ctx.request.query.populate || [];
|
|
28740
|
+
if (!idToken) {
|
|
28741
|
+
return ctx.badRequest(ERROR_MESSAGES.TOKEN_MISSING);
|
|
28742
|
+
}
|
|
28743
|
+
const result = await strapi.plugin(pluginName).service("firebaseService").validateFirebaseToken(idToken, profileMetaData, populate2);
|
|
28744
|
+
ctx.body = result;
|
|
28745
|
+
} catch (error2) {
|
|
28746
|
+
strapi.log.error(`validateToken controller error: ${error2.message}`);
|
|
28747
|
+
if (error2.name === "ValidationError") {
|
|
28748
|
+
return ctx.badRequest(error2.message);
|
|
28749
|
+
}
|
|
28750
|
+
if (error2.name === "UnauthorizedError") {
|
|
28751
|
+
return ctx.unauthorized(error2.message);
|
|
28752
|
+
}
|
|
28753
|
+
throw error2;
|
|
28754
|
+
}
|
|
28755
|
+
},
|
|
28756
|
+
async deleteByEmail(email2) {
|
|
28757
|
+
try {
|
|
28758
|
+
const user = await strapi.firebase.auth().getUserByEmail(email2);
|
|
28759
|
+
await strapi.plugin(pluginName).service("firebaseService").delete(user.toJSON().uid);
|
|
28760
|
+
return user.toJSON();
|
|
28761
|
+
} catch (error2) {
|
|
28762
|
+
strapi.log.error("deleteByEmail error:", error2);
|
|
28763
|
+
throw error2;
|
|
28764
|
+
}
|
|
28765
|
+
},
|
|
28766
|
+
async overrideAccess(ctx) {
|
|
28767
|
+
try {
|
|
28768
|
+
const { overrideUserId } = ctx.request.body || {};
|
|
28769
|
+
const populate2 = ctx.request.query.populate || [];
|
|
28770
|
+
const result = await strapi.plugin(pluginName).service("firebaseService").overrideFirebaseAccess(overrideUserId, populate2);
|
|
28771
|
+
ctx.body = result;
|
|
28772
|
+
} catch (error2) {
|
|
28773
|
+
if (error2.name === "ValidationError") {
|
|
28774
|
+
return ctx.badRequest(error2.message);
|
|
28775
|
+
}
|
|
28776
|
+
if (error2.name === "NotFoundError") {
|
|
28777
|
+
return ctx.notFound(error2.message);
|
|
28778
|
+
}
|
|
28779
|
+
throw error2;
|
|
28780
|
+
}
|
|
28781
|
+
},
|
|
28782
|
+
/**
|
|
28783
|
+
* Controller method for email/password authentication
|
|
28784
|
+
* Handles the `/api/firebase-authentication/emailLogin` endpoint
|
|
28785
|
+
*
|
|
28786
|
+
* @param ctx - Koa context object
|
|
28787
|
+
* @returns Promise that sets ctx.body with user data and JWT or error message
|
|
28788
|
+
*
|
|
28789
|
+
* @remarks
|
|
28790
|
+
* This controller acts as a proxy to Firebase's Identity Toolkit API,
|
|
28791
|
+
* allowing users to authenticate with email/password and receive a Strapi JWT.
|
|
28792
|
+
*
|
|
28793
|
+
* HTTP Status Codes:
|
|
28794
|
+
* - `400`: Validation errors (missing credentials, invalid email/password)
|
|
28795
|
+
* - `500`: Server errors (missing configuration, Firebase API issues)
|
|
28796
|
+
*/
|
|
28797
|
+
async emailLogin(ctx) {
|
|
28798
|
+
strapi.log.debug("emailLogin controller");
|
|
28799
|
+
try {
|
|
28800
|
+
const { email: email2, password } = ctx.request.body || {};
|
|
28801
|
+
const populate2 = ctx.request.query.populate || [];
|
|
28802
|
+
const result = await strapi.plugin(pluginName).service("firebaseService").emailLogin(email2, password, populate2);
|
|
28803
|
+
ctx.body = result;
|
|
28804
|
+
} catch (error2) {
|
|
28805
|
+
strapi.log.error("emailLogin controller error:", error2);
|
|
28806
|
+
throw error2;
|
|
28807
|
+
}
|
|
28808
|
+
},
|
|
28809
|
+
/**
|
|
28810
|
+
* Forgot password - sends reset email
|
|
28811
|
+
* POST /api/firebase-authentication/forgotPassword
|
|
28812
|
+
* Public endpoint - no authentication required
|
|
28813
|
+
*/
|
|
28814
|
+
async forgotPassword(ctx) {
|
|
28815
|
+
strapi.log.debug("forgotPassword endpoint called");
|
|
28816
|
+
try {
|
|
28817
|
+
const { email: email2 } = ctx.request.body || {};
|
|
28818
|
+
ctx.body = await strapi.plugin(pluginName).service("firebaseService").forgotPassword(email2);
|
|
28819
|
+
} catch (error2) {
|
|
28820
|
+
strapi.log.error("forgotPassword controller error:", error2);
|
|
28821
|
+
throw error2;
|
|
28822
|
+
}
|
|
28823
|
+
},
|
|
28824
|
+
/**
|
|
28825
|
+
* Reset password - authenticated password change
|
|
28826
|
+
* POST /api/firebase-authentication/resetPassword
|
|
28827
|
+
* Authenticated endpoint - requires valid JWT (enforced by is-authenticated policy)
|
|
28828
|
+
* Used for admin-initiated resets or user self-service password changes
|
|
28829
|
+
* NOT used for forgot password email flow (which uses Firebase's hosted UI)
|
|
28830
|
+
*/
|
|
28831
|
+
async resetPassword(ctx) {
|
|
28832
|
+
strapi.log.debug("resetPassword endpoint called");
|
|
28833
|
+
try {
|
|
28834
|
+
const { password } = ctx.request.body || {};
|
|
28835
|
+
const user = ctx.state.user;
|
|
28836
|
+
const populate2 = ctx.request.query.populate || [];
|
|
28837
|
+
ctx.body = await strapi.plugin(pluginName).service("firebaseService").resetPassword(password, user, populate2);
|
|
28838
|
+
} catch (error2) {
|
|
28839
|
+
strapi.log.error("resetPassword controller error:", error2);
|
|
28840
|
+
throw error2;
|
|
28841
|
+
}
|
|
28842
|
+
},
|
|
28843
|
+
async requestMagicLink(ctx) {
|
|
28844
|
+
try {
|
|
28845
|
+
const { email: email2 } = ctx.request.body || {};
|
|
28846
|
+
const result = await strapi.plugin("firebase-authentication").service("firebaseService").requestMagicLink(email2);
|
|
28847
|
+
ctx.body = result;
|
|
28848
|
+
} catch (error2) {
|
|
28849
|
+
if (error2.name === "ValidationError" || error2.name === "ApplicationError") {
|
|
28850
|
+
throw error2;
|
|
28851
|
+
}
|
|
28852
|
+
strapi.log.error("requestMagicLink controller error:", error2);
|
|
28853
|
+
ctx.body = {
|
|
28854
|
+
success: false,
|
|
28855
|
+
message: "An error occurred while processing your request"
|
|
28856
|
+
};
|
|
28857
|
+
}
|
|
28858
|
+
},
|
|
28859
|
+
/**
|
|
28860
|
+
* Reset password using custom JWT token
|
|
28861
|
+
* POST /api/firebase-authentication/resetPasswordWithToken
|
|
28862
|
+
* Public endpoint - token provides authentication
|
|
28863
|
+
*
|
|
28864
|
+
* @param ctx - Koa context with { token, newPassword } in body
|
|
28865
|
+
* @returns { success: true, message: "Password has been reset successfully" }
|
|
28866
|
+
*/
|
|
28867
|
+
async resetPasswordWithToken(ctx) {
|
|
28868
|
+
strapi.log.debug("resetPasswordWithToken endpoint called");
|
|
28869
|
+
try {
|
|
28870
|
+
const { token, newPassword } = ctx.request.body || {};
|
|
28871
|
+
if (!token) {
|
|
28872
|
+
throw new ValidationError$1("Token is required");
|
|
28873
|
+
}
|
|
28874
|
+
if (!newPassword) {
|
|
28875
|
+
throw new ValidationError$1("New password is required");
|
|
28876
|
+
}
|
|
28877
|
+
const result = await strapi.plugin(pluginName).service("userService").resetPasswordWithToken(token, newPassword);
|
|
28878
|
+
ctx.body = result;
|
|
28879
|
+
} catch (error2) {
|
|
28880
|
+
strapi.log.error("resetPasswordWithToken controller error:", error2);
|
|
28881
|
+
throw error2;
|
|
28882
|
+
}
|
|
28883
|
+
},
|
|
28884
|
+
/**
|
|
28885
|
+
* Send email verification - public endpoint
|
|
28886
|
+
* POST /api/firebase-authentication/sendVerificationEmail
|
|
28887
|
+
* Authenticated endpoint - sends verification email to the logged-in user's email
|
|
28888
|
+
*/
|
|
28889
|
+
async sendVerificationEmail(ctx) {
|
|
28890
|
+
strapi.log.debug("sendVerificationEmail endpoint called");
|
|
28891
|
+
try {
|
|
28892
|
+
const user = ctx.state.user;
|
|
28893
|
+
const email2 = user.email;
|
|
28894
|
+
ctx.body = await strapi.plugin(pluginName).service("firebaseService").sendVerificationEmail(email2);
|
|
28895
|
+
} catch (error2) {
|
|
28896
|
+
strapi.log.error("sendVerificationEmail controller error:", error2);
|
|
28897
|
+
throw error2;
|
|
28898
|
+
}
|
|
28899
|
+
},
|
|
28900
|
+
/**
|
|
28901
|
+
* Verify email using custom JWT token
|
|
28902
|
+
* POST /api/firebase-authentication/verifyEmail
|
|
28903
|
+
* Public endpoint - token provides authentication
|
|
28904
|
+
*
|
|
28905
|
+
* @param ctx - Koa context with { token } in body
|
|
28906
|
+
* @returns { success: true, message: "Email verified successfully" }
|
|
28907
|
+
*/
|
|
28908
|
+
async verifyEmail(ctx) {
|
|
28909
|
+
strapi.log.debug("verifyEmail endpoint called");
|
|
28910
|
+
try {
|
|
28911
|
+
const { token } = ctx.request.body || {};
|
|
28912
|
+
if (!token) {
|
|
28913
|
+
throw new ValidationError$1("Token is required");
|
|
28914
|
+
}
|
|
28915
|
+
const result = await strapi.plugin(pluginName).service("firebaseService").verifyEmail(token);
|
|
28916
|
+
ctx.body = result;
|
|
28917
|
+
} catch (error2) {
|
|
28918
|
+
strapi.log.error("verifyEmail controller error:", error2);
|
|
28919
|
+
throw error2;
|
|
28920
|
+
}
|
|
28921
|
+
}
|
|
28922
|
+
};
|
|
28868
28923
|
const STRAPI_DESTINATION = "strapi";
|
|
28869
28924
|
const FIREBASE_DESTINATION = "firebase";
|
|
28870
28925
|
const userController = {
|
|
@@ -28950,6 +29005,17 @@ const userController = {
|
|
|
28950
29005
|
} catch (error2) {
|
|
28951
29006
|
throw new ApplicationError$2(error2.message || "Failed to send password reset email");
|
|
28952
29007
|
}
|
|
29008
|
+
},
|
|
29009
|
+
sendVerificationEmail: async (ctx) => {
|
|
29010
|
+
const userId = ctx.params.id;
|
|
29011
|
+
if (!userId) {
|
|
29012
|
+
throw new ValidationError$1("User ID is required");
|
|
29013
|
+
}
|
|
29014
|
+
try {
|
|
29015
|
+
ctx.body = await strapi.plugin("firebase-authentication").service("userService").sendVerificationEmail(userId);
|
|
29016
|
+
} catch (error2) {
|
|
29017
|
+
throw new ApplicationError$2(error2.message || "Failed to send verification email");
|
|
29018
|
+
}
|
|
28953
29019
|
}
|
|
28954
29020
|
};
|
|
28955
29021
|
const settingsController = {
|
|
@@ -29052,7 +29118,9 @@ const settingsController = {
|
|
|
29052
29118
|
enableMagicLink = false,
|
|
29053
29119
|
magicLinkUrl = "http://localhost:1338/verify-magic-link.html",
|
|
29054
29120
|
magicLinkEmailSubject = "Sign in to Your Application",
|
|
29055
|
-
magicLinkExpiryHours = 1
|
|
29121
|
+
magicLinkExpiryHours = 1,
|
|
29122
|
+
emailVerificationUrl = "http://localhost:3000/verify-email",
|
|
29123
|
+
emailVerificationEmailSubject = "Verify Your Email"
|
|
29056
29124
|
} = requestBody;
|
|
29057
29125
|
const existingConfig = await strapi.db.query("plugin::firebase-authentication.firebase-authentication-configuration").findOne({ where: {} });
|
|
29058
29126
|
let result;
|
|
@@ -29066,7 +29134,9 @@ const settingsController = {
|
|
|
29066
29134
|
enableMagicLink,
|
|
29067
29135
|
magicLinkUrl,
|
|
29068
29136
|
magicLinkEmailSubject,
|
|
29069
|
-
magicLinkExpiryHours
|
|
29137
|
+
magicLinkExpiryHours,
|
|
29138
|
+
emailVerificationUrl,
|
|
29139
|
+
emailVerificationEmailSubject
|
|
29070
29140
|
}
|
|
29071
29141
|
});
|
|
29072
29142
|
} else {
|
|
@@ -29080,7 +29150,9 @@ const settingsController = {
|
|
|
29080
29150
|
enableMagicLink,
|
|
29081
29151
|
magicLinkUrl,
|
|
29082
29152
|
magicLinkEmailSubject,
|
|
29083
|
-
magicLinkExpiryHours
|
|
29153
|
+
magicLinkExpiryHours,
|
|
29154
|
+
emailVerificationUrl,
|
|
29155
|
+
emailVerificationEmailSubject
|
|
29084
29156
|
}
|
|
29085
29157
|
});
|
|
29086
29158
|
}
|
|
@@ -29092,7 +29164,9 @@ const settingsController = {
|
|
|
29092
29164
|
enableMagicLink: result.enableMagicLink,
|
|
29093
29165
|
magicLinkUrl: result.magicLinkUrl,
|
|
29094
29166
|
magicLinkEmailSubject: result.magicLinkEmailSubject,
|
|
29095
|
-
magicLinkExpiryHours: result.magicLinkExpiryHours
|
|
29167
|
+
magicLinkExpiryHours: result.magicLinkExpiryHours,
|
|
29168
|
+
emailVerificationUrl: result.emailVerificationUrl,
|
|
29169
|
+
emailVerificationEmailSubject: result.emailVerificationEmailSubject
|
|
29096
29170
|
};
|
|
29097
29171
|
} catch (error2) {
|
|
29098
29172
|
throw new ApplicationError$2("Error saving password configuration", {
|
|
@@ -29107,7 +29181,16 @@ const controllers = {
|
|
|
29107
29181
|
settingsController
|
|
29108
29182
|
};
|
|
29109
29183
|
const middlewares = {};
|
|
29110
|
-
const
|
|
29184
|
+
const isAuthenticated = async (policyContext) => {
|
|
29185
|
+
const user = policyContext.state.user;
|
|
29186
|
+
if (!user) {
|
|
29187
|
+
throw new UnauthorizedError("Authentication required");
|
|
29188
|
+
}
|
|
29189
|
+
return true;
|
|
29190
|
+
};
|
|
29191
|
+
const policies = {
|
|
29192
|
+
"is-authenticated": isAuthenticated
|
|
29193
|
+
};
|
|
29111
29194
|
const settingsRoute = [
|
|
29112
29195
|
{
|
|
29113
29196
|
method: "POST",
|
|
@@ -29185,6 +29268,14 @@ const admin = {
|
|
|
29185
29268
|
policies: ["admin::isAuthenticatedAdmin"]
|
|
29186
29269
|
}
|
|
29187
29270
|
},
|
|
29271
|
+
{
|
|
29272
|
+
method: "PUT",
|
|
29273
|
+
path: "/users/sendVerificationEmail/:id",
|
|
29274
|
+
handler: "userController.sendVerificationEmail",
|
|
29275
|
+
config: {
|
|
29276
|
+
policies: ["admin::isAuthenticatedAdmin"]
|
|
29277
|
+
}
|
|
29278
|
+
},
|
|
29188
29279
|
{
|
|
29189
29280
|
method: "GET",
|
|
29190
29281
|
path: "/users/:id",
|
|
@@ -29249,9 +29340,7 @@ const contentApi = {
|
|
|
29249
29340
|
path: "/resetPassword",
|
|
29250
29341
|
handler: "firebaseController.resetPassword",
|
|
29251
29342
|
config: {
|
|
29252
|
-
|
|
29253
|
-
// Public endpoint - authenticated password change, requires valid JWT in Authorization header
|
|
29254
|
-
policies: []
|
|
29343
|
+
policies: ["plugin::firebase-authentication.is-authenticated"]
|
|
29255
29344
|
}
|
|
29256
29345
|
},
|
|
29257
29346
|
{
|
|
@@ -29283,6 +29372,24 @@ const contentApi = {
|
|
|
29283
29372
|
// Public endpoint - token provides authentication
|
|
29284
29373
|
policies: []
|
|
29285
29374
|
}
|
|
29375
|
+
},
|
|
29376
|
+
{
|
|
29377
|
+
method: "POST",
|
|
29378
|
+
path: "/sendVerificationEmail",
|
|
29379
|
+
handler: "firebaseController.sendVerificationEmail",
|
|
29380
|
+
config: {
|
|
29381
|
+
policies: ["plugin::firebase-authentication.is-authenticated"]
|
|
29382
|
+
}
|
|
29383
|
+
},
|
|
29384
|
+
{
|
|
29385
|
+
method: "POST",
|
|
29386
|
+
path: "/verifyEmail",
|
|
29387
|
+
handler: "firebaseController.verifyEmail",
|
|
29388
|
+
config: {
|
|
29389
|
+
auth: false,
|
|
29390
|
+
// Public endpoint - token provides authentication
|
|
29391
|
+
policies: []
|
|
29392
|
+
}
|
|
29286
29393
|
}
|
|
29287
29394
|
]
|
|
29288
29395
|
};
|
|
@@ -29403,7 +29510,10 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29403
29510
|
enableMagicLink: configObject.enableMagicLink || false,
|
|
29404
29511
|
magicLinkUrl: configObject.magicLinkUrl || "http://localhost:1338/verify-magic-link.html",
|
|
29405
29512
|
magicLinkEmailSubject: configObject.magicLinkEmailSubject || "Sign in to Your Application",
|
|
29406
|
-
magicLinkExpiryHours: configObject.magicLinkExpiryHours || 1
|
|
29513
|
+
magicLinkExpiryHours: configObject.magicLinkExpiryHours || 1,
|
|
29514
|
+
// Include email verification configuration fields
|
|
29515
|
+
emailVerificationUrl: configObject.emailVerificationUrl || "http://localhost:3000/verify-email",
|
|
29516
|
+
emailVerificationEmailSubject: configObject.emailVerificationEmailSubject || "Verify Your Email"
|
|
29407
29517
|
};
|
|
29408
29518
|
} catch (error2) {
|
|
29409
29519
|
strapi2.log.error(`Firebase config error: ${error2.message}`);
|
|
@@ -29446,7 +29556,9 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29446
29556
|
enableMagicLink = false,
|
|
29447
29557
|
magicLinkUrl = "http://localhost:1338/verify-magic-link.html",
|
|
29448
29558
|
magicLinkEmailSubject = "Sign in to Your Application",
|
|
29449
|
-
magicLinkExpiryHours = 1
|
|
29559
|
+
magicLinkExpiryHours = 1,
|
|
29560
|
+
emailVerificationUrl = "http://localhost:3000/verify-email",
|
|
29561
|
+
emailVerificationEmailSubject = "Verify Your Email"
|
|
29450
29562
|
} = requestBody;
|
|
29451
29563
|
if (!requestBody) throw new ValidationError3(ERROR_MESSAGES.MISSING_DATA);
|
|
29452
29564
|
try {
|
|
@@ -29482,7 +29594,9 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29482
29594
|
enableMagicLink,
|
|
29483
29595
|
magicLinkUrl,
|
|
29484
29596
|
magicLinkEmailSubject,
|
|
29485
|
-
magicLinkExpiryHours
|
|
29597
|
+
magicLinkExpiryHours,
|
|
29598
|
+
emailVerificationUrl,
|
|
29599
|
+
emailVerificationEmailSubject
|
|
29486
29600
|
}
|
|
29487
29601
|
});
|
|
29488
29602
|
} else {
|
|
@@ -29498,11 +29612,20 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29498
29612
|
enableMagicLink,
|
|
29499
29613
|
magicLinkUrl,
|
|
29500
29614
|
magicLinkEmailSubject,
|
|
29501
|
-
magicLinkExpiryHours
|
|
29615
|
+
magicLinkExpiryHours,
|
|
29616
|
+
emailVerificationUrl,
|
|
29617
|
+
emailVerificationEmailSubject
|
|
29502
29618
|
}
|
|
29503
29619
|
});
|
|
29504
29620
|
}
|
|
29505
29621
|
await strapi2.plugin("firebase-authentication").service("settingsService").init();
|
|
29622
|
+
setImmediate(async () => {
|
|
29623
|
+
try {
|
|
29624
|
+
await strapi2.plugin("firebase-authentication").service("autoLinkService").linkAllUsers(strapi2);
|
|
29625
|
+
} catch (error2) {
|
|
29626
|
+
strapi2.log.error(`Auto-linking after config save failed: ${error2.message}`);
|
|
29627
|
+
}
|
|
29628
|
+
});
|
|
29506
29629
|
const configData = res.firebaseConfigJson || res.firebase_config_json;
|
|
29507
29630
|
if (!configData) {
|
|
29508
29631
|
strapi2.log.error("Firebase config data missing from database response");
|
|
@@ -29526,6 +29649,8 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29526
29649
|
res.magicLinkUrl = res.magicLinkUrl || magicLinkUrl;
|
|
29527
29650
|
res.magicLinkEmailSubject = res.magicLinkEmailSubject || magicLinkEmailSubject;
|
|
29528
29651
|
res.magicLinkExpiryHours = res.magicLinkExpiryHours || magicLinkExpiryHours;
|
|
29652
|
+
res.emailVerificationUrl = res.emailVerificationUrl || emailVerificationUrl;
|
|
29653
|
+
res.emailVerificationEmailSubject = res.emailVerificationEmailSubject || emailVerificationEmailSubject;
|
|
29529
29654
|
return res;
|
|
29530
29655
|
} catch (error2) {
|
|
29531
29656
|
strapi2.log.error("=== FIREBASE CONFIG SAVE ERROR ===");
|
|
@@ -30185,6 +30310,40 @@ const userService = ({ strapi: strapi2 }) => {
|
|
|
30185
30310
|
}
|
|
30186
30311
|
throw new ApplicationError$2(e.message?.toString() || "Failed to reset password");
|
|
30187
30312
|
}
|
|
30313
|
+
},
|
|
30314
|
+
/**
|
|
30315
|
+
* Send email verification email (admin-initiated)
|
|
30316
|
+
* @param entityId - Firebase UID of the user
|
|
30317
|
+
*/
|
|
30318
|
+
sendVerificationEmail: async (entityId) => {
|
|
30319
|
+
try {
|
|
30320
|
+
ensureFirebaseInitialized();
|
|
30321
|
+
const user = await strapi2.firebase.auth().getUser(entityId);
|
|
30322
|
+
if (!user.email) {
|
|
30323
|
+
throw new ApplicationError$2("User does not have an email address");
|
|
30324
|
+
}
|
|
30325
|
+
if (user.emailVerified) {
|
|
30326
|
+
return { success: true, message: "Email is already verified" };
|
|
30327
|
+
}
|
|
30328
|
+
const config2 = await strapi2.db.query("plugin::firebase-authentication.firebase-authentication-configuration").findOne({ where: {} });
|
|
30329
|
+
const emailVerificationUrl = config2?.emailVerificationUrl;
|
|
30330
|
+
if (!emailVerificationUrl) {
|
|
30331
|
+
throw new ApplicationError$2("Email verification URL is not configured");
|
|
30332
|
+
}
|
|
30333
|
+
const firebaseUserData2 = await strapi2.plugin("firebase-authentication").service("firebaseUserDataService").getByFirebaseUID(entityId);
|
|
30334
|
+
if (!firebaseUserData2) {
|
|
30335
|
+
throw new ApplicationError$2("User is not linked to Firebase authentication");
|
|
30336
|
+
}
|
|
30337
|
+
const tokenService2 = strapi2.plugin("firebase-authentication").service("tokenService");
|
|
30338
|
+
const token = await tokenService2.generateVerificationToken(firebaseUserData2.documentId, user.email);
|
|
30339
|
+
const verificationLink = `${emailVerificationUrl}?token=${token}`;
|
|
30340
|
+
strapi2.log.debug(`Generated email verification link for user ${user.email}`);
|
|
30341
|
+
const emailService2 = strapi2.plugin("firebase-authentication").service("emailService");
|
|
30342
|
+
return await emailService2.sendVerificationEmail(user, verificationLink);
|
|
30343
|
+
} catch (e) {
|
|
30344
|
+
strapi2.log.error(`sendVerificationEmail error: ${e.message}`);
|
|
30345
|
+
throw new ApplicationError$2(e.message?.toString() || "Failed to send verification email");
|
|
30346
|
+
}
|
|
30188
30347
|
}
|
|
30189
30348
|
};
|
|
30190
30349
|
};
|
|
@@ -30728,20 +30887,17 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30728
30887
|
* 2. User-initiated password change (when already authenticated)
|
|
30729
30888
|
*
|
|
30730
30889
|
* NOT used for forgot password email flow - that now uses Firebase's hosted UI
|
|
30890
|
+
*
|
|
30891
|
+
* @param password - New password to set
|
|
30892
|
+
* @param user - Authenticated user from ctx.state.user (populated by is-authenticated policy)
|
|
30893
|
+
* @param populate - Fields to populate in response
|
|
30731
30894
|
*/
|
|
30732
|
-
resetPassword: async (password,
|
|
30895
|
+
resetPassword: async (password, user, populate2) => {
|
|
30733
30896
|
if (!password) {
|
|
30734
30897
|
throw new ValidationError$1("Password is required");
|
|
30735
30898
|
}
|
|
30736
|
-
if (!
|
|
30737
|
-
throw new UnauthorizedError("
|
|
30738
|
-
}
|
|
30739
|
-
let decoded;
|
|
30740
|
-
try {
|
|
30741
|
-
const jwtService = strapi2.plugin("users-permissions").service("jwt");
|
|
30742
|
-
decoded = await jwtService.verify(token);
|
|
30743
|
-
} catch (error2) {
|
|
30744
|
-
throw new UnauthorizedError("Invalid or expired token");
|
|
30899
|
+
if (!user || !user.id) {
|
|
30900
|
+
throw new UnauthorizedError("Authentication required");
|
|
30745
30901
|
}
|
|
30746
30902
|
const config2 = await strapi2.plugin("firebase-authentication").service("settingsService").getFirebaseConfigJson();
|
|
30747
30903
|
const passwordRegex = config2?.passwordRequirementsRegex || "^.{6,}$";
|
|
@@ -30752,8 +30908,7 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30752
30908
|
}
|
|
30753
30909
|
try {
|
|
30754
30910
|
const strapiUser = await strapi2.db.query("plugin::users-permissions.user").findOne({
|
|
30755
|
-
where: { id:
|
|
30756
|
-
// Use numeric id from JWT
|
|
30911
|
+
where: { id: user.id }
|
|
30757
30912
|
});
|
|
30758
30913
|
if (!strapiUser) {
|
|
30759
30914
|
throw new NotFoundError("User not found");
|
|
@@ -30864,6 +31019,159 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30864
31019
|
verificationUrl: magicLinkUrl
|
|
30865
31020
|
};
|
|
30866
31021
|
}
|
|
31022
|
+
},
|
|
31023
|
+
/**
|
|
31024
|
+
* Send email verification - public endpoint
|
|
31025
|
+
* Generates a verification token and sends an email to the user
|
|
31026
|
+
* Security: Always returns generic success message to prevent email enumeration
|
|
31027
|
+
*/
|
|
31028
|
+
async sendVerificationEmail(email2) {
|
|
31029
|
+
strapi2.log.info(`[sendVerificationEmail] Starting email verification for: ${email2}`);
|
|
31030
|
+
if (!email2) {
|
|
31031
|
+
throw new ValidationError$1("Email is required");
|
|
31032
|
+
}
|
|
31033
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
31034
|
+
if (!emailRegex.test(email2)) {
|
|
31035
|
+
throw new ValidationError$1("Invalid email format");
|
|
31036
|
+
}
|
|
31037
|
+
const config2 = await strapi2.plugin("firebase-authentication").service("settingsService").getFirebaseConfigJson();
|
|
31038
|
+
const verificationUrl = config2?.emailVerificationUrl;
|
|
31039
|
+
if (!verificationUrl) {
|
|
31040
|
+
throw new ApplicationError$2("Email verification URL is not configured");
|
|
31041
|
+
}
|
|
31042
|
+
if (process.env.NODE_ENV === "production" && !verificationUrl.startsWith("https://")) {
|
|
31043
|
+
throw new ApplicationError$2("Email verification URL must use HTTPS in production");
|
|
31044
|
+
}
|
|
31045
|
+
try {
|
|
31046
|
+
new URL(verificationUrl);
|
|
31047
|
+
} catch (error2) {
|
|
31048
|
+
throw new ApplicationError$2("Email verification URL is not a valid URL format");
|
|
31049
|
+
}
|
|
31050
|
+
try {
|
|
31051
|
+
let firebaseUser;
|
|
31052
|
+
try {
|
|
31053
|
+
firebaseUser = await strapi2.firebase.auth().getUserByEmail(email2);
|
|
31054
|
+
} catch (fbError) {
|
|
31055
|
+
strapi2.log.debug("User not found in Firebase");
|
|
31056
|
+
}
|
|
31057
|
+
if (!firebaseUser) {
|
|
31058
|
+
strapi2.log.warn(`⚠️ [sendVerificationEmail] User not found in Firebase for email: ${email2}`);
|
|
31059
|
+
return { message: "If an account with that email exists, a verification link has been sent." };
|
|
31060
|
+
}
|
|
31061
|
+
if (firebaseUser.emailVerified) {
|
|
31062
|
+
strapi2.log.info(`[sendVerificationEmail] User ${email2} is already verified`);
|
|
31063
|
+
return { message: "Email is already verified." };
|
|
31064
|
+
}
|
|
31065
|
+
const firebaseData = await strapi2.plugin("firebase-authentication").service("firebaseUserDataService").getByFirebaseUID(firebaseUser.uid);
|
|
31066
|
+
if (!firebaseData) {
|
|
31067
|
+
strapi2.log.warn(`⚠️ [sendVerificationEmail] No firebase-user-data record for: ${email2}`);
|
|
31068
|
+
return { message: "If an account with that email exists, a verification link has been sent." };
|
|
31069
|
+
}
|
|
31070
|
+
strapi2.log.info(
|
|
31071
|
+
`✅ [sendVerificationEmail] User found: ${JSON.stringify({
|
|
31072
|
+
firebaseUID: firebaseUser.uid,
|
|
31073
|
+
email: firebaseUser.email,
|
|
31074
|
+
emailVerified: firebaseUser.emailVerified
|
|
31075
|
+
})}`
|
|
31076
|
+
);
|
|
31077
|
+
const tokenService2 = strapi2.plugin("firebase-authentication").service("tokenService");
|
|
31078
|
+
const token = await tokenService2.generateVerificationToken(firebaseData.documentId, email2);
|
|
31079
|
+
const verificationLink = `${verificationUrl}?token=${token}`;
|
|
31080
|
+
strapi2.log.info(`✅ [sendVerificationEmail] Verification link generated for ${email2}`);
|
|
31081
|
+
strapi2.log.info(`[sendVerificationEmail] Attempting to send verification email to: ${email2}`);
|
|
31082
|
+
await strapi2.plugin("firebase-authentication").service("emailService").sendVerificationEmail(firebaseUser, verificationLink);
|
|
31083
|
+
strapi2.log.info(`✅ [sendVerificationEmail] Verification email sent successfully to: ${email2}`);
|
|
31084
|
+
return {
|
|
31085
|
+
message: "If an account with that email exists, a verification link has been sent."
|
|
31086
|
+
};
|
|
31087
|
+
} catch (error2) {
|
|
31088
|
+
strapi2.log.error(
|
|
31089
|
+
`❌ [sendVerificationEmail] ERROR: ${JSON.stringify({
|
|
31090
|
+
email: email2,
|
|
31091
|
+
message: error2.message,
|
|
31092
|
+
code: error2.code,
|
|
31093
|
+
name: error2.name,
|
|
31094
|
+
stack: error2.stack
|
|
31095
|
+
})}`
|
|
31096
|
+
);
|
|
31097
|
+
return {
|
|
31098
|
+
message: "If an account with that email exists, a verification link has been sent."
|
|
31099
|
+
};
|
|
31100
|
+
}
|
|
31101
|
+
},
|
|
31102
|
+
/**
|
|
31103
|
+
* Verify email with token - public endpoint
|
|
31104
|
+
* Validates the token and marks the user's email as verified in Firebase
|
|
31105
|
+
*/
|
|
31106
|
+
async verifyEmail(token) {
|
|
31107
|
+
strapi2.log.info(`[verifyEmail] Starting email verification with token`);
|
|
31108
|
+
if (!token) {
|
|
31109
|
+
throw new ValidationError$1("Verification token is required");
|
|
31110
|
+
}
|
|
31111
|
+
const tokenService2 = strapi2.plugin("firebase-authentication").service("tokenService");
|
|
31112
|
+
const validationResult = await tokenService2.validateVerificationToken(token);
|
|
31113
|
+
if (!validationResult.valid) {
|
|
31114
|
+
strapi2.log.warn(`[verifyEmail] Token validation failed: ${validationResult.error}`);
|
|
31115
|
+
throw new ValidationError$1(validationResult.error || "Invalid verification link");
|
|
31116
|
+
}
|
|
31117
|
+
const { firebaseUID, firebaseUserDataDocumentId, email: tokenEmail } = validationResult;
|
|
31118
|
+
try {
|
|
31119
|
+
const firebaseUser = await strapi2.firebase.auth().getUser(firebaseUID);
|
|
31120
|
+
if (tokenEmail && firebaseUser.email !== tokenEmail) {
|
|
31121
|
+
strapi2.log.warn(
|
|
31122
|
+
`[verifyEmail] Email changed: token email ${tokenEmail} != current email ${firebaseUser.email}`
|
|
31123
|
+
);
|
|
31124
|
+
await tokenService2.invalidateVerificationToken(firebaseUserDataDocumentId);
|
|
31125
|
+
throw new ValidationError$1(
|
|
31126
|
+
"Email address has changed since verification was requested. Please request a new verification link."
|
|
31127
|
+
);
|
|
31128
|
+
}
|
|
31129
|
+
if (firebaseUser.emailVerified) {
|
|
31130
|
+
strapi2.log.info(`[verifyEmail] User ${firebaseUser.email} is already verified`);
|
|
31131
|
+
await tokenService2.invalidateVerificationToken(firebaseUserDataDocumentId);
|
|
31132
|
+
return {
|
|
31133
|
+
success: true,
|
|
31134
|
+
message: "Email is already verified."
|
|
31135
|
+
};
|
|
31136
|
+
}
|
|
31137
|
+
await strapi2.firebase.auth().updateUser(firebaseUID, {
|
|
31138
|
+
emailVerified: true
|
|
31139
|
+
});
|
|
31140
|
+
try {
|
|
31141
|
+
const firebaseUserDataService2 = strapi2.plugin("firebase-authentication").service("firebaseUserDataService");
|
|
31142
|
+
const firebaseUserData2 = await firebaseUserDataService2.getByFirebaseUID(firebaseUID);
|
|
31143
|
+
if (firebaseUserData2?.user?.documentId) {
|
|
31144
|
+
await strapi2.db.query("plugin::users-permissions.user").update({
|
|
31145
|
+
where: { documentId: firebaseUserData2.user.documentId },
|
|
31146
|
+
data: { confirmed: true }
|
|
31147
|
+
});
|
|
31148
|
+
strapi2.log.info(`✅ [verifyEmail] Strapi user confirmed for: ${firebaseUserData2.user.documentId}`);
|
|
31149
|
+
}
|
|
31150
|
+
} catch (strapiUpdateError) {
|
|
31151
|
+
strapi2.log.warn(
|
|
31152
|
+
`[verifyEmail] Failed to update Strapi user confirmed status: ${strapiUpdateError.message}`
|
|
31153
|
+
);
|
|
31154
|
+
}
|
|
31155
|
+
strapi2.log.info(`✅ [verifyEmail] Email verified successfully for: ${firebaseUser.email}`);
|
|
31156
|
+
await tokenService2.invalidateVerificationToken(firebaseUserDataDocumentId);
|
|
31157
|
+
return {
|
|
31158
|
+
success: true,
|
|
31159
|
+
message: "Email verified successfully."
|
|
31160
|
+
};
|
|
31161
|
+
} catch (error2) {
|
|
31162
|
+
strapi2.log.error(
|
|
31163
|
+
`❌ [verifyEmail] ERROR: ${JSON.stringify({
|
|
31164
|
+
firebaseUID,
|
|
31165
|
+
message: error2.message,
|
|
31166
|
+
code: error2.code,
|
|
31167
|
+
name: error2.name
|
|
31168
|
+
})}`
|
|
31169
|
+
);
|
|
31170
|
+
if (error2 instanceof ValidationError$1) {
|
|
31171
|
+
throw error2;
|
|
31172
|
+
}
|
|
31173
|
+
throw new ApplicationError$2("Failed to verify email. Please try again.");
|
|
31174
|
+
}
|
|
30867
31175
|
}
|
|
30868
31176
|
});
|
|
30869
31177
|
const passwordResetTemplate = {
|
|
@@ -31243,13 +31551,144 @@ The <%= appName %> Team
|
|
|
31243
31551
|
Need help? Contact us at <%= supportEmail %>
|
|
31244
31552
|
<% } %>
|
|
31245
31553
|
|
|
31554
|
+
© <%= year %> <%= appName %>. All rights reserved.
|
|
31555
|
+
`.trim()
|
|
31556
|
+
};
|
|
31557
|
+
const emailVerificationTemplate = {
|
|
31558
|
+
subject: "Verify Your Email - <%= appName %>",
|
|
31559
|
+
html: `
|
|
31560
|
+
<!DOCTYPE html>
|
|
31561
|
+
<html lang="en">
|
|
31562
|
+
<head>
|
|
31563
|
+
<meta charset="UTF-8">
|
|
31564
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
31565
|
+
<title>Verify Your Email</title>
|
|
31566
|
+
</head>
|
|
31567
|
+
<body style="margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f4f4f4;">
|
|
31568
|
+
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #f4f4f4;">
|
|
31569
|
+
<tr>
|
|
31570
|
+
<td align="center" style="padding: 40px 0;">
|
|
31571
|
+
<table role="presentation" style="width: 600px; max-width: 100%; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
|
|
31572
|
+
<!-- Header -->
|
|
31573
|
+
<tr>
|
|
31574
|
+
<td style="padding: 40px 40px 20px 40px; text-align: center;">
|
|
31575
|
+
<h1 style="margin: 0; font-size: 28px; color: #333333; font-weight: 600;">
|
|
31576
|
+
Verify Your Email Address
|
|
31577
|
+
</h1>
|
|
31578
|
+
</td>
|
|
31579
|
+
</tr>
|
|
31580
|
+
|
|
31581
|
+
<!-- Content -->
|
|
31582
|
+
<tr>
|
|
31583
|
+
<td style="padding: 20px 40px;">
|
|
31584
|
+
<p style="margin: 0 0 20px 0; font-size: 16px; line-height: 1.6; color: #555555;">
|
|
31585
|
+
Hello<% if (user.firstName) { %> <%= user.firstName %><% } %>,
|
|
31586
|
+
</p>
|
|
31587
|
+
|
|
31588
|
+
<p style="margin: 0 0 20px 0; font-size: 16px; line-height: 1.6; color: #555555;">
|
|
31589
|
+
Thank you for signing up with <strong><%= appName %></strong>! Please verify your email address <strong><%= user.email %></strong> to complete your registration.
|
|
31590
|
+
</p>
|
|
31591
|
+
|
|
31592
|
+
<p style="margin: 0 0 30px 0; font-size: 16px; line-height: 1.6; color: #555555;">
|
|
31593
|
+
Click the button below to verify your email:
|
|
31594
|
+
</p>
|
|
31595
|
+
|
|
31596
|
+
<!-- CTA Button -->
|
|
31597
|
+
<table role="presentation" style="width: 100%; border-collapse: collapse;">
|
|
31598
|
+
<tr>
|
|
31599
|
+
<td align="center" style="padding: 0 0 30px 0;">
|
|
31600
|
+
<a href="<%= verificationLink %>"
|
|
31601
|
+
style="display: inline-block; padding: 14px 32px; background-color: #28a745; color: #ffffff; text-decoration: none; font-size: 16px; font-weight: 600; border-radius: 5px; transition: background-color 0.3s;">
|
|
31602
|
+
Verify Email Address
|
|
31603
|
+
</a>
|
|
31604
|
+
</td>
|
|
31605
|
+
</tr>
|
|
31606
|
+
</table>
|
|
31607
|
+
|
|
31608
|
+
<p style="margin: 0 0 20px 0; font-size: 14px; line-height: 1.6; color: #777777;">
|
|
31609
|
+
Or copy and paste this link into your browser:
|
|
31610
|
+
</p>
|
|
31611
|
+
|
|
31612
|
+
<p style="margin: 0 0 30px 0; font-size: 14px; line-height: 1.6; word-break: break-all; color: #28a745;">
|
|
31613
|
+
<%= verificationLink %>
|
|
31614
|
+
</p>
|
|
31615
|
+
|
|
31616
|
+
<!-- Security Notice -->
|
|
31617
|
+
<table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #fff3cd; border-radius: 4px; margin: 0 0 20px 0;">
|
|
31618
|
+
<tr>
|
|
31619
|
+
<td style="padding: 12px 16px;">
|
|
31620
|
+
<p style="margin: 0; font-size: 14px; line-height: 1.6; color: #856404;">
|
|
31621
|
+
<strong>⚠️ Important:</strong> This link will expire in <strong><%= expiresIn %></strong>.
|
|
31622
|
+
If you didn't create an account with us, you can safely ignore this email.
|
|
31623
|
+
</p>
|
|
31624
|
+
</td>
|
|
31625
|
+
</tr>
|
|
31626
|
+
</table>
|
|
31627
|
+
|
|
31628
|
+
<p style="margin: 0 0 20px 0; font-size: 16px; line-height: 1.6; color: #555555;">
|
|
31629
|
+
For security reasons, please do not share this link with anyone.
|
|
31630
|
+
</p>
|
|
31631
|
+
</td>
|
|
31632
|
+
</tr>
|
|
31633
|
+
|
|
31634
|
+
<!-- Footer -->
|
|
31635
|
+
<tr>
|
|
31636
|
+
<td style="padding: 30px 40px 40px 40px; border-top: 1px solid #eeeeee;">
|
|
31637
|
+
<p style="margin: 0 0 10px 0; font-size: 14px; line-height: 1.6; color: #999999; text-align: center;">
|
|
31638
|
+
Best regards,<br>
|
|
31639
|
+
The <%= appName %> Team
|
|
31640
|
+
</p>
|
|
31641
|
+
|
|
31642
|
+
<% if (supportEmail) { %>
|
|
31643
|
+
<p style="margin: 0 0 10px 0; font-size: 12px; line-height: 1.6; color: #999999; text-align: center;">
|
|
31644
|
+
Need help? Contact us at <a href="mailto:<%= supportEmail %>" style="color: #28a745; text-decoration: none;"><%= supportEmail %></a>
|
|
31645
|
+
</p>
|
|
31646
|
+
<% } %>
|
|
31647
|
+
|
|
31648
|
+
<p style="margin: 0; font-size: 12px; line-height: 1.6; color: #999999; text-align: center;">
|
|
31649
|
+
© <%= year %> <%= appName %>. All rights reserved.
|
|
31650
|
+
</p>
|
|
31651
|
+
</td>
|
|
31652
|
+
</tr>
|
|
31653
|
+
</table>
|
|
31654
|
+
</td>
|
|
31655
|
+
</tr>
|
|
31656
|
+
</table>
|
|
31657
|
+
</body>
|
|
31658
|
+
</html>
|
|
31659
|
+
`.trim(),
|
|
31660
|
+
text: `
|
|
31661
|
+
Verify Your Email Address
|
|
31662
|
+
|
|
31663
|
+
Hello<% if (user.firstName) { %> <%= user.firstName %><% } %>,
|
|
31664
|
+
|
|
31665
|
+
Thank you for signing up with <%= appName %>! Please verify your email address <%= user.email %> to complete your registration.
|
|
31666
|
+
|
|
31667
|
+
To verify your email, please visit the following link:
|
|
31668
|
+
|
|
31669
|
+
<%= verificationLink %>
|
|
31670
|
+
|
|
31671
|
+
This link will expire in <%= expiresIn %>.
|
|
31672
|
+
|
|
31673
|
+
If you didn't create an account with us, you can safely ignore this email.
|
|
31674
|
+
|
|
31675
|
+
For security reasons, please do not share this link with anyone.
|
|
31676
|
+
|
|
31677
|
+
Best regards,
|
|
31678
|
+
The <%= appName %> Team
|
|
31679
|
+
|
|
31680
|
+
<% if (supportEmail) { %>
|
|
31681
|
+
Need help? Contact us at <%= supportEmail %>
|
|
31682
|
+
<% } %>
|
|
31683
|
+
|
|
31246
31684
|
© <%= year %> <%= appName %>. All rights reserved.
|
|
31247
31685
|
`.trim()
|
|
31248
31686
|
};
|
|
31249
31687
|
const defaultTemplates = {
|
|
31250
31688
|
passwordReset: passwordResetTemplate,
|
|
31251
31689
|
magicLink: magicLinkTemplate,
|
|
31252
|
-
passwordChanged: passwordChangedTemplate
|
|
31690
|
+
passwordChanged: passwordChangedTemplate,
|
|
31691
|
+
emailVerification: emailVerificationTemplate
|
|
31253
31692
|
};
|
|
31254
31693
|
class TemplateService {
|
|
31255
31694
|
/**
|
|
@@ -31661,6 +32100,114 @@ class EmailService {
|
|
|
31661
32100
|
message: "Password changed but confirmation email could not be sent"
|
|
31662
32101
|
};
|
|
31663
32102
|
}
|
|
32103
|
+
/**
|
|
32104
|
+
* Send email verification email with three-tier fallback system
|
|
32105
|
+
* Tier 1: Strapi Email Plugin (if configured)
|
|
32106
|
+
* Tier 2: Custom Hook Function (if provided in config)
|
|
32107
|
+
* Tier 3: Development Console Logging (dev mode only)
|
|
32108
|
+
*/
|
|
32109
|
+
async sendVerificationEmail(user, verificationLink) {
|
|
32110
|
+
if (!user.email) {
|
|
32111
|
+
throw new ValidationError$1("User does not have an email address");
|
|
32112
|
+
}
|
|
32113
|
+
const variables = {
|
|
32114
|
+
user: {
|
|
32115
|
+
email: user.email,
|
|
32116
|
+
firstName: user.firstName || user.displayName?.split(" ")[0],
|
|
32117
|
+
lastName: user.lastName,
|
|
32118
|
+
displayName: user.displayName,
|
|
32119
|
+
phoneNumber: user.phoneNumber,
|
|
32120
|
+
uid: user.uid
|
|
32121
|
+
},
|
|
32122
|
+
verificationLink,
|
|
32123
|
+
expiresIn: "1 hour"
|
|
32124
|
+
};
|
|
32125
|
+
const settingsService2 = strapi.plugin("firebase-authentication").service("settingsService");
|
|
32126
|
+
const dbConfig = await settingsService2.getFirebaseConfigJson();
|
|
32127
|
+
const customSubject = dbConfig?.emailVerificationEmailSubject;
|
|
32128
|
+
const pluginConfig = strapi.config.get("plugin::firebase-authentication");
|
|
32129
|
+
const appConfig = pluginConfig?.app || {};
|
|
32130
|
+
const completeVariables = {
|
|
32131
|
+
...variables,
|
|
32132
|
+
appName: appConfig?.name || "Your Application",
|
|
32133
|
+
appUrl: appConfig?.url || process.env.PUBLIC_URL || "http://localhost:3000",
|
|
32134
|
+
supportEmail: appConfig?.supportEmail,
|
|
32135
|
+
year: (/* @__PURE__ */ new Date()).getFullYear(),
|
|
32136
|
+
expiresIn: variables.expiresIn
|
|
32137
|
+
};
|
|
32138
|
+
const templateService2 = strapi.plugin("firebase-authentication").service("templateService");
|
|
32139
|
+
const template = await templateService2.getTemplate("emailVerification");
|
|
32140
|
+
const subjectTemplate = customSubject || template.subject;
|
|
32141
|
+
const compiledSubject = _$1.template(subjectTemplate)(completeVariables);
|
|
32142
|
+
try {
|
|
32143
|
+
const compiledHtml = template.html ? _$1.template(template.html)(completeVariables) : void 0;
|
|
32144
|
+
const compiledText = template.text ? _$1.template(template.text)(completeVariables) : void 0;
|
|
32145
|
+
const emailPlugin = strapi.plugin("email");
|
|
32146
|
+
if (!emailPlugin) {
|
|
32147
|
+
throw new Error("Email plugin not found");
|
|
32148
|
+
}
|
|
32149
|
+
const emailService2 = emailPlugin.service("email");
|
|
32150
|
+
await emailService2.send({
|
|
32151
|
+
to: user.email,
|
|
32152
|
+
subject: compiledSubject,
|
|
32153
|
+
html: compiledHtml,
|
|
32154
|
+
text: compiledText
|
|
32155
|
+
});
|
|
32156
|
+
strapi.log.info(`✅ Email verification sent via Strapi email plugin to ${user.email}`);
|
|
32157
|
+
return {
|
|
32158
|
+
success: true,
|
|
32159
|
+
message: `Verification email sent to ${user.email}`
|
|
32160
|
+
};
|
|
32161
|
+
} catch (tier1Error) {
|
|
32162
|
+
strapi.log.debug(`Strapi email plugin failed: ${tier1Error.message}. Trying fallback options...`);
|
|
32163
|
+
}
|
|
32164
|
+
const customSender = pluginConfig?.sendVerificationEmail;
|
|
32165
|
+
if (customSender && typeof customSender === "function") {
|
|
32166
|
+
try {
|
|
32167
|
+
const compiledHtml = template.html ? _$1.template(template.html)(completeVariables) : void 0;
|
|
32168
|
+
const compiledText = template.text ? _$1.template(template.text)(completeVariables) : void 0;
|
|
32169
|
+
await customSender({
|
|
32170
|
+
to: user.email,
|
|
32171
|
+
subject: compiledSubject,
|
|
32172
|
+
html: compiledHtml,
|
|
32173
|
+
text: compiledText,
|
|
32174
|
+
verificationLink,
|
|
32175
|
+
variables: completeVariables
|
|
32176
|
+
});
|
|
32177
|
+
strapi.log.info(`✅ Email verification sent via custom hook to ${user.email}`);
|
|
32178
|
+
return {
|
|
32179
|
+
success: true,
|
|
32180
|
+
message: `Verification email sent to ${user.email}`
|
|
32181
|
+
};
|
|
32182
|
+
} catch (tier2Error) {
|
|
32183
|
+
strapi.log.error(`Custom hook failed: ${tier2Error.message}. Continuing to next fallback...`);
|
|
32184
|
+
}
|
|
32185
|
+
}
|
|
32186
|
+
if (process.env.NODE_ENV !== "production") {
|
|
32187
|
+
try {
|
|
32188
|
+
strapi.log.info("\n" + "=".repeat(80));
|
|
32189
|
+
strapi.log.info("EMAIL VERIFICATION (Development Mode)");
|
|
32190
|
+
strapi.log.info("=".repeat(80));
|
|
32191
|
+
strapi.log.info(`To: ${user.email}`);
|
|
32192
|
+
strapi.log.info(`Subject: ${compiledSubject}`);
|
|
32193
|
+
strapi.log.info(`Verification Link: ${verificationLink}`);
|
|
32194
|
+
strapi.log.info(`Expires In: 1 hour`);
|
|
32195
|
+
strapi.log.info("=".repeat(80));
|
|
32196
|
+
strapi.log.info("Note: Email not sent - no email service configured");
|
|
32197
|
+
strapi.log.info("Copy the link above and open in your browser to verify");
|
|
32198
|
+
strapi.log.info("=".repeat(80) + "\n");
|
|
32199
|
+
return {
|
|
32200
|
+
success: true,
|
|
32201
|
+
message: "Verification link logged to console (development mode)"
|
|
32202
|
+
};
|
|
32203
|
+
} catch (tier3Error) {
|
|
32204
|
+
strapi.log.error(`Development fallback failed: ${tier3Error.message}`);
|
|
32205
|
+
}
|
|
32206
|
+
}
|
|
32207
|
+
throw new ApplicationError$2(
|
|
32208
|
+
"Email service is not configured. Please configure Strapi email plugin or provide custom sendVerificationEmail function in plugin config."
|
|
32209
|
+
);
|
|
32210
|
+
}
|
|
31664
32211
|
}
|
|
31665
32212
|
const emailService = ({ strapi: strapi2 }) => new EmailService();
|
|
31666
32213
|
const firebaseUserDataService = ({ strapi: strapi2 }) => ({
|
|
@@ -32023,7 +32570,7 @@ var TokenExpiredError_1 = TokenExpiredError$1;
|
|
|
32023
32570
|
var jws$3 = {};
|
|
32024
32571
|
var safeBuffer = { exports: {} };
|
|
32025
32572
|
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
|
32026
|
-
(function(module, exports) {
|
|
32573
|
+
(function(module, exports$1) {
|
|
32027
32574
|
var buffer = require$$0$7;
|
|
32028
32575
|
var Buffer2 = buffer.Buffer;
|
|
32029
32576
|
function copyProps(src, dst) {
|
|
@@ -32034,8 +32581,8 @@ var safeBuffer = { exports: {} };
|
|
|
32034
32581
|
if (Buffer2.from && Buffer2.alloc && Buffer2.allocUnsafe && Buffer2.allocUnsafeSlow) {
|
|
32035
32582
|
module.exports = buffer;
|
|
32036
32583
|
} else {
|
|
32037
|
-
copyProps(buffer, exports);
|
|
32038
|
-
exports.Buffer = SafeBuffer;
|
|
32584
|
+
copyProps(buffer, exports$1);
|
|
32585
|
+
exports$1.Buffer = SafeBuffer;
|
|
32039
32586
|
}
|
|
32040
32587
|
function SafeBuffer(arg, encodingOrOffset, length) {
|
|
32041
32588
|
return Buffer2(arg, encodingOrOffset, length);
|
|
@@ -32550,7 +33097,12 @@ function jwsSign(opts) {
|
|
|
32550
33097
|
return util$1.format("%s.%s", securedInput, signature);
|
|
32551
33098
|
}
|
|
32552
33099
|
function SignStream$1(opts) {
|
|
32553
|
-
var secret = opts.secret
|
|
33100
|
+
var secret = opts.secret;
|
|
33101
|
+
secret = secret == null ? opts.privateKey : secret;
|
|
33102
|
+
secret = secret == null ? opts.key : secret;
|
|
33103
|
+
if (/^hs/i.test(opts.header.alg) === true && secret == null) {
|
|
33104
|
+
throw new TypeError("secret must be a string or buffer or a KeyObject");
|
|
33105
|
+
}
|
|
32554
33106
|
var secretStream = new DataStream$1(secret);
|
|
32555
33107
|
this.readable = true;
|
|
32556
33108
|
this.header = opts.header;
|
|
@@ -32656,7 +33208,12 @@ function jwsDecode(jwsSig, opts) {
|
|
|
32656
33208
|
}
|
|
32657
33209
|
function VerifyStream$1(opts) {
|
|
32658
33210
|
opts = opts || {};
|
|
32659
|
-
var secretOrKey = opts.secret
|
|
33211
|
+
var secretOrKey = opts.secret;
|
|
33212
|
+
secretOrKey = secretOrKey == null ? opts.publicKey : secretOrKey;
|
|
33213
|
+
secretOrKey = secretOrKey == null ? opts.key : secretOrKey;
|
|
33214
|
+
if (/^hs/i.test(opts.algorithm) === true && secretOrKey == null) {
|
|
33215
|
+
throw new TypeError("secret must be a string or buffer or a KeyObject");
|
|
33216
|
+
}
|
|
32660
33217
|
var secretStream = new DataStream(secretOrKey);
|
|
32661
33218
|
this.readable = true;
|
|
32662
33219
|
this.algorithm = opts.algorithm;
|
|
@@ -32899,19 +33456,19 @@ var constants$1 = {
|
|
|
32899
33456
|
const debug$1 = typeof process === "object" && process.env && process.env.NODE_DEBUG && /\bsemver\b/i.test(process.env.NODE_DEBUG) ? (...args) => console.error("SEMVER", ...args) : () => {
|
|
32900
33457
|
};
|
|
32901
33458
|
var debug_1 = debug$1;
|
|
32902
|
-
(function(module, exports) {
|
|
33459
|
+
(function(module, exports$1) {
|
|
32903
33460
|
const {
|
|
32904
33461
|
MAX_SAFE_COMPONENT_LENGTH: MAX_SAFE_COMPONENT_LENGTH2,
|
|
32905
33462
|
MAX_SAFE_BUILD_LENGTH: MAX_SAFE_BUILD_LENGTH2,
|
|
32906
33463
|
MAX_LENGTH: MAX_LENGTH2
|
|
32907
33464
|
} = constants$1;
|
|
32908
33465
|
const debug2 = debug_1;
|
|
32909
|
-
exports = module.exports = {};
|
|
32910
|
-
const re2 = exports.re = [];
|
|
32911
|
-
const safeRe = exports.safeRe = [];
|
|
32912
|
-
const src = exports.src = [];
|
|
32913
|
-
const safeSrc = exports.safeSrc = [];
|
|
32914
|
-
const t2 = exports.t = {};
|
|
33466
|
+
exports$1 = module.exports = {};
|
|
33467
|
+
const re2 = exports$1.re = [];
|
|
33468
|
+
const safeRe = exports$1.safeRe = [];
|
|
33469
|
+
const src = exports$1.src = [];
|
|
33470
|
+
const safeSrc = exports$1.safeSrc = [];
|
|
33471
|
+
const t2 = exports$1.t = {};
|
|
32915
33472
|
let R = 0;
|
|
32916
33473
|
const LETTERDASHNUMBER = "[a-zA-Z0-9-]";
|
|
32917
33474
|
const safeRegexReplacements = [
|
|
@@ -32964,18 +33521,18 @@ var debug_1 = debug$1;
|
|
|
32964
33521
|
createToken("COERCERTLFULL", src[t2.COERCEFULL], true);
|
|
32965
33522
|
createToken("LONETILDE", "(?:~>?)");
|
|
32966
33523
|
createToken("TILDETRIM", `(\\s*)${src[t2.LONETILDE]}\\s+`, true);
|
|
32967
|
-
exports.tildeTrimReplace = "$1~";
|
|
33524
|
+
exports$1.tildeTrimReplace = "$1~";
|
|
32968
33525
|
createToken("TILDE", `^${src[t2.LONETILDE]}${src[t2.XRANGEPLAIN]}$`);
|
|
32969
33526
|
createToken("TILDELOOSE", `^${src[t2.LONETILDE]}${src[t2.XRANGEPLAINLOOSE]}$`);
|
|
32970
33527
|
createToken("LONECARET", "(?:\\^)");
|
|
32971
33528
|
createToken("CARETTRIM", `(\\s*)${src[t2.LONECARET]}\\s+`, true);
|
|
32972
|
-
exports.caretTrimReplace = "$1^";
|
|
33529
|
+
exports$1.caretTrimReplace = "$1^";
|
|
32973
33530
|
createToken("CARET", `^${src[t2.LONECARET]}${src[t2.XRANGEPLAIN]}$`);
|
|
32974
33531
|
createToken("CARETLOOSE", `^${src[t2.LONECARET]}${src[t2.XRANGEPLAINLOOSE]}$`);
|
|
32975
33532
|
createToken("COMPARATORLOOSE", `^${src[t2.GTLT]}\\s*(${src[t2.LOOSEPLAIN]})$|^$`);
|
|
32976
33533
|
createToken("COMPARATOR", `^${src[t2.GTLT]}\\s*(${src[t2.FULLPLAIN]})$|^$`);
|
|
32977
33534
|
createToken("COMPARATORTRIM", `(\\s*)${src[t2.GTLT]}\\s*(${src[t2.LOOSEPLAIN]}|${src[t2.XRANGEPLAIN]})`, true);
|
|
32978
|
-
exports.comparatorTrimReplace = "$1$2$3";
|
|
33535
|
+
exports$1.comparatorTrimReplace = "$1$2$3";
|
|
32979
33536
|
createToken("HYPHENRANGE", `^\\s*(${src[t2.XRANGEPLAIN]})\\s+-\\s+(${src[t2.XRANGEPLAIN]})\\s*$`);
|
|
32980
33537
|
createToken("HYPHENRANGELOOSE", `^\\s*(${src[t2.XRANGEPLAINLOOSE]})\\s+-\\s+(${src[t2.XRANGEPLAINLOOSE]})\\s*$`);
|
|
32981
33538
|
createToken("STAR", "(<|>)?=?\\s*\\*");
|
|
@@ -35114,6 +35671,125 @@ const tokenService = ({ strapi: strapi2 }) => {
|
|
|
35114
35671
|
}
|
|
35115
35672
|
});
|
|
35116
35673
|
strapi2.log.debug(`Invalidated reset token for user ${firebaseUserDataDocumentId}`);
|
|
35674
|
+
},
|
|
35675
|
+
// ==================== EMAIL VERIFICATION TOKENS ====================
|
|
35676
|
+
/**
|
|
35677
|
+
* Generate an email verification token for a user
|
|
35678
|
+
* @param firebaseUserDataDocumentId - The documentId of the firebase-user-data record
|
|
35679
|
+
* @param email - The email address at time of request (for change detection)
|
|
35680
|
+
* @returns The JWT token to include in the verification URL
|
|
35681
|
+
*/
|
|
35682
|
+
async generateVerificationToken(firebaseUserDataDocumentId, email2) {
|
|
35683
|
+
const signingKey = getSigningKey();
|
|
35684
|
+
const jti = crypto$1.randomBytes(32).toString("hex");
|
|
35685
|
+
const payload = {
|
|
35686
|
+
sub: firebaseUserDataDocumentId,
|
|
35687
|
+
purpose: "email-verification",
|
|
35688
|
+
email: email2,
|
|
35689
|
+
jti
|
|
35690
|
+
};
|
|
35691
|
+
const token = jwt.sign(payload, signingKey, {
|
|
35692
|
+
expiresIn: "1h"
|
|
35693
|
+
});
|
|
35694
|
+
const tokenHash = crypto$1.createHash("sha256").update(jti).digest("hex");
|
|
35695
|
+
const expiresAt = new Date(Date.now() + 60 * 60 * 1e3);
|
|
35696
|
+
await strapi2.db.query(FIREBASE_USER_DATA_CONTENT_TYPE).update({
|
|
35697
|
+
where: { documentId: firebaseUserDataDocumentId },
|
|
35698
|
+
data: {
|
|
35699
|
+
verificationTokenHash: tokenHash,
|
|
35700
|
+
verificationTokenExpiresAt: expiresAt.toISOString()
|
|
35701
|
+
}
|
|
35702
|
+
});
|
|
35703
|
+
strapi2.log.debug(`Generated verification token for user ${firebaseUserDataDocumentId}`);
|
|
35704
|
+
return token;
|
|
35705
|
+
},
|
|
35706
|
+
/**
|
|
35707
|
+
* Validate an email verification token
|
|
35708
|
+
* @param token - The JWT token from the verification URL
|
|
35709
|
+
* @returns Validation result with user info and email if valid
|
|
35710
|
+
*/
|
|
35711
|
+
async validateVerificationToken(token) {
|
|
35712
|
+
const signingKey = getSigningKey();
|
|
35713
|
+
try {
|
|
35714
|
+
const decoded = jwt.verify(token, signingKey);
|
|
35715
|
+
if (decoded.purpose !== "email-verification") {
|
|
35716
|
+
return {
|
|
35717
|
+
valid: false,
|
|
35718
|
+
firebaseUserDataDocumentId: "",
|
|
35719
|
+
firebaseUID: "",
|
|
35720
|
+
error: "Invalid token purpose"
|
|
35721
|
+
};
|
|
35722
|
+
}
|
|
35723
|
+
const tokenHash = crypto$1.createHash("sha256").update(decoded.jti).digest("hex");
|
|
35724
|
+
const firebaseUserData2 = await strapi2.db.query(FIREBASE_USER_DATA_CONTENT_TYPE).findOne({
|
|
35725
|
+
where: { documentId: decoded.sub }
|
|
35726
|
+
});
|
|
35727
|
+
if (!firebaseUserData2) {
|
|
35728
|
+
return {
|
|
35729
|
+
valid: false,
|
|
35730
|
+
firebaseUserDataDocumentId: "",
|
|
35731
|
+
firebaseUID: "",
|
|
35732
|
+
error: "User not found"
|
|
35733
|
+
};
|
|
35734
|
+
}
|
|
35735
|
+
if (firebaseUserData2.verificationTokenHash !== tokenHash) {
|
|
35736
|
+
return {
|
|
35737
|
+
valid: false,
|
|
35738
|
+
firebaseUserDataDocumentId: "",
|
|
35739
|
+
firebaseUID: "",
|
|
35740
|
+
error: "Verification link has already been used or is invalid"
|
|
35741
|
+
};
|
|
35742
|
+
}
|
|
35743
|
+
if (firebaseUserData2.verificationTokenExpiresAt) {
|
|
35744
|
+
const expiresAt = new Date(firebaseUserData2.verificationTokenExpiresAt);
|
|
35745
|
+
if (expiresAt < /* @__PURE__ */ new Date()) {
|
|
35746
|
+
return {
|
|
35747
|
+
valid: false,
|
|
35748
|
+
firebaseUserDataDocumentId: "",
|
|
35749
|
+
firebaseUID: "",
|
|
35750
|
+
error: "Verification link has expired"
|
|
35751
|
+
};
|
|
35752
|
+
}
|
|
35753
|
+
}
|
|
35754
|
+
return {
|
|
35755
|
+
valid: true,
|
|
35756
|
+
firebaseUserDataDocumentId: firebaseUserData2.documentId,
|
|
35757
|
+
firebaseUID: firebaseUserData2.firebaseUserID,
|
|
35758
|
+
email: decoded.email
|
|
35759
|
+
};
|
|
35760
|
+
} catch (error2) {
|
|
35761
|
+
if (error2.name === "TokenExpiredError") {
|
|
35762
|
+
return {
|
|
35763
|
+
valid: false,
|
|
35764
|
+
firebaseUserDataDocumentId: "",
|
|
35765
|
+
firebaseUID: "",
|
|
35766
|
+
error: "Verification link has expired"
|
|
35767
|
+
};
|
|
35768
|
+
}
|
|
35769
|
+
if (error2.name === "JsonWebTokenError") {
|
|
35770
|
+
return {
|
|
35771
|
+
valid: false,
|
|
35772
|
+
firebaseUserDataDocumentId: "",
|
|
35773
|
+
firebaseUID: "",
|
|
35774
|
+
error: "Invalid verification link"
|
|
35775
|
+
};
|
|
35776
|
+
}
|
|
35777
|
+
throw error2;
|
|
35778
|
+
}
|
|
35779
|
+
},
|
|
35780
|
+
/**
|
|
35781
|
+
* Invalidate a verification token after use
|
|
35782
|
+
* @param firebaseUserDataDocumentId - The documentId of the firebase-user-data record
|
|
35783
|
+
*/
|
|
35784
|
+
async invalidateVerificationToken(firebaseUserDataDocumentId) {
|
|
35785
|
+
await strapi2.db.query(FIREBASE_USER_DATA_CONTENT_TYPE).update({
|
|
35786
|
+
where: { documentId: firebaseUserDataDocumentId },
|
|
35787
|
+
data: {
|
|
35788
|
+
verificationTokenHash: null,
|
|
35789
|
+
verificationTokenExpiresAt: null
|
|
35790
|
+
}
|
|
35791
|
+
});
|
|
35792
|
+
strapi2.log.debug(`Invalidated verification token for user ${firebaseUserDataDocumentId}`);
|
|
35117
35793
|
}
|
|
35118
35794
|
};
|
|
35119
35795
|
};
|