strapi-plugin-firebase-authentication 1.1.8 → 1.1.10
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-Dt8F60Rl.mjs → App-Beg-rhfs.mjs} +124 -122
- package/dist/_chunks/{App-C_pWLYYH.js → App-Cd5yIsKz.js} +124 -122
- package/dist/_chunks/{api-T8QRCile.mjs → api-BznQNKgc.mjs} +1 -1
- package/dist/_chunks/{api-CxfueBVM.js → api-C7A4Ky3o.js} +1 -1
- package/dist/_chunks/{index-BzSU0Li9.mjs → index-C3713gpv.mjs} +1 -1
- package/dist/_chunks/{index-Do9Y0scX.mjs → index-Dxg4gNu5.mjs} +2 -2
- package/dist/_chunks/{index-BIHS9XB4.js → index-Dz1nqS7B.js} +2 -2
- package/dist/_chunks/{index-C87l6qcA.js → index-d4XkRCYN.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +185 -66
- package/dist/server/index.mjs +185 -66
- package/dist/server/src/controllers/firebaseController.d.ts +0 -1
- package/dist/server/src/controllers/index.d.ts +0 -1
- package/dist/server/src/index.d.ts +5 -6
- package/dist/server/src/services/firebaseService.d.ts +3 -3
- package/dist/server/src/services/index.d.ts +5 -5
- package/dist/server/src/services/settingsService.d.ts +1 -2
- package/dist/server/src/services/userService.d.ts +1 -1
- package/package.json +1 -1
package/dist/server/index.mjs
CHANGED
|
@@ -167,8 +167,8 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
167
167
|
}
|
|
168
168
|
} catch (error2) {
|
|
169
169
|
strapi2.log.error("Firebase initialization failed during bootstrap:");
|
|
170
|
-
strapi2.log.error(
|
|
171
|
-
strapi2.log.error(
|
|
170
|
+
strapi2.log.error(` Error: ${error2.message}`);
|
|
171
|
+
strapi2.log.error(` Stack: ${error2.stack}`);
|
|
172
172
|
}
|
|
173
173
|
await strapi2.admin.services.permission.actionProvider.registerMany(actions);
|
|
174
174
|
if (strapi2.plugin("users-permissions")) {
|
|
@@ -178,14 +178,14 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
178
178
|
if (process.env.RUN_FIREBASE_MIGRATION === "true") {
|
|
179
179
|
const dryRun = process.env.DRY_RUN === "true";
|
|
180
180
|
strapi2.log.info("");
|
|
181
|
-
strapi2.log.info("
|
|
181
|
+
strapi2.log.info("Firebase migration triggered by RUN_FIREBASE_MIGRATION env variable");
|
|
182
182
|
await migrateFirebaseUserData(strapi2, dryRun);
|
|
183
183
|
}
|
|
184
184
|
setImmediate(async () => {
|
|
185
185
|
try {
|
|
186
186
|
await strapi2.plugin("firebase-authentication").service("autoLinkService").linkAllUsers(strapi2);
|
|
187
187
|
} catch (error2) {
|
|
188
|
-
strapi2.log.error(
|
|
188
|
+
strapi2.log.error(`Auto-linking failed: ${error2.message}`);
|
|
189
189
|
}
|
|
190
190
|
});
|
|
191
191
|
};
|
|
@@ -410,7 +410,7 @@ const firebaseController = {
|
|
|
410
410
|
const result = await strapi.plugin(pluginName).service("firebaseService").validateFirebaseToken(idToken, profileMetaData, populate2);
|
|
411
411
|
ctx.body = result;
|
|
412
412
|
} catch (error2) {
|
|
413
|
-
strapi.log.error(
|
|
413
|
+
strapi.log.error(`validateToken controller error: ${error2.message}`);
|
|
414
414
|
if (error2.name === "ValidationError") {
|
|
415
415
|
return ctx.badRequest(error2.message);
|
|
416
416
|
}
|
|
@@ -420,9 +420,6 @@ const firebaseController = {
|
|
|
420
420
|
throw error2;
|
|
421
421
|
}
|
|
422
422
|
},
|
|
423
|
-
async createAlias(ctx) {
|
|
424
|
-
ctx.body = await strapi.plugin(pluginName).service("firebaseService").createAlias(ctx);
|
|
425
|
-
},
|
|
426
423
|
async deleteByEmail(email2) {
|
|
427
424
|
const user = await strapi.firebase.auth().getUserByEmail(email2);
|
|
428
425
|
await strapi.plugin(pluginName).service("firebaseService").delete(user.toJSON().uid);
|
|
@@ -486,7 +483,8 @@ const firebaseController = {
|
|
|
486
483
|
async forgotPassword(ctx) {
|
|
487
484
|
strapi.log.debug("forgotPassword endpoint called");
|
|
488
485
|
try {
|
|
489
|
-
ctx.body
|
|
486
|
+
const { email: email2 } = ctx.request.body || {};
|
|
487
|
+
ctx.body = await strapi.plugin(pluginName).service("firebaseService").forgotPassword(email2);
|
|
490
488
|
} catch (error2) {
|
|
491
489
|
strapi.log.error("forgotPassword controller error:", error2);
|
|
492
490
|
if (error2.name === "ValidationError") {
|
|
@@ -509,7 +507,10 @@ const firebaseController = {
|
|
|
509
507
|
async resetPassword(ctx) {
|
|
510
508
|
strapi.log.debug("resetPassword endpoint called");
|
|
511
509
|
try {
|
|
512
|
-
ctx.body
|
|
510
|
+
const { password } = ctx.request.body || {};
|
|
511
|
+
const token = ctx.request.headers.authorization?.replace("Bearer ", "");
|
|
512
|
+
const populate2 = ctx.request.query.populate || [];
|
|
513
|
+
ctx.body = await strapi.plugin(pluginName).service("firebaseService").resetPassword(password, token, populate2);
|
|
513
514
|
} catch (error2) {
|
|
514
515
|
strapi.log.error("resetPassword controller error:", error2);
|
|
515
516
|
if (error2.name === "ValidationError" || error2.name === "UnauthorizedError") {
|
|
@@ -522,7 +523,8 @@ const firebaseController = {
|
|
|
522
523
|
},
|
|
523
524
|
async requestMagicLink(ctx) {
|
|
524
525
|
try {
|
|
525
|
-
const
|
|
526
|
+
const { email: email2 } = ctx.request.body || {};
|
|
527
|
+
const result = await strapi.plugin("firebase-authentication").service("firebaseService").requestMagicLink(email2);
|
|
526
528
|
ctx.body = result;
|
|
527
529
|
} catch (error2) {
|
|
528
530
|
if (error2.name === "ValidationError" || error2.name === "ApplicationError") {
|
|
@@ -28905,11 +28907,12 @@ const userController = {
|
|
|
28905
28907
|
};
|
|
28906
28908
|
const settingsController = {
|
|
28907
28909
|
setFirebaseConfigJson: async (ctx) => {
|
|
28908
|
-
|
|
28910
|
+
const requestBody = ctx.request.body;
|
|
28911
|
+
ctx.body = await strapi.plugin("firebase-authentication").service("settingsService").setFirebaseConfigJson(requestBody);
|
|
28909
28912
|
},
|
|
28910
28913
|
getFirebaseConfigJson: async (ctx) => {
|
|
28911
28914
|
try {
|
|
28912
|
-
const config2 = await strapi.plugin("firebase-authentication").service("settingsService").getFirebaseConfigJson(
|
|
28915
|
+
const config2 = await strapi.plugin("firebase-authentication").service("settingsService").getFirebaseConfigJson();
|
|
28913
28916
|
if (!config2) {
|
|
28914
28917
|
return ctx.send(null);
|
|
28915
28918
|
}
|
|
@@ -28922,7 +28925,7 @@ const settingsController = {
|
|
|
28922
28925
|
},
|
|
28923
28926
|
async delFirebaseConfigJson(ctx) {
|
|
28924
28927
|
try {
|
|
28925
|
-
const isExist = await strapi.plugin("firebase-authentication").service("settingsService").delFirebaseConfigJson(
|
|
28928
|
+
const isExist = await strapi.plugin("firebase-authentication").service("settingsService").delFirebaseConfigJson();
|
|
28926
28929
|
if (!isExist) {
|
|
28927
28930
|
throw new NotFoundError("No Firebase configs exists for deletion");
|
|
28928
28931
|
}
|
|
@@ -29249,7 +29252,7 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29249
29252
|
try {
|
|
29250
29253
|
strapi2.log.info("Starting Firebase initialization...");
|
|
29251
29254
|
const res = await strapi2.db.query(CONFIG_CONTENT_TYPE).findOne({ where: {} });
|
|
29252
|
-
strapi2.log.debug(
|
|
29255
|
+
strapi2.log.debug(`Found config: ${!!res}`);
|
|
29253
29256
|
if (!res) {
|
|
29254
29257
|
strapi2.log.debug("No config found, checking for existing Firebase app...");
|
|
29255
29258
|
if (strapi2.firebase) {
|
|
@@ -29259,7 +29262,7 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29259
29262
|
return;
|
|
29260
29263
|
}
|
|
29261
29264
|
const jsonObject = res["firebase_config_json"];
|
|
29262
|
-
strapi2.log.debug(
|
|
29265
|
+
strapi2.log.debug(`Config JSON present: ${!!jsonObject?.firebaseConfigJson}`);
|
|
29263
29266
|
if (!jsonObject || !jsonObject.firebaseConfigJson) {
|
|
29264
29267
|
strapi2.log.debug("No valid JSON config, checking for existing Firebase app...");
|
|
29265
29268
|
if (strapi2.firebase) {
|
|
@@ -29294,11 +29297,11 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29294
29297
|
strapi2.firebase = admin$1;
|
|
29295
29298
|
strapi2.log.info("Firebase initialization complete - admin instance attached to strapi.firebase");
|
|
29296
29299
|
} catch (initError) {
|
|
29297
|
-
strapi2.log.error(
|
|
29300
|
+
strapi2.log.error(`Failed to initialize Firebase: ${initError.message}`);
|
|
29298
29301
|
throw initError;
|
|
29299
29302
|
}
|
|
29300
29303
|
} catch (error2) {
|
|
29301
|
-
strapi2.log.error(
|
|
29304
|
+
strapi2.log.error(`Firebase bootstrap error: ${error2.message}`);
|
|
29302
29305
|
}
|
|
29303
29306
|
},
|
|
29304
29307
|
/**
|
|
@@ -29346,7 +29349,7 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29346
29349
|
magicLinkExpiryHours: configObject.magicLinkExpiryHours || 1
|
|
29347
29350
|
};
|
|
29348
29351
|
} catch (error2) {
|
|
29349
|
-
strapi2.log.error(
|
|
29352
|
+
strapi2.log.error(`Firebase config error: ${error2.message}`);
|
|
29350
29353
|
throw new ApplicationError$1("Error retrieving Firebase config", {
|
|
29351
29354
|
error: error2.message
|
|
29352
29355
|
});
|
|
@@ -29373,10 +29376,9 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29373
29376
|
* The service account JSON is encrypted using AES before storage,
|
|
29374
29377
|
* while the Web API key and password settings are stored in plain text.
|
|
29375
29378
|
*/
|
|
29376
|
-
async setFirebaseConfigJson(
|
|
29379
|
+
async setFirebaseConfigJson(requestBody) {
|
|
29377
29380
|
try {
|
|
29378
|
-
strapi2.log.debug("setFirebaseConfigJson"
|
|
29379
|
-
const { body: requestBody } = ctx.request;
|
|
29381
|
+
strapi2.log.debug("setFirebaseConfigJson called");
|
|
29380
29382
|
const firebaseConfigJsonString = requestBody.firebaseConfigJson;
|
|
29381
29383
|
const firebaseWebApiKey = requestBody.firebaseWebApiKey || null;
|
|
29382
29384
|
const {
|
|
@@ -29447,18 +29449,17 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29447
29449
|
const configData = res.firebaseConfigJson || res.firebase_config_json;
|
|
29448
29450
|
if (!configData) {
|
|
29449
29451
|
strapi2.log.error("Firebase config data missing from database response");
|
|
29450
|
-
strapi2.log.error(
|
|
29452
|
+
strapi2.log.error(`Available keys in response: ${JSON.stringify(Object.keys(res))}`);
|
|
29451
29453
|
throw new ApplicationError2("Failed to retrieve Firebase configuration from database");
|
|
29452
29454
|
}
|
|
29453
29455
|
const firebaseConfigHash = configData.firebaseConfigJson;
|
|
29454
29456
|
if (!firebaseConfigHash) {
|
|
29455
|
-
strapi2.log.error(
|
|
29457
|
+
strapi2.log.error(`Firebase config hash missing from config data: ${JSON.stringify(configData)}`);
|
|
29456
29458
|
throw new ApplicationError2("Firebase configuration hash is missing");
|
|
29457
29459
|
}
|
|
29458
29460
|
const firebaseConfigJsonValue = await this.decryptJson(encryptionKey, firebaseConfigHash);
|
|
29459
|
-
|
|
29460
|
-
res.
|
|
29461
|
-
res.firebase_config_json = configData;
|
|
29461
|
+
res.firebaseConfigJson = firebaseConfigJsonValue;
|
|
29462
|
+
res.firebase_config_json = firebaseConfigJsonValue;
|
|
29462
29463
|
res.firebaseWebApiKey = res.firebase_web_api_key || null;
|
|
29463
29464
|
res.passwordRequirementsRegex = res.passwordRequirementsRegex || passwordRequirementsRegex;
|
|
29464
29465
|
res.passwordRequirementsMessage = res.passwordRequirementsMessage || passwordRequirementsMessage;
|
|
@@ -29471,13 +29472,12 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29471
29472
|
return res;
|
|
29472
29473
|
} catch (error2) {
|
|
29473
29474
|
strapi2.log.error("=== FIREBASE CONFIG SAVE ERROR ===");
|
|
29474
|
-
strapi2.log.error(
|
|
29475
|
-
strapi2.log.error(
|
|
29476
|
-
strapi2.log.error(
|
|
29475
|
+
strapi2.log.error(`Error name: ${error2.name}`);
|
|
29476
|
+
strapi2.log.error(`Error message: ${error2.message}`);
|
|
29477
|
+
strapi2.log.error(`Error stack: ${error2.stack}`);
|
|
29477
29478
|
try {
|
|
29478
|
-
|
|
29479
|
-
|
|
29480
|
-
strapi2.log.error("Request body keys:", Object.keys(body));
|
|
29479
|
+
if (requestBody) {
|
|
29480
|
+
strapi2.log.error(`Request body keys: ${JSON.stringify(Object.keys(requestBody))}`);
|
|
29481
29481
|
}
|
|
29482
29482
|
} catch (e) {
|
|
29483
29483
|
strapi2.log.error("Could not access request body");
|
|
@@ -29498,7 +29498,7 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29498
29498
|
try {
|
|
29499
29499
|
strapi2.log.debug("delFirebaseConfigJson called");
|
|
29500
29500
|
const isExist = await strapi2.db.query(CONFIG_CONTENT_TYPE).findOne({ where: {} });
|
|
29501
|
-
strapi2.log.debug(
|
|
29501
|
+
strapi2.log.debug(`Config exists: ${!!isExist}`);
|
|
29502
29502
|
if (!isExist) {
|
|
29503
29503
|
strapi2.log.info(ERROR_MESSAGES.DELETION_NO_CONFIG);
|
|
29504
29504
|
return null;
|
|
@@ -29506,12 +29506,12 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29506
29506
|
const res = await strapi2.db.query(CONFIG_CONTENT_TYPE).delete({
|
|
29507
29507
|
where: { id: isExist.id }
|
|
29508
29508
|
});
|
|
29509
|
-
strapi2.log.debug(
|
|
29509
|
+
strapi2.log.debug(`Delete result: ${JSON.stringify(res)}`);
|
|
29510
29510
|
await strapi2.plugin("firebase-authentication").service("settingsService").init();
|
|
29511
29511
|
strapi2.log.info(SUCCESS_MESSAGES.FIREBASE_CONFIG_DELETED);
|
|
29512
29512
|
return res;
|
|
29513
29513
|
} catch (error2) {
|
|
29514
|
-
strapi2.log.error(
|
|
29514
|
+
strapi2.log.error(`delFirebaseConfigJson error: ${error2.message}`);
|
|
29515
29515
|
throw new ApplicationError2(ERROR_MESSAGES.SOMETHING_WENT_WRONG, {
|
|
29516
29516
|
error: error2
|
|
29517
29517
|
});
|
|
@@ -29818,9 +29818,71 @@ const userService = ({ strapi: strapi2 }) => {
|
|
|
29818
29818
|
update: async (entityId, payload) => {
|
|
29819
29819
|
try {
|
|
29820
29820
|
ensureFirebaseInitialized();
|
|
29821
|
-
const
|
|
29822
|
-
|
|
29821
|
+
const firebaseData = await strapi2.plugin("firebase-authentication").service("firebaseUserDataService").getByFirebaseUID(entityId);
|
|
29822
|
+
if (!firebaseData?.user) {
|
|
29823
|
+
throw new NotFoundError(`User not found for Firebase UID: ${entityId}`);
|
|
29824
|
+
}
|
|
29825
|
+
const firebasePayload = {};
|
|
29826
|
+
if (payload.email !== void 0) firebasePayload.email = payload.email;
|
|
29827
|
+
if (payload.phoneNumber !== void 0) firebasePayload.phoneNumber = payload.phoneNumber;
|
|
29828
|
+
if (payload.displayName !== void 0) firebasePayload.displayName = payload.displayName;
|
|
29829
|
+
if (payload.photoURL !== void 0) firebasePayload.photoURL = payload.photoURL;
|
|
29830
|
+
if (payload.disabled !== void 0) firebasePayload.disabled = payload.disabled;
|
|
29831
|
+
if (payload.emailVerified !== void 0) firebasePayload.emailVerified = payload.emailVerified;
|
|
29832
|
+
if (payload.password !== void 0) firebasePayload.password = payload.password;
|
|
29833
|
+
const firebasePromise = strapi2.firebase.auth().updateUser(entityId, firebasePayload);
|
|
29834
|
+
const strapiPayload = {};
|
|
29835
|
+
if (payload.email !== void 0) {
|
|
29836
|
+
strapiPayload.email = payload.email;
|
|
29837
|
+
}
|
|
29838
|
+
if (payload.phoneNumber !== void 0) {
|
|
29839
|
+
strapiPayload.phoneNumber = payload.phoneNumber;
|
|
29840
|
+
}
|
|
29841
|
+
if (payload.displayName !== void 0) {
|
|
29842
|
+
if (payload.displayName) {
|
|
29843
|
+
const nameParts = payload.displayName.trim().split(" ");
|
|
29844
|
+
strapiPayload.firstName = nameParts[0] || "";
|
|
29845
|
+
strapiPayload.lastName = nameParts.slice(1).join(" ") || "";
|
|
29846
|
+
} else {
|
|
29847
|
+
strapiPayload.firstName = "";
|
|
29848
|
+
strapiPayload.lastName = "";
|
|
29849
|
+
}
|
|
29850
|
+
}
|
|
29851
|
+
if (typeof payload.disabled === "boolean") {
|
|
29852
|
+
strapiPayload.blocked = payload.disabled;
|
|
29853
|
+
}
|
|
29854
|
+
const strapiPromise = Object.keys(strapiPayload).length > 0 ? strapi2.db.query("plugin::users-permissions.user").update({
|
|
29855
|
+
where: { documentId: firebaseData.user.documentId },
|
|
29856
|
+
data: strapiPayload
|
|
29857
|
+
}) : Promise.resolve(firebaseData.user);
|
|
29858
|
+
const results = await Promise.allSettled([firebasePromise, strapiPromise]);
|
|
29859
|
+
strapi2.log.info("User update operation", {
|
|
29860
|
+
userId: entityId,
|
|
29861
|
+
firebaseStatus: results[0].status,
|
|
29862
|
+
strapiStatus: results[1].status,
|
|
29863
|
+
updatedFields: Object.keys(firebasePayload)
|
|
29864
|
+
});
|
|
29865
|
+
if (results[0].status === "rejected" || results[1].status === "rejected") {
|
|
29866
|
+
strapi2.log.error("Partial update failure detected", {
|
|
29867
|
+
userId: entityId,
|
|
29868
|
+
firebaseError: results[0].status === "rejected" ? results[0].reason : null,
|
|
29869
|
+
strapiError: results[1].status === "rejected" ? results[1].reason : null
|
|
29870
|
+
});
|
|
29871
|
+
}
|
|
29872
|
+
return results;
|
|
29823
29873
|
} catch (e) {
|
|
29874
|
+
if (e.code === "auth/email-already-exists") {
|
|
29875
|
+
throw new ValidationError$1("Email address is already in use by another account");
|
|
29876
|
+
}
|
|
29877
|
+
if (e.code === "auth/phone-number-already-exists") {
|
|
29878
|
+
throw new ValidationError$1("Phone number is already in use by another account");
|
|
29879
|
+
}
|
|
29880
|
+
if (e.code === "auth/invalid-email") {
|
|
29881
|
+
throw new ValidationError$1("Invalid email address format");
|
|
29882
|
+
}
|
|
29883
|
+
if (e.code === "auth/invalid-phone-number") {
|
|
29884
|
+
throw new ValidationError$1("Invalid phone number format");
|
|
29885
|
+
}
|
|
29824
29886
|
throw new ApplicationError$1(e.message.toString());
|
|
29825
29887
|
}
|
|
29826
29888
|
},
|
|
@@ -29838,8 +29900,8 @@ const userService = ({ strapi: strapi2 }) => {
|
|
|
29838
29900
|
if (!firebaseData?.user) {
|
|
29839
29901
|
throw new NotFoundError(`User not found for Firebase UID: ${entityId}`);
|
|
29840
29902
|
}
|
|
29841
|
-
return await strapi2.
|
|
29842
|
-
documentId: firebaseData.user.documentId,
|
|
29903
|
+
return await strapi2.db.query("plugin::users-permissions.user").update({
|
|
29904
|
+
where: { documentId: firebaseData.user.documentId },
|
|
29843
29905
|
data: payload
|
|
29844
29906
|
});
|
|
29845
29907
|
} catch (e) {
|
|
@@ -29854,8 +29916,8 @@ const userService = ({ strapi: strapi2 }) => {
|
|
|
29854
29916
|
throw new NotFoundError(`User not found for Firebase UID: ${entityId}`);
|
|
29855
29917
|
}
|
|
29856
29918
|
const firebasePromise = strapi2.firebase.auth().updateUser(entityId, payload);
|
|
29857
|
-
const strapiPromise = strapi2.
|
|
29858
|
-
documentId: firebaseData.user.documentId,
|
|
29919
|
+
const strapiPromise = strapi2.db.query("plugin::users-permissions.user").update({
|
|
29920
|
+
where: { documentId: firebaseData.user.documentId },
|
|
29859
29921
|
data: payload
|
|
29860
29922
|
});
|
|
29861
29923
|
return Promise.allSettled([firebasePromise, strapiPromise]);
|
|
@@ -29871,8 +29933,8 @@ const userService = ({ strapi: strapi2 }) => {
|
|
|
29871
29933
|
throw new NotFoundError(`User not found for Firebase UID: ${entityId}`);
|
|
29872
29934
|
}
|
|
29873
29935
|
const firebasePromise = strapi2.firebase.auth().deleteUser(entityId);
|
|
29874
|
-
const strapiPromise = strapi2.
|
|
29875
|
-
documentId: firebaseData.user.documentId
|
|
29936
|
+
const strapiPromise = strapi2.db.query("plugin::users-permissions.user").delete({
|
|
29937
|
+
where: { documentId: firebaseData.user.documentId }
|
|
29876
29938
|
});
|
|
29877
29939
|
return Promise.allSettled([firebasePromise, strapiPromise]);
|
|
29878
29940
|
} catch (e) {
|
|
@@ -29894,8 +29956,8 @@ const userService = ({ strapi: strapi2 }) => {
|
|
|
29894
29956
|
if (!firebaseData?.user) {
|
|
29895
29957
|
throw new NotFoundError(`User not found for Firebase UID: ${entityId}`);
|
|
29896
29958
|
}
|
|
29897
|
-
const response = await strapi2.
|
|
29898
|
-
documentId: firebaseData.user.documentId
|
|
29959
|
+
const response = await strapi2.db.query("plugin::users-permissions.user").delete({
|
|
29960
|
+
where: { documentId: firebaseData.user.documentId }
|
|
29899
29961
|
});
|
|
29900
29962
|
return response;
|
|
29901
29963
|
} catch (e) {
|
|
@@ -30434,8 +30496,8 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30434
30496
|
* Forgot password flow - sends reset email
|
|
30435
30497
|
* Public endpoint that sends a Firebase-hosted password reset email using Firebase's secure hosted UI
|
|
30436
30498
|
*/
|
|
30437
|
-
forgotPassword: async (
|
|
30438
|
-
|
|
30499
|
+
forgotPassword: async (email2) => {
|
|
30500
|
+
strapi2.log.info(`[forgotPassword] Starting password reset for email: ${email2}`);
|
|
30439
30501
|
if (!email2) {
|
|
30440
30502
|
throw new ValidationError$1("Email is required");
|
|
30441
30503
|
}
|
|
@@ -30476,8 +30538,16 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30476
30538
|
});
|
|
30477
30539
|
}
|
|
30478
30540
|
if (!strapiUser) {
|
|
30541
|
+
strapi2.log.warn(`⚠️ [forgotPassword] User not found for email: ${email2}`);
|
|
30479
30542
|
return { message: "If an account with that email exists, a password reset link has been sent." };
|
|
30480
30543
|
}
|
|
30544
|
+
strapi2.log.info(
|
|
30545
|
+
`✅ [forgotPassword] User found: ${JSON.stringify({
|
|
30546
|
+
documentId: strapiUser.documentId,
|
|
30547
|
+
email: strapiUser.email,
|
|
30548
|
+
firebaseUID: firebaseUser?.uid || "not in Firebase"
|
|
30549
|
+
})}`
|
|
30550
|
+
);
|
|
30481
30551
|
const actionCodeSettings = {
|
|
30482
30552
|
url: resetUrl,
|
|
30483
30553
|
// Continue URL after reset completes on Firebase's page
|
|
@@ -30491,6 +30561,7 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30491
30561
|
});
|
|
30492
30562
|
let resetLink;
|
|
30493
30563
|
try {
|
|
30564
|
+
strapi2.log.info(`[forgotPassword] Generating Firebase password reset link for: ${strapiUser.email}`);
|
|
30494
30565
|
resetLink = await Promise.race([
|
|
30495
30566
|
strapi2.firebase.auth().generatePasswordResetLink(strapiUser.email, actionCodeSettings),
|
|
30496
30567
|
timeoutPromise
|
|
@@ -30502,12 +30573,22 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30502
30573
|
strapi2.log.error(`❌ Failed to generate password reset link for ${strapiUser.email}:`, error2);
|
|
30503
30574
|
throw error2;
|
|
30504
30575
|
}
|
|
30576
|
+
strapi2.log.info(`[forgotPassword] Attempting to send password reset email to: ${strapiUser.email}`);
|
|
30505
30577
|
await strapi2.plugin("firebase-authentication").service("emailService").sendPasswordResetEmail(strapiUser, resetLink);
|
|
30578
|
+
strapi2.log.info(`✅ [forgotPassword] Password reset email sent successfully to: ${strapiUser.email}`);
|
|
30506
30579
|
return {
|
|
30507
30580
|
message: "If an account with that email exists, a password reset link has been sent."
|
|
30508
30581
|
};
|
|
30509
30582
|
} catch (error2) {
|
|
30510
|
-
strapi2.log.error(
|
|
30583
|
+
strapi2.log.error(
|
|
30584
|
+
`❌ [forgotPassword] ERROR: ${JSON.stringify({
|
|
30585
|
+
email: email2,
|
|
30586
|
+
message: error2.message,
|
|
30587
|
+
code: error2.code,
|
|
30588
|
+
name: error2.name,
|
|
30589
|
+
stack: error2.stack
|
|
30590
|
+
})}`
|
|
30591
|
+
);
|
|
30511
30592
|
return {
|
|
30512
30593
|
message: "If an account with that email exists, a password reset link has been sent."
|
|
30513
30594
|
};
|
|
@@ -30527,13 +30608,10 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30527
30608
|
*
|
|
30528
30609
|
* NOT used for forgot password email flow - that now uses Firebase's hosted UI
|
|
30529
30610
|
*/
|
|
30530
|
-
resetPassword: async (
|
|
30531
|
-
const { password } = ctx.request.body;
|
|
30532
|
-
const populate2 = ctx.request.query.populate || [];
|
|
30611
|
+
resetPassword: async (password, token, populate2) => {
|
|
30533
30612
|
if (!password) {
|
|
30534
30613
|
throw new ValidationError$1("Password is required");
|
|
30535
30614
|
}
|
|
30536
|
-
const token = ctx.request.header.authorization?.replace("Bearer ", "");
|
|
30537
30615
|
if (!token) {
|
|
30538
30616
|
throw new UnauthorizedError("Authorization token is required");
|
|
30539
30617
|
}
|
|
@@ -30589,8 +30667,7 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30589
30667
|
* Generates a sign-in link using Firebase Admin SDK
|
|
30590
30668
|
* Note: Verification requires client-side completion
|
|
30591
30669
|
*/
|
|
30592
|
-
async requestMagicLink(
|
|
30593
|
-
const { email: email2 } = ctx.request.body;
|
|
30670
|
+
async requestMagicLink(email2) {
|
|
30594
30671
|
if (!email2 || typeof email2 !== "string") {
|
|
30595
30672
|
throw new ValidationError$1("Valid email is required");
|
|
30596
30673
|
}
|
|
@@ -30620,7 +30697,7 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30620
30697
|
};
|
|
30621
30698
|
const magicLink = await strapi2.firebase.auth().generateSignInWithEmailLink(email2, actionCodeSettings);
|
|
30622
30699
|
if (process.env.NODE_ENV !== "production") {
|
|
30623
|
-
strapi2.log.info("
|
|
30700
|
+
strapi2.log.info("Magic Link Generation Request:");
|
|
30624
30701
|
strapi2.log.info(` Email: ${email2}`);
|
|
30625
30702
|
strapi2.log.info(` Verification URL: ${magicLinkUrl}`);
|
|
30626
30703
|
strapi2.log.info(` Expires: ${config2.magicLinkExpiryHours || 1} hour(s)`);
|
|
@@ -31226,7 +31303,7 @@ class EmailService {
|
|
|
31226
31303
|
const template = await templateService2.getTemplate("magicLink");
|
|
31227
31304
|
const compiledSubject = _$1.template(template.subject)(completeVariables);
|
|
31228
31305
|
strapi.log.info("\n" + "=".repeat(80));
|
|
31229
|
-
strapi.log.info("
|
|
31306
|
+
strapi.log.info("MAGIC LINK EMAIL (Development Mode)");
|
|
31230
31307
|
strapi.log.info("=".repeat(80));
|
|
31231
31308
|
strapi.log.info(`To: ${email2}`);
|
|
31232
31309
|
strapi.log.info(`Subject: ${compiledSubject}`);
|
|
@@ -31301,7 +31378,7 @@ const firebaseUserDataService = ({ strapi: strapi2 }) => ({
|
|
|
31301
31378
|
}
|
|
31302
31379
|
});
|
|
31303
31380
|
} catch (error2) {
|
|
31304
|
-
if (error2.code === "23505") {
|
|
31381
|
+
if (error2.code === "23505" || error2.name === "ValidationError") {
|
|
31305
31382
|
strapi2.log.warn(`Race condition detected for user ${userId}, retrying findFirst`);
|
|
31306
31383
|
firebaseData = await strapi2.documents("plugin::firebase-authentication.firebase-user-data").findFirst({
|
|
31307
31384
|
filters: { user: { documentId: { $eq: userId } } }
|
|
@@ -31341,7 +31418,7 @@ const autoLinkService = {
|
|
|
31341
31418
|
errors: 0
|
|
31342
31419
|
};
|
|
31343
31420
|
try {
|
|
31344
|
-
strapi2.log.info("
|
|
31421
|
+
strapi2.log.info("Auto-linking Strapi users with Firebase users...");
|
|
31345
31422
|
if (!strapi2.firebase) {
|
|
31346
31423
|
strapi2.log.warn("Firebase not initialized - skipping auto-linking");
|
|
31347
31424
|
return result;
|
|
@@ -31380,6 +31457,7 @@ const autoLinkService = {
|
|
|
31380
31457
|
}
|
|
31381
31458
|
}
|
|
31382
31459
|
for (const strapiUser of strapiUsers) {
|
|
31460
|
+
let firebaseUser = null;
|
|
31383
31461
|
try {
|
|
31384
31462
|
const existing = await strapi2.db.query("plugin::firebase-authentication.firebase-user-data").findOne({
|
|
31385
31463
|
select: ["id"],
|
|
@@ -31389,7 +31467,7 @@ const autoLinkService = {
|
|
|
31389
31467
|
result.skipped++;
|
|
31390
31468
|
continue;
|
|
31391
31469
|
}
|
|
31392
|
-
|
|
31470
|
+
firebaseUser = null;
|
|
31393
31471
|
if (strapiUser.email) {
|
|
31394
31472
|
firebaseUser = firebaseByEmail.get(strapiUser.email.toLowerCase());
|
|
31395
31473
|
}
|
|
@@ -31400,6 +31478,23 @@ const autoLinkService = {
|
|
|
31400
31478
|
result.skipped++;
|
|
31401
31479
|
continue;
|
|
31402
31480
|
}
|
|
31481
|
+
const existingUIDLink = await strapi2.db.query("plugin::firebase-authentication.firebase-user-data").findOne({
|
|
31482
|
+
where: { firebaseUserID: firebaseUser.uid },
|
|
31483
|
+
populate: ["user"]
|
|
31484
|
+
});
|
|
31485
|
+
if (existingUIDLink && existingUIDLink.user?.documentId !== strapiUser.documentId) {
|
|
31486
|
+
strapi2.log.warn(
|
|
31487
|
+
`⚠️ [Auto-Link] Skipping '${strapiUser.username || strapiUser.email}' - Firebase UID already linked`
|
|
31488
|
+
);
|
|
31489
|
+
strapi2.log.warn(` Strapi User: ${strapiUser.email} (documentId: ${strapiUser.documentId})`);
|
|
31490
|
+
strapi2.log.warn(` Firebase UID: ${firebaseUser.uid}`);
|
|
31491
|
+
strapi2.log.warn(
|
|
31492
|
+
` Already linked to: ${existingUIDLink.user?.email} (documentId: ${existingUIDLink.user?.documentId})`
|
|
31493
|
+
);
|
|
31494
|
+
strapi2.log.warn(` Action: Resolve duplicate users or manually unlink conflicting record`);
|
|
31495
|
+
result.skipped++;
|
|
31496
|
+
continue;
|
|
31497
|
+
}
|
|
31403
31498
|
try {
|
|
31404
31499
|
const firebaseData = {
|
|
31405
31500
|
firebaseUserID: firebaseUser.uid
|
|
@@ -31418,18 +31513,42 @@ const autoLinkService = {
|
|
|
31418
31513
|
throw createError;
|
|
31419
31514
|
}
|
|
31420
31515
|
} catch (error2) {
|
|
31421
|
-
|
|
31422
|
-
|
|
31516
|
+
if (error2.message?.includes("This attribute must be unique")) {
|
|
31517
|
+
strapi2.log.warn(
|
|
31518
|
+
`⚠️ [Auto-Link] Unique constraint conflict for '${strapiUser.username || strapiUser.email}'`
|
|
31519
|
+
);
|
|
31520
|
+
strapi2.log.warn(` Strapi User: ${strapiUser.email} (documentId: ${strapiUser.documentId})`);
|
|
31521
|
+
if (firebaseUser) {
|
|
31522
|
+
strapi2.log.warn(` Firebase UID: ${firebaseUser.uid}`);
|
|
31523
|
+
strapi2.log.warn(` Cause: This Firebase UID is already linked to another Strapi user`);
|
|
31524
|
+
strapi2.log.warn(
|
|
31525
|
+
` Action: Query firebase_user_data table to find conflict - WHERE firebase_user_id = '${firebaseUser.uid}'`
|
|
31526
|
+
);
|
|
31527
|
+
}
|
|
31528
|
+
result.skipped++;
|
|
31529
|
+
} else {
|
|
31530
|
+
result.errors++;
|
|
31531
|
+
strapi2.log.error(
|
|
31532
|
+
`❌ [Auto-Link] Unexpected error linking user '${strapiUser.username || strapiUser.email}': ${error2.message}`
|
|
31533
|
+
);
|
|
31534
|
+
}
|
|
31423
31535
|
}
|
|
31424
31536
|
}
|
|
31425
|
-
|
|
31426
|
-
|
|
31427
|
-
|
|
31537
|
+
if (result.errors > 0) {
|
|
31538
|
+
strapi2.log.error(
|
|
31539
|
+
`❌ Auto-linking completed with unexpected errors: ${result.linked} linked, ${result.skipped} skipped, ${result.errors} errors`
|
|
31540
|
+
);
|
|
31541
|
+
strapi2.log.error(` Review error logs above for resolution steps`);
|
|
31542
|
+
} else {
|
|
31543
|
+
strapi2.log.info(
|
|
31544
|
+
`✅ Auto-linking complete: ${result.linked} linked, ${result.skipped} skipped, ${result.errors} errors`
|
|
31545
|
+
);
|
|
31546
|
+
}
|
|
31428
31547
|
strapi2.log.info(
|
|
31429
31548
|
` Total: ${result.totalStrapiUsers} Strapi users, ${result.totalFirebaseUsers} Firebase users`
|
|
31430
31549
|
);
|
|
31431
31550
|
} catch (error2) {
|
|
31432
|
-
strapi2.log.error(
|
|
31551
|
+
strapi2.log.error(`❌ Fatal error during auto-linking: ${error2.message}`);
|
|
31433
31552
|
}
|
|
31434
31553
|
return result;
|
|
31435
31554
|
}
|
|
@@ -22,7 +22,6 @@ declare const _default: {
|
|
|
22
22
|
controllers: {
|
|
23
23
|
firebaseController: {
|
|
24
24
|
validateToken(ctx: any): Promise<any>;
|
|
25
|
-
createAlias(ctx: any): Promise<void>;
|
|
26
25
|
deleteByEmail(email: any): Promise<any>;
|
|
27
26
|
overrideAccess(ctx: any): Promise<any>;
|
|
28
27
|
emailLogin(ctx: any): Promise<void>;
|
|
@@ -94,7 +93,7 @@ declare const _default: {
|
|
|
94
93
|
magicLinkEmailSubject: any;
|
|
95
94
|
magicLinkExpiryHours: any;
|
|
96
95
|
}>;
|
|
97
|
-
setFirebaseConfigJson(
|
|
96
|
+
setFirebaseConfigJson(requestBody: any): Promise<any>;
|
|
98
97
|
delFirebaseConfigJson: () => Promise<any>;
|
|
99
98
|
updateMagicLinkSettings(settings: any): Promise<{
|
|
100
99
|
enableMagicLink: any;
|
|
@@ -120,7 +119,7 @@ declare const _default: {
|
|
|
120
119
|
};
|
|
121
120
|
}>;
|
|
122
121
|
updateFirebaseUser: (entityId: any, payload: any) => Promise<any>;
|
|
123
|
-
update: (entityId: any, payload: any) => Promise<[PromiseSettledResult<any>]>;
|
|
122
|
+
update: (entityId: any, payload: any) => Promise<[PromiseSettledResult<any>, PromiseSettledResult<any>]>;
|
|
124
123
|
resetPasswordFirebaseUser: (entityId: any, payload: any) => Promise<any>;
|
|
125
124
|
resetPasswordStrapiUser: (entityId: any, payload: any) => Promise<any>;
|
|
126
125
|
resetPassword: (entityId: any, payload: any) => Promise<[PromiseSettledResult<any>, PromiseSettledResult<any>]>;
|
|
@@ -156,14 +155,14 @@ declare const _default: {
|
|
|
156
155
|
user: any;
|
|
157
156
|
jwt: any;
|
|
158
157
|
}>;
|
|
159
|
-
forgotPassword: (
|
|
158
|
+
forgotPassword: (email: string) => Promise<{
|
|
160
159
|
message: string;
|
|
161
160
|
}>;
|
|
162
|
-
resetPassword: (
|
|
161
|
+
resetPassword: (password: string, token: string, populate: any[]) => Promise<{
|
|
163
162
|
user: any;
|
|
164
163
|
jwt: any;
|
|
165
164
|
}>;
|
|
166
|
-
requestMagicLink(
|
|
165
|
+
requestMagicLink(email: string): Promise<{
|
|
167
166
|
debug: {
|
|
168
167
|
linkSent: any;
|
|
169
168
|
email: string;
|
|
@@ -68,7 +68,7 @@ declare const _default: ({ strapi }: {
|
|
|
68
68
|
* Forgot password flow - sends reset email
|
|
69
69
|
* Public endpoint that sends a Firebase-hosted password reset email using Firebase's secure hosted UI
|
|
70
70
|
*/
|
|
71
|
-
forgotPassword: (
|
|
71
|
+
forgotPassword: (email: string) => Promise<{
|
|
72
72
|
message: string;
|
|
73
73
|
}>;
|
|
74
74
|
/**
|
|
@@ -85,7 +85,7 @@ declare const _default: ({ strapi }: {
|
|
|
85
85
|
*
|
|
86
86
|
* NOT used for forgot password email flow - that now uses Firebase's hosted UI
|
|
87
87
|
*/
|
|
88
|
-
resetPassword: (
|
|
88
|
+
resetPassword: (password: string, token: string, populate: any[]) => Promise<{
|
|
89
89
|
user: any;
|
|
90
90
|
jwt: any;
|
|
91
91
|
}>;
|
|
@@ -94,7 +94,7 @@ declare const _default: ({ strapi }: {
|
|
|
94
94
|
* Generates a sign-in link using Firebase Admin SDK
|
|
95
95
|
* Note: Verification requires client-side completion
|
|
96
96
|
*/
|
|
97
|
-
requestMagicLink(
|
|
97
|
+
requestMagicLink(email: string): Promise<{
|
|
98
98
|
debug: {
|
|
99
99
|
linkSent: any;
|
|
100
100
|
email: string;
|