strapi-plugin-firebase-authentication 1.1.7 → 1.1.9

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.
@@ -199,8 +199,8 @@ const bootstrap = async ({ strapi: strapi2 }) => {
199
199
  }
200
200
  } catch (error2) {
201
201
  strapi2.log.error("Firebase initialization failed during bootstrap:");
202
- strapi2.log.error(" Error:", error2.message);
203
- strapi2.log.error(" Stack:", error2.stack);
202
+ strapi2.log.error(` Error: ${error2.message}`);
203
+ strapi2.log.error(` Stack: ${error2.stack}`);
204
204
  }
205
205
  await strapi2.admin.services.permission.actionProvider.registerMany(actions);
206
206
  if (strapi2.plugin("users-permissions")) {
@@ -210,14 +210,14 @@ const bootstrap = async ({ strapi: strapi2 }) => {
210
210
  if (process.env.RUN_FIREBASE_MIGRATION === "true") {
211
211
  const dryRun = process.env.DRY_RUN === "true";
212
212
  strapi2.log.info("");
213
- strapi2.log.info("🚀 Firebase migration triggered by RUN_FIREBASE_MIGRATION env variable");
213
+ strapi2.log.info("Firebase migration triggered by RUN_FIREBASE_MIGRATION env variable");
214
214
  await migrateFirebaseUserData(strapi2, dryRun);
215
215
  }
216
216
  setImmediate(async () => {
217
217
  try {
218
218
  await strapi2.plugin("firebase-authentication").service("autoLinkService").linkAllUsers(strapi2);
219
219
  } catch (error2) {
220
- strapi2.log.error("Auto-linking failed:", error2);
220
+ strapi2.log.error(`Auto-linking failed: ${error2.message}`);
221
221
  }
222
222
  });
223
223
  };
@@ -442,7 +442,7 @@ const firebaseController = {
442
442
  const result = await strapi.plugin(pluginName).service("firebaseService").validateFirebaseToken(idToken, profileMetaData, populate2);
443
443
  ctx.body = result;
444
444
  } catch (error2) {
445
- strapi.log.error("validateToken controller error:", error2.message);
445
+ strapi.log.error(`validateToken controller error: ${error2.message}`);
446
446
  if (error2.name === "ValidationError") {
447
447
  return ctx.badRequest(error2.message);
448
448
  }
@@ -452,9 +452,6 @@ const firebaseController = {
452
452
  throw error2;
453
453
  }
454
454
  },
455
- async createAlias(ctx) {
456
- ctx.body = await strapi.plugin(pluginName).service("firebaseService").createAlias(ctx);
457
- },
458
455
  async deleteByEmail(email2) {
459
456
  const user = await strapi.firebase.auth().getUserByEmail(email2);
460
457
  await strapi.plugin(pluginName).service("firebaseService").delete(user.toJSON().uid);
@@ -518,7 +515,8 @@ const firebaseController = {
518
515
  async forgotPassword(ctx) {
519
516
  strapi.log.debug("forgotPassword endpoint called");
520
517
  try {
521
- ctx.body = await strapi.plugin(pluginName).service("firebaseService").forgotPassword(ctx);
518
+ const { email: email2 } = ctx.request.body || {};
519
+ ctx.body = await strapi.plugin(pluginName).service("firebaseService").forgotPassword(email2);
522
520
  } catch (error2) {
523
521
  strapi.log.error("forgotPassword controller error:", error2);
524
522
  if (error2.name === "ValidationError") {
@@ -541,7 +539,10 @@ const firebaseController = {
541
539
  async resetPassword(ctx) {
542
540
  strapi.log.debug("resetPassword endpoint called");
543
541
  try {
544
- ctx.body = await strapi.plugin(pluginName).service("firebaseService").resetPassword(ctx);
542
+ const { password } = ctx.request.body || {};
543
+ const token = ctx.request.headers.authorization?.replace("Bearer ", "");
544
+ const populate2 = ctx.request.query.populate || [];
545
+ ctx.body = await strapi.plugin(pluginName).service("firebaseService").resetPassword(password, token, populate2);
545
546
  } catch (error2) {
546
547
  strapi.log.error("resetPassword controller error:", error2);
547
548
  if (error2.name === "ValidationError" || error2.name === "UnauthorizedError") {
@@ -554,7 +555,8 @@ const firebaseController = {
554
555
  },
555
556
  async requestMagicLink(ctx) {
556
557
  try {
557
- const result = await strapi.plugin("firebase-authentication").service("firebaseService").requestMagicLink(ctx);
558
+ const { email: email2 } = ctx.request.body || {};
559
+ const result = await strapi.plugin("firebase-authentication").service("firebaseService").requestMagicLink(email2);
558
560
  ctx.body = result;
559
561
  } catch (error2) {
560
562
  if (error2.name === "ValidationError" || error2.name === "ApplicationError") {
@@ -28937,11 +28939,12 @@ const userController = {
28937
28939
  };
28938
28940
  const settingsController = {
28939
28941
  setFirebaseConfigJson: async (ctx) => {
28940
- ctx.body = await strapi.plugin("firebase-authentication").service("settingsService").setFirebaseConfigJson(ctx);
28942
+ const requestBody = ctx.request.body;
28943
+ ctx.body = await strapi.plugin("firebase-authentication").service("settingsService").setFirebaseConfigJson(requestBody);
28941
28944
  },
28942
28945
  getFirebaseConfigJson: async (ctx) => {
28943
28946
  try {
28944
- const config2 = await strapi.plugin("firebase-authentication").service("settingsService").getFirebaseConfigJson(ctx);
28947
+ const config2 = await strapi.plugin("firebase-authentication").service("settingsService").getFirebaseConfigJson();
28945
28948
  if (!config2) {
28946
28949
  return ctx.send(null);
28947
28950
  }
@@ -28954,7 +28957,7 @@ const settingsController = {
28954
28957
  },
28955
28958
  async delFirebaseConfigJson(ctx) {
28956
28959
  try {
28957
- const isExist = await strapi.plugin("firebase-authentication").service("settingsService").delFirebaseConfigJson(ctx);
28960
+ const isExist = await strapi.plugin("firebase-authentication").service("settingsService").delFirebaseConfigJson();
28958
28961
  if (!isExist) {
28959
28962
  throw new NotFoundError("No Firebase configs exists for deletion");
28960
28963
  }
@@ -29281,7 +29284,7 @@ const settingsService = ({ strapi: strapi2 }) => {
29281
29284
  try {
29282
29285
  strapi2.log.info("Starting Firebase initialization...");
29283
29286
  const res = await strapi2.db.query(CONFIG_CONTENT_TYPE).findOne({ where: {} });
29284
- strapi2.log.debug("Found config:", !!res);
29287
+ strapi2.log.debug(`Found config: ${!!res}`);
29285
29288
  if (!res) {
29286
29289
  strapi2.log.debug("No config found, checking for existing Firebase app...");
29287
29290
  if (strapi2.firebase) {
@@ -29291,7 +29294,7 @@ const settingsService = ({ strapi: strapi2 }) => {
29291
29294
  return;
29292
29295
  }
29293
29296
  const jsonObject = res["firebase_config_json"];
29294
- strapi2.log.debug("Config JSON present:", !!jsonObject?.firebaseConfigJson);
29297
+ strapi2.log.debug(`Config JSON present: ${!!jsonObject?.firebaseConfigJson}`);
29295
29298
  if (!jsonObject || !jsonObject.firebaseConfigJson) {
29296
29299
  strapi2.log.debug("No valid JSON config, checking for existing Firebase app...");
29297
29300
  if (strapi2.firebase) {
@@ -29326,11 +29329,11 @@ const settingsService = ({ strapi: strapi2 }) => {
29326
29329
  strapi2.firebase = admin__default.default;
29327
29330
  strapi2.log.info("Firebase initialization complete - admin instance attached to strapi.firebase");
29328
29331
  } catch (initError) {
29329
- strapi2.log.error("Failed to initialize Firebase:", initError);
29332
+ strapi2.log.error(`Failed to initialize Firebase: ${initError.message}`);
29330
29333
  throw initError;
29331
29334
  }
29332
29335
  } catch (error2) {
29333
- strapi2.log.error("Firebase bootstrap error:", error2);
29336
+ strapi2.log.error(`Firebase bootstrap error: ${error2.message}`);
29334
29337
  }
29335
29338
  },
29336
29339
  /**
@@ -29378,7 +29381,7 @@ const settingsService = ({ strapi: strapi2 }) => {
29378
29381
  magicLinkExpiryHours: configObject.magicLinkExpiryHours || 1
29379
29382
  };
29380
29383
  } catch (error2) {
29381
- strapi2.log.error("Firebase config error:", error2);
29384
+ strapi2.log.error(`Firebase config error: ${error2.message}`);
29382
29385
  throw new ApplicationError$1("Error retrieving Firebase config", {
29383
29386
  error: error2.message
29384
29387
  });
@@ -29405,10 +29408,9 @@ const settingsService = ({ strapi: strapi2 }) => {
29405
29408
  * The service account JSON is encrypted using AES before storage,
29406
29409
  * while the Web API key and password settings are stored in plain text.
29407
29410
  */
29408
- async setFirebaseConfigJson(ctx) {
29411
+ async setFirebaseConfigJson(requestBody) {
29409
29412
  try {
29410
- strapi2.log.debug("setFirebaseConfigJson", ctx.request);
29411
- const { body: requestBody } = ctx.request;
29413
+ strapi2.log.debug("setFirebaseConfigJson called");
29412
29414
  const firebaseConfigJsonString = requestBody.firebaseConfigJson;
29413
29415
  const firebaseWebApiKey = requestBody.firebaseWebApiKey || null;
29414
29416
  const {
@@ -29479,12 +29481,12 @@ const settingsService = ({ strapi: strapi2 }) => {
29479
29481
  const configData = res.firebaseConfigJson || res.firebase_config_json;
29480
29482
  if (!configData) {
29481
29483
  strapi2.log.error("Firebase config data missing from database response");
29482
- strapi2.log.error("Available keys in response:", Object.keys(res));
29484
+ strapi2.log.error(`Available keys in response: ${JSON.stringify(Object.keys(res))}`);
29483
29485
  throw new ApplicationError2("Failed to retrieve Firebase configuration from database");
29484
29486
  }
29485
29487
  const firebaseConfigHash = configData.firebaseConfigJson;
29486
29488
  if (!firebaseConfigHash) {
29487
- strapi2.log.error("Firebase config hash missing from config data:", configData);
29489
+ strapi2.log.error(`Firebase config hash missing from config data: ${JSON.stringify(configData)}`);
29488
29490
  throw new ApplicationError2("Firebase configuration hash is missing");
29489
29491
  }
29490
29492
  const firebaseConfigJsonValue = await this.decryptJson(encryptionKey, firebaseConfigHash);
@@ -29503,13 +29505,12 @@ const settingsService = ({ strapi: strapi2 }) => {
29503
29505
  return res;
29504
29506
  } catch (error2) {
29505
29507
  strapi2.log.error("=== FIREBASE CONFIG SAVE ERROR ===");
29506
- strapi2.log.error("Error name:", error2.name);
29507
- strapi2.log.error("Error message:", error2.message);
29508
- strapi2.log.error("Error stack:", error2.stack);
29508
+ strapi2.log.error(`Error name: ${error2.name}`);
29509
+ strapi2.log.error(`Error message: ${error2.message}`);
29510
+ strapi2.log.error(`Error stack: ${error2.stack}`);
29509
29511
  try {
29510
- const { body } = ctx.request;
29511
- if (body) {
29512
- strapi2.log.error("Request body keys:", Object.keys(body));
29512
+ if (requestBody) {
29513
+ strapi2.log.error(`Request body keys: ${JSON.stringify(Object.keys(requestBody))}`);
29513
29514
  }
29514
29515
  } catch (e) {
29515
29516
  strapi2.log.error("Could not access request body");
@@ -29530,7 +29531,7 @@ const settingsService = ({ strapi: strapi2 }) => {
29530
29531
  try {
29531
29532
  strapi2.log.debug("delFirebaseConfigJson called");
29532
29533
  const isExist = await strapi2.db.query(CONFIG_CONTENT_TYPE).findOne({ where: {} });
29533
- strapi2.log.debug("Config exists:", isExist);
29534
+ strapi2.log.debug(`Config exists: ${!!isExist}`);
29534
29535
  if (!isExist) {
29535
29536
  strapi2.log.info(ERROR_MESSAGES.DELETION_NO_CONFIG);
29536
29537
  return null;
@@ -29538,12 +29539,12 @@ const settingsService = ({ strapi: strapi2 }) => {
29538
29539
  const res = await strapi2.db.query(CONFIG_CONTENT_TYPE).delete({
29539
29540
  where: { id: isExist.id }
29540
29541
  });
29541
- strapi2.log.debug("Delete result:", res);
29542
+ strapi2.log.debug(`Delete result: ${JSON.stringify(res)}`);
29542
29543
  await strapi2.plugin("firebase-authentication").service("settingsService").init();
29543
29544
  strapi2.log.info(SUCCESS_MESSAGES.FIREBASE_CONFIG_DELETED);
29544
29545
  return res;
29545
29546
  } catch (error2) {
29546
- strapi2.log.error("delFirebaseConfigJson error:", error2);
29547
+ strapi2.log.error(`delFirebaseConfigJson error: ${error2.message}`);
29547
29548
  throw new ApplicationError2(ERROR_MESSAGES.SOMETHING_WENT_WRONG, {
29548
29549
  error: error2
29549
29550
  });
@@ -30466,8 +30467,8 @@ const firebaseService = ({ strapi: strapi2 }) => ({
30466
30467
  * Forgot password flow - sends reset email
30467
30468
  * Public endpoint that sends a Firebase-hosted password reset email using Firebase's secure hosted UI
30468
30469
  */
30469
- forgotPassword: async (ctx) => {
30470
- const { email: email2 } = ctx.request.body;
30470
+ forgotPassword: async (email2) => {
30471
+ strapi2.log.info(`[forgotPassword] Starting password reset for email: ${email2}`);
30471
30472
  if (!email2) {
30472
30473
  throw new ValidationError$1("Email is required");
30473
30474
  }
@@ -30508,8 +30509,16 @@ const firebaseService = ({ strapi: strapi2 }) => ({
30508
30509
  });
30509
30510
  }
30510
30511
  if (!strapiUser) {
30512
+ strapi2.log.warn(`⚠️ [forgotPassword] User not found for email: ${email2}`);
30511
30513
  return { message: "If an account with that email exists, a password reset link has been sent." };
30512
30514
  }
30515
+ strapi2.log.info(
30516
+ `✅ [forgotPassword] User found: ${JSON.stringify({
30517
+ documentId: strapiUser.documentId,
30518
+ email: strapiUser.email,
30519
+ firebaseUID: firebaseUser?.uid || "not in Firebase"
30520
+ })}`
30521
+ );
30513
30522
  const actionCodeSettings = {
30514
30523
  url: resetUrl,
30515
30524
  // Continue URL after reset completes on Firebase's page
@@ -30523,6 +30532,7 @@ const firebaseService = ({ strapi: strapi2 }) => ({
30523
30532
  });
30524
30533
  let resetLink;
30525
30534
  try {
30535
+ strapi2.log.info(`[forgotPassword] Generating Firebase password reset link for: ${strapiUser.email}`);
30526
30536
  resetLink = await Promise.race([
30527
30537
  strapi2.firebase.auth().generatePasswordResetLink(strapiUser.email, actionCodeSettings),
30528
30538
  timeoutPromise
@@ -30534,12 +30544,22 @@ const firebaseService = ({ strapi: strapi2 }) => ({
30534
30544
  strapi2.log.error(`❌ Failed to generate password reset link for ${strapiUser.email}:`, error2);
30535
30545
  throw error2;
30536
30546
  }
30547
+ strapi2.log.info(`[forgotPassword] Attempting to send password reset email to: ${strapiUser.email}`);
30537
30548
  await strapi2.plugin("firebase-authentication").service("emailService").sendPasswordResetEmail(strapiUser, resetLink);
30549
+ strapi2.log.info(`✅ [forgotPassword] Password reset email sent successfully to: ${strapiUser.email}`);
30538
30550
  return {
30539
30551
  message: "If an account with that email exists, a password reset link has been sent."
30540
30552
  };
30541
30553
  } catch (error2) {
30542
- strapi2.log.error("forgotPassword error:", error2);
30554
+ strapi2.log.error(
30555
+ `❌ [forgotPassword] ERROR: ${JSON.stringify({
30556
+ email: email2,
30557
+ message: error2.message,
30558
+ code: error2.code,
30559
+ name: error2.name,
30560
+ stack: error2.stack
30561
+ })}`
30562
+ );
30543
30563
  return {
30544
30564
  message: "If an account with that email exists, a password reset link has been sent."
30545
30565
  };
@@ -30559,13 +30579,10 @@ const firebaseService = ({ strapi: strapi2 }) => ({
30559
30579
  *
30560
30580
  * NOT used for forgot password email flow - that now uses Firebase's hosted UI
30561
30581
  */
30562
- resetPassword: async (ctx) => {
30563
- const { password } = ctx.request.body;
30564
- const populate2 = ctx.request.query.populate || [];
30582
+ resetPassword: async (password, token, populate2) => {
30565
30583
  if (!password) {
30566
30584
  throw new ValidationError$1("Password is required");
30567
30585
  }
30568
- const token = ctx.request.header.authorization?.replace("Bearer ", "");
30569
30586
  if (!token) {
30570
30587
  throw new UnauthorizedError("Authorization token is required");
30571
30588
  }
@@ -30621,8 +30638,7 @@ const firebaseService = ({ strapi: strapi2 }) => ({
30621
30638
  * Generates a sign-in link using Firebase Admin SDK
30622
30639
  * Note: Verification requires client-side completion
30623
30640
  */
30624
- async requestMagicLink(ctx) {
30625
- const { email: email2 } = ctx.request.body;
30641
+ async requestMagicLink(email2) {
30626
30642
  if (!email2 || typeof email2 !== "string") {
30627
30643
  throw new ValidationError$1("Valid email is required");
30628
30644
  }
@@ -30652,7 +30668,7 @@ const firebaseService = ({ strapi: strapi2 }) => ({
30652
30668
  };
30653
30669
  const magicLink = await strapi2.firebase.auth().generateSignInWithEmailLink(email2, actionCodeSettings);
30654
30670
  if (process.env.NODE_ENV !== "production") {
30655
- strapi2.log.info("🔗 Magic Link Generation Request:");
30671
+ strapi2.log.info("Magic Link Generation Request:");
30656
30672
  strapi2.log.info(` Email: ${email2}`);
30657
30673
  strapi2.log.info(` Verification URL: ${magicLinkUrl}`);
30658
30674
  strapi2.log.info(` Expires: ${config2.magicLinkExpiryHours || 1} hour(s)`);
@@ -31258,7 +31274,7 @@ class EmailService {
31258
31274
  const template = await templateService2.getTemplate("magicLink");
31259
31275
  const compiledSubject = _$1.template(template.subject)(completeVariables);
31260
31276
  strapi.log.info("\n" + "=".repeat(80));
31261
- strapi.log.info("🔗 MAGIC LINK EMAIL (Development Mode)");
31277
+ strapi.log.info("MAGIC LINK EMAIL (Development Mode)");
31262
31278
  strapi.log.info("=".repeat(80));
31263
31279
  strapi.log.info(`To: ${email2}`);
31264
31280
  strapi.log.info(`Subject: ${compiledSubject}`);
@@ -31373,7 +31389,7 @@ const autoLinkService = {
31373
31389
  errors: 0
31374
31390
  };
31375
31391
  try {
31376
- strapi2.log.info("🔗 Auto-linking Strapi users with Firebase users...");
31392
+ strapi2.log.info("Auto-linking Strapi users with Firebase users...");
31377
31393
  if (!strapi2.firebase) {
31378
31394
  strapi2.log.warn("Firebase not initialized - skipping auto-linking");
31379
31395
  return result;
@@ -31412,6 +31428,7 @@ const autoLinkService = {
31412
31428
  }
31413
31429
  }
31414
31430
  for (const strapiUser of strapiUsers) {
31431
+ let firebaseUser = null;
31415
31432
  try {
31416
31433
  const existing = await strapi2.db.query("plugin::firebase-authentication.firebase-user-data").findOne({
31417
31434
  select: ["id"],
@@ -31421,7 +31438,7 @@ const autoLinkService = {
31421
31438
  result.skipped++;
31422
31439
  continue;
31423
31440
  }
31424
- let firebaseUser = null;
31441
+ firebaseUser = null;
31425
31442
  if (strapiUser.email) {
31426
31443
  firebaseUser = firebaseByEmail.get(strapiUser.email.toLowerCase());
31427
31444
  }
@@ -31432,6 +31449,23 @@ const autoLinkService = {
31432
31449
  result.skipped++;
31433
31450
  continue;
31434
31451
  }
31452
+ const existingUIDLink = await strapi2.db.query("plugin::firebase-authentication.firebase-user-data").findOne({
31453
+ where: { firebaseUserID: firebaseUser.uid },
31454
+ populate: ["user"]
31455
+ });
31456
+ if (existingUIDLink && existingUIDLink.user?.documentId !== strapiUser.documentId) {
31457
+ strapi2.log.warn(
31458
+ `⚠️ [Auto-Link] Skipping '${strapiUser.username || strapiUser.email}' - Firebase UID already linked`
31459
+ );
31460
+ strapi2.log.warn(` Strapi User: ${strapiUser.email} (documentId: ${strapiUser.documentId})`);
31461
+ strapi2.log.warn(` Firebase UID: ${firebaseUser.uid}`);
31462
+ strapi2.log.warn(
31463
+ ` Already linked to: ${existingUIDLink.user?.email} (documentId: ${existingUIDLink.user?.documentId})`
31464
+ );
31465
+ strapi2.log.warn(` Action: Resolve duplicate users or manually unlink conflicting record`);
31466
+ result.skipped++;
31467
+ continue;
31468
+ }
31435
31469
  try {
31436
31470
  const firebaseData = {
31437
31471
  firebaseUserID: firebaseUser.uid
@@ -31450,18 +31484,42 @@ const autoLinkService = {
31450
31484
  throw createError;
31451
31485
  }
31452
31486
  } catch (error2) {
31453
- result.errors++;
31454
- strapi2.log.error(`Error linking user ${strapiUser.username}:`, error2);
31487
+ if (error2.message?.includes("This attribute must be unique")) {
31488
+ strapi2.log.warn(
31489
+ `⚠️ [Auto-Link] Unique constraint conflict for '${strapiUser.username || strapiUser.email}'`
31490
+ );
31491
+ strapi2.log.warn(` Strapi User: ${strapiUser.email} (documentId: ${strapiUser.documentId})`);
31492
+ if (firebaseUser) {
31493
+ strapi2.log.warn(` Firebase UID: ${firebaseUser.uid}`);
31494
+ strapi2.log.warn(` Cause: This Firebase UID is already linked to another Strapi user`);
31495
+ strapi2.log.warn(
31496
+ ` Action: Query firebase_user_data table to find conflict - WHERE firebase_user_id = '${firebaseUser.uid}'`
31497
+ );
31498
+ }
31499
+ result.skipped++;
31500
+ } else {
31501
+ result.errors++;
31502
+ strapi2.log.error(
31503
+ `❌ [Auto-Link] Unexpected error linking user '${strapiUser.username || strapiUser.email}': ${error2.message}`
31504
+ );
31505
+ }
31455
31506
  }
31456
31507
  }
31457
- strapi2.log.info(
31458
- `✅ Auto-linking complete: ${result.linked} linked, ${result.skipped} skipped, ${result.errors} errors`
31459
- );
31508
+ if (result.errors > 0) {
31509
+ strapi2.log.error(
31510
+ `❌ Auto-linking completed with unexpected errors: ${result.linked} linked, ${result.skipped} skipped, ${result.errors} errors`
31511
+ );
31512
+ strapi2.log.error(` Review error logs above for resolution steps`);
31513
+ } else {
31514
+ strapi2.log.info(
31515
+ `✅ Auto-linking complete: ${result.linked} linked, ${result.skipped} skipped, ${result.errors} errors`
31516
+ );
31517
+ }
31460
31518
  strapi2.log.info(
31461
31519
  ` Total: ${result.totalStrapiUsers} Strapi users, ${result.totalFirebaseUsers} Firebase users`
31462
31520
  );
31463
31521
  } catch (error2) {
31464
- strapi2.log.error("Fatal error during auto-linking:", error2);
31522
+ strapi2.log.error(`❌ Fatal error during auto-linking: ${error2.message}`);
31465
31523
  }
31466
31524
  return result;
31467
31525
  }