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.
- package/dist/_chunks/{App-C49N6om4.mjs → App-C0XJYDQh.mjs} +34 -98
- package/dist/_chunks/{App-CtjmoTFU.js → App-COMAshYj.js} +34 -98
- package/dist/_chunks/{api-DrAXGM3H.mjs → api-CM79I13t.mjs} +1 -1
- package/dist/_chunks/{api-Bw_7tM52.js → api-DzbiYzK4.js} +1 -1
- package/dist/_chunks/{index-FAW4iPgh.mjs → index-4s0RQKAx.mjs} +2 -2
- package/dist/_chunks/{index-C9AUBuP7.mjs → index-B8sQntQh.mjs} +1 -1
- package/dist/_chunks/{index-yE1zATuU.js → index-DHMYqfl3.js} +2 -2
- package/dist/_chunks/{index-BkQ4pF_p.js → index-DyXjIUy8.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/forms/index.d.ts +2 -4
- package/dist/server/index.js +110 -52
- package/dist/server/index.mjs +110 -52
- 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 +4 -5
- package/dist/server/src/services/firebaseService.d.ts +3 -3
- package/dist/server/src/services/index.d.ts +4 -4
- package/dist/server/src/services/settingsService.d.ts +1 -2
- 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,12 +29449,12 @@ 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);
|
|
@@ -29471,13 +29473,12 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29471
29473
|
return res;
|
|
29472
29474
|
} catch (error2) {
|
|
29473
29475
|
strapi2.log.error("=== FIREBASE CONFIG SAVE ERROR ===");
|
|
29474
|
-
strapi2.log.error(
|
|
29475
|
-
strapi2.log.error(
|
|
29476
|
-
strapi2.log.error(
|
|
29476
|
+
strapi2.log.error(`Error name: ${error2.name}`);
|
|
29477
|
+
strapi2.log.error(`Error message: ${error2.message}`);
|
|
29478
|
+
strapi2.log.error(`Error stack: ${error2.stack}`);
|
|
29477
29479
|
try {
|
|
29478
|
-
|
|
29479
|
-
|
|
29480
|
-
strapi2.log.error("Request body keys:", Object.keys(body));
|
|
29480
|
+
if (requestBody) {
|
|
29481
|
+
strapi2.log.error(`Request body keys: ${JSON.stringify(Object.keys(requestBody))}`);
|
|
29481
29482
|
}
|
|
29482
29483
|
} catch (e) {
|
|
29483
29484
|
strapi2.log.error("Could not access request body");
|
|
@@ -29498,7 +29499,7 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29498
29499
|
try {
|
|
29499
29500
|
strapi2.log.debug("delFirebaseConfigJson called");
|
|
29500
29501
|
const isExist = await strapi2.db.query(CONFIG_CONTENT_TYPE).findOne({ where: {} });
|
|
29501
|
-
strapi2.log.debug(
|
|
29502
|
+
strapi2.log.debug(`Config exists: ${!!isExist}`);
|
|
29502
29503
|
if (!isExist) {
|
|
29503
29504
|
strapi2.log.info(ERROR_MESSAGES.DELETION_NO_CONFIG);
|
|
29504
29505
|
return null;
|
|
@@ -29506,12 +29507,12 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29506
29507
|
const res = await strapi2.db.query(CONFIG_CONTENT_TYPE).delete({
|
|
29507
29508
|
where: { id: isExist.id }
|
|
29508
29509
|
});
|
|
29509
|
-
strapi2.log.debug(
|
|
29510
|
+
strapi2.log.debug(`Delete result: ${JSON.stringify(res)}`);
|
|
29510
29511
|
await strapi2.plugin("firebase-authentication").service("settingsService").init();
|
|
29511
29512
|
strapi2.log.info(SUCCESS_MESSAGES.FIREBASE_CONFIG_DELETED);
|
|
29512
29513
|
return res;
|
|
29513
29514
|
} catch (error2) {
|
|
29514
|
-
strapi2.log.error(
|
|
29515
|
+
strapi2.log.error(`delFirebaseConfigJson error: ${error2.message}`);
|
|
29515
29516
|
throw new ApplicationError2(ERROR_MESSAGES.SOMETHING_WENT_WRONG, {
|
|
29516
29517
|
error: error2
|
|
29517
29518
|
});
|
|
@@ -30434,8 +30435,8 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30434
30435
|
* Forgot password flow - sends reset email
|
|
30435
30436
|
* Public endpoint that sends a Firebase-hosted password reset email using Firebase's secure hosted UI
|
|
30436
30437
|
*/
|
|
30437
|
-
forgotPassword: async (
|
|
30438
|
-
|
|
30438
|
+
forgotPassword: async (email2) => {
|
|
30439
|
+
strapi2.log.info(`[forgotPassword] Starting password reset for email: ${email2}`);
|
|
30439
30440
|
if (!email2) {
|
|
30440
30441
|
throw new ValidationError$1("Email is required");
|
|
30441
30442
|
}
|
|
@@ -30476,8 +30477,16 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30476
30477
|
});
|
|
30477
30478
|
}
|
|
30478
30479
|
if (!strapiUser) {
|
|
30480
|
+
strapi2.log.warn(`⚠️ [forgotPassword] User not found for email: ${email2}`);
|
|
30479
30481
|
return { message: "If an account with that email exists, a password reset link has been sent." };
|
|
30480
30482
|
}
|
|
30483
|
+
strapi2.log.info(
|
|
30484
|
+
`✅ [forgotPassword] User found: ${JSON.stringify({
|
|
30485
|
+
documentId: strapiUser.documentId,
|
|
30486
|
+
email: strapiUser.email,
|
|
30487
|
+
firebaseUID: firebaseUser?.uid || "not in Firebase"
|
|
30488
|
+
})}`
|
|
30489
|
+
);
|
|
30481
30490
|
const actionCodeSettings = {
|
|
30482
30491
|
url: resetUrl,
|
|
30483
30492
|
// Continue URL after reset completes on Firebase's page
|
|
@@ -30491,6 +30500,7 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30491
30500
|
});
|
|
30492
30501
|
let resetLink;
|
|
30493
30502
|
try {
|
|
30503
|
+
strapi2.log.info(`[forgotPassword] Generating Firebase password reset link for: ${strapiUser.email}`);
|
|
30494
30504
|
resetLink = await Promise.race([
|
|
30495
30505
|
strapi2.firebase.auth().generatePasswordResetLink(strapiUser.email, actionCodeSettings),
|
|
30496
30506
|
timeoutPromise
|
|
@@ -30502,12 +30512,22 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30502
30512
|
strapi2.log.error(`❌ Failed to generate password reset link for ${strapiUser.email}:`, error2);
|
|
30503
30513
|
throw error2;
|
|
30504
30514
|
}
|
|
30515
|
+
strapi2.log.info(`[forgotPassword] Attempting to send password reset email to: ${strapiUser.email}`);
|
|
30505
30516
|
await strapi2.plugin("firebase-authentication").service("emailService").sendPasswordResetEmail(strapiUser, resetLink);
|
|
30517
|
+
strapi2.log.info(`✅ [forgotPassword] Password reset email sent successfully to: ${strapiUser.email}`);
|
|
30506
30518
|
return {
|
|
30507
30519
|
message: "If an account with that email exists, a password reset link has been sent."
|
|
30508
30520
|
};
|
|
30509
30521
|
} catch (error2) {
|
|
30510
|
-
strapi2.log.error(
|
|
30522
|
+
strapi2.log.error(
|
|
30523
|
+
`❌ [forgotPassword] ERROR: ${JSON.stringify({
|
|
30524
|
+
email: email2,
|
|
30525
|
+
message: error2.message,
|
|
30526
|
+
code: error2.code,
|
|
30527
|
+
name: error2.name,
|
|
30528
|
+
stack: error2.stack
|
|
30529
|
+
})}`
|
|
30530
|
+
);
|
|
30511
30531
|
return {
|
|
30512
30532
|
message: "If an account with that email exists, a password reset link has been sent."
|
|
30513
30533
|
};
|
|
@@ -30527,13 +30547,10 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30527
30547
|
*
|
|
30528
30548
|
* NOT used for forgot password email flow - that now uses Firebase's hosted UI
|
|
30529
30549
|
*/
|
|
30530
|
-
resetPassword: async (
|
|
30531
|
-
const { password } = ctx.request.body;
|
|
30532
|
-
const populate2 = ctx.request.query.populate || [];
|
|
30550
|
+
resetPassword: async (password, token, populate2) => {
|
|
30533
30551
|
if (!password) {
|
|
30534
30552
|
throw new ValidationError$1("Password is required");
|
|
30535
30553
|
}
|
|
30536
|
-
const token = ctx.request.header.authorization?.replace("Bearer ", "");
|
|
30537
30554
|
if (!token) {
|
|
30538
30555
|
throw new UnauthorizedError("Authorization token is required");
|
|
30539
30556
|
}
|
|
@@ -30589,8 +30606,7 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30589
30606
|
* Generates a sign-in link using Firebase Admin SDK
|
|
30590
30607
|
* Note: Verification requires client-side completion
|
|
30591
30608
|
*/
|
|
30592
|
-
async requestMagicLink(
|
|
30593
|
-
const { email: email2 } = ctx.request.body;
|
|
30609
|
+
async requestMagicLink(email2) {
|
|
30594
30610
|
if (!email2 || typeof email2 !== "string") {
|
|
30595
30611
|
throw new ValidationError$1("Valid email is required");
|
|
30596
30612
|
}
|
|
@@ -30620,7 +30636,7 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30620
30636
|
};
|
|
30621
30637
|
const magicLink = await strapi2.firebase.auth().generateSignInWithEmailLink(email2, actionCodeSettings);
|
|
30622
30638
|
if (process.env.NODE_ENV !== "production") {
|
|
30623
|
-
strapi2.log.info("
|
|
30639
|
+
strapi2.log.info("Magic Link Generation Request:");
|
|
30624
30640
|
strapi2.log.info(` Email: ${email2}`);
|
|
30625
30641
|
strapi2.log.info(` Verification URL: ${magicLinkUrl}`);
|
|
30626
30642
|
strapi2.log.info(` Expires: ${config2.magicLinkExpiryHours || 1} hour(s)`);
|
|
@@ -31226,7 +31242,7 @@ class EmailService {
|
|
|
31226
31242
|
const template = await templateService2.getTemplate("magicLink");
|
|
31227
31243
|
const compiledSubject = _$1.template(template.subject)(completeVariables);
|
|
31228
31244
|
strapi.log.info("\n" + "=".repeat(80));
|
|
31229
|
-
strapi.log.info("
|
|
31245
|
+
strapi.log.info("MAGIC LINK EMAIL (Development Mode)");
|
|
31230
31246
|
strapi.log.info("=".repeat(80));
|
|
31231
31247
|
strapi.log.info(`To: ${email2}`);
|
|
31232
31248
|
strapi.log.info(`Subject: ${compiledSubject}`);
|
|
@@ -31341,7 +31357,7 @@ const autoLinkService = {
|
|
|
31341
31357
|
errors: 0
|
|
31342
31358
|
};
|
|
31343
31359
|
try {
|
|
31344
|
-
strapi2.log.info("
|
|
31360
|
+
strapi2.log.info("Auto-linking Strapi users with Firebase users...");
|
|
31345
31361
|
if (!strapi2.firebase) {
|
|
31346
31362
|
strapi2.log.warn("Firebase not initialized - skipping auto-linking");
|
|
31347
31363
|
return result;
|
|
@@ -31380,6 +31396,7 @@ const autoLinkService = {
|
|
|
31380
31396
|
}
|
|
31381
31397
|
}
|
|
31382
31398
|
for (const strapiUser of strapiUsers) {
|
|
31399
|
+
let firebaseUser = null;
|
|
31383
31400
|
try {
|
|
31384
31401
|
const existing = await strapi2.db.query("plugin::firebase-authentication.firebase-user-data").findOne({
|
|
31385
31402
|
select: ["id"],
|
|
@@ -31389,7 +31406,7 @@ const autoLinkService = {
|
|
|
31389
31406
|
result.skipped++;
|
|
31390
31407
|
continue;
|
|
31391
31408
|
}
|
|
31392
|
-
|
|
31409
|
+
firebaseUser = null;
|
|
31393
31410
|
if (strapiUser.email) {
|
|
31394
31411
|
firebaseUser = firebaseByEmail.get(strapiUser.email.toLowerCase());
|
|
31395
31412
|
}
|
|
@@ -31400,6 +31417,23 @@ const autoLinkService = {
|
|
|
31400
31417
|
result.skipped++;
|
|
31401
31418
|
continue;
|
|
31402
31419
|
}
|
|
31420
|
+
const existingUIDLink = await strapi2.db.query("plugin::firebase-authentication.firebase-user-data").findOne({
|
|
31421
|
+
where: { firebaseUserID: firebaseUser.uid },
|
|
31422
|
+
populate: ["user"]
|
|
31423
|
+
});
|
|
31424
|
+
if (existingUIDLink && existingUIDLink.user?.documentId !== strapiUser.documentId) {
|
|
31425
|
+
strapi2.log.warn(
|
|
31426
|
+
`⚠️ [Auto-Link] Skipping '${strapiUser.username || strapiUser.email}' - Firebase UID already linked`
|
|
31427
|
+
);
|
|
31428
|
+
strapi2.log.warn(` Strapi User: ${strapiUser.email} (documentId: ${strapiUser.documentId})`);
|
|
31429
|
+
strapi2.log.warn(` Firebase UID: ${firebaseUser.uid}`);
|
|
31430
|
+
strapi2.log.warn(
|
|
31431
|
+
` Already linked to: ${existingUIDLink.user?.email} (documentId: ${existingUIDLink.user?.documentId})`
|
|
31432
|
+
);
|
|
31433
|
+
strapi2.log.warn(` Action: Resolve duplicate users or manually unlink conflicting record`);
|
|
31434
|
+
result.skipped++;
|
|
31435
|
+
continue;
|
|
31436
|
+
}
|
|
31403
31437
|
try {
|
|
31404
31438
|
const firebaseData = {
|
|
31405
31439
|
firebaseUserID: firebaseUser.uid
|
|
@@ -31418,18 +31452,42 @@ const autoLinkService = {
|
|
|
31418
31452
|
throw createError;
|
|
31419
31453
|
}
|
|
31420
31454
|
} catch (error2) {
|
|
31421
|
-
|
|
31422
|
-
|
|
31455
|
+
if (error2.message?.includes("This attribute must be unique")) {
|
|
31456
|
+
strapi2.log.warn(
|
|
31457
|
+
`⚠️ [Auto-Link] Unique constraint conflict for '${strapiUser.username || strapiUser.email}'`
|
|
31458
|
+
);
|
|
31459
|
+
strapi2.log.warn(` Strapi User: ${strapiUser.email} (documentId: ${strapiUser.documentId})`);
|
|
31460
|
+
if (firebaseUser) {
|
|
31461
|
+
strapi2.log.warn(` Firebase UID: ${firebaseUser.uid}`);
|
|
31462
|
+
strapi2.log.warn(` Cause: This Firebase UID is already linked to another Strapi user`);
|
|
31463
|
+
strapi2.log.warn(
|
|
31464
|
+
` Action: Query firebase_user_data table to find conflict - WHERE firebase_user_id = '${firebaseUser.uid}'`
|
|
31465
|
+
);
|
|
31466
|
+
}
|
|
31467
|
+
result.skipped++;
|
|
31468
|
+
} else {
|
|
31469
|
+
result.errors++;
|
|
31470
|
+
strapi2.log.error(
|
|
31471
|
+
`❌ [Auto-Link] Unexpected error linking user '${strapiUser.username || strapiUser.email}': ${error2.message}`
|
|
31472
|
+
);
|
|
31473
|
+
}
|
|
31423
31474
|
}
|
|
31424
31475
|
}
|
|
31425
|
-
|
|
31426
|
-
|
|
31427
|
-
|
|
31476
|
+
if (result.errors > 0) {
|
|
31477
|
+
strapi2.log.error(
|
|
31478
|
+
`❌ Auto-linking completed with unexpected errors: ${result.linked} linked, ${result.skipped} skipped, ${result.errors} errors`
|
|
31479
|
+
);
|
|
31480
|
+
strapi2.log.error(` Review error logs above for resolution steps`);
|
|
31481
|
+
} else {
|
|
31482
|
+
strapi2.log.info(
|
|
31483
|
+
`✅ Auto-linking complete: ${result.linked} linked, ${result.skipped} skipped, ${result.errors} errors`
|
|
31484
|
+
);
|
|
31485
|
+
}
|
|
31428
31486
|
strapi2.log.info(
|
|
31429
31487
|
` Total: ${result.totalStrapiUsers} Strapi users, ${result.totalFirebaseUsers} Firebase users`
|
|
31430
31488
|
);
|
|
31431
31489
|
} catch (error2) {
|
|
31432
|
-
strapi2.log.error(
|
|
31490
|
+
strapi2.log.error(`❌ Fatal error during auto-linking: ${error2.message}`);
|
|
31433
31491
|
}
|
|
31434
31492
|
return result;
|
|
31435
31493
|
}
|
|
@@ -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;
|
|
@@ -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;
|
|
@@ -16,7 +16,7 @@ declare const _default: {
|
|
|
16
16
|
magicLinkEmailSubject: any;
|
|
17
17
|
magicLinkExpiryHours: any;
|
|
18
18
|
}>;
|
|
19
|
-
setFirebaseConfigJson(
|
|
19
|
+
setFirebaseConfigJson(requestBody: any): Promise<any>;
|
|
20
20
|
delFirebaseConfigJson: () => Promise<any>;
|
|
21
21
|
updateMagicLinkSettings(settings: any): Promise<{
|
|
22
22
|
enableMagicLink: any;
|
|
@@ -78,14 +78,14 @@ declare const _default: {
|
|
|
78
78
|
user: any;
|
|
79
79
|
jwt: any;
|
|
80
80
|
}>;
|
|
81
|
-
forgotPassword: (
|
|
81
|
+
forgotPassword: (email: string) => Promise<{
|
|
82
82
|
message: string;
|
|
83
83
|
}>;
|
|
84
|
-
resetPassword: (
|
|
84
|
+
resetPassword: (password: string, token: string, populate: any[]) => Promise<{
|
|
85
85
|
user: any;
|
|
86
86
|
jwt: any;
|
|
87
87
|
}>;
|
|
88
|
-
requestMagicLink(
|
|
88
|
+
requestMagicLink(email: string): Promise<{
|
|
89
89
|
debug: {
|
|
90
90
|
linkSent: any;
|
|
91
91
|
email: string;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Context, DefaultContext } from "koa";
|
|
2
1
|
declare const _default: ({ strapi }: {
|
|
3
2
|
strapi: any;
|
|
4
3
|
}) => {
|
|
@@ -52,7 +51,7 @@ declare const _default: ({ strapi }: {
|
|
|
52
51
|
* The service account JSON is encrypted using AES before storage,
|
|
53
52
|
* while the Web API key and password settings are stored in plain text.
|
|
54
53
|
*/
|
|
55
|
-
setFirebaseConfigJson(
|
|
54
|
+
setFirebaseConfigJson(requestBody: any): Promise<any>;
|
|
56
55
|
delFirebaseConfigJson: () => Promise<any>;
|
|
57
56
|
/**
|
|
58
57
|
* Updates only the magic link settings without affecting other configuration
|
package/package.json
CHANGED