strapi-plugin-firebase-authentication 1.1.0 → 1.1.7
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/README.md +51 -17
- package/dist/_chunks/{App-Bl6D4TFu.mjs → App-C49N6om4.mjs} +208 -134
- package/dist/_chunks/{App-HfsY_18f.js → App-CtjmoTFU.js} +136 -24
- package/dist/_chunks/{api-BSejy8nn.js → api-Bw_7tM52.js} +1 -1
- package/dist/_chunks/{api-B01IAVEC.mjs → api-DrAXGM3H.mjs} +1 -1
- package/dist/_chunks/{index-BbVqBI3M.js → index-BkQ4pF_p.js} +1 -1
- package/dist/_chunks/{index-BqF9RRVF.mjs → index-C9AUBuP7.mjs} +1 -1
- package/dist/_chunks/{index-4hUrKd7Y.mjs → index-FAW4iPgh.mjs} +2 -2
- package/dist/_chunks/{index-DgfRCyyQ.js → index-yE1zATuU.js} +2 -2
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/forms/index.d.ts +4 -2
- package/dist/server/index.js +153 -145
- package/dist/server/index.mjs +153 -145
- package/dist/server/src/controllers/firebaseController.d.ts +4 -2
- package/dist/server/src/controllers/index.d.ts +0 -1
- package/dist/server/src/controllers/settingsController.d.ts +0 -1
- package/dist/server/src/index.d.ts +3 -11
- package/dist/server/src/routes/index.d.ts +0 -10
- package/dist/server/src/services/firebaseService.d.ts +12 -2
- package/package.json +11 -10
- package/dist/server/src/routes/content-internal-api.d.ts +0 -11
package/dist/server/index.mjs
CHANGED
|
@@ -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("validateToken controller error:", error2);
|
|
413
|
+
strapi.log.error("validateToken controller error:", error2.message);
|
|
414
414
|
if (error2.name === "ValidationError") {
|
|
415
415
|
return ctx.badRequest(error2.message);
|
|
416
416
|
}
|
|
@@ -500,9 +500,11 @@ const firebaseController = {
|
|
|
500
500
|
}
|
|
501
501
|
},
|
|
502
502
|
/**
|
|
503
|
-
* Reset password - authenticated
|
|
503
|
+
* Reset password - authenticated password change
|
|
504
504
|
* POST /api/firebase-authentication/resetPassword
|
|
505
|
-
* Public endpoint -
|
|
505
|
+
* Public endpoint - requires valid JWT in Authorization header
|
|
506
|
+
* Used for admin-initiated resets or user self-service password changes
|
|
507
|
+
* NOT used for forgot password email flow (which uses Firebase's hosted UI)
|
|
506
508
|
*/
|
|
507
509
|
async resetPassword(ctx) {
|
|
508
510
|
strapi.log.debug("resetPassword endpoint called");
|
|
@@ -5186,13 +5188,13 @@ lodash.exports;
|
|
|
5186
5188
|
return string2.slice(position, position + target.length) == target;
|
|
5187
5189
|
}
|
|
5188
5190
|
function template(string2, options2, guard) {
|
|
5189
|
-
var
|
|
5191
|
+
var settings = lodash2.templateSettings;
|
|
5190
5192
|
if (guard && isIterateeCall(string2, options2, guard)) {
|
|
5191
5193
|
options2 = undefined$1;
|
|
5192
5194
|
}
|
|
5193
5195
|
string2 = toString4(string2);
|
|
5194
|
-
options2 = assignInWith({}, options2,
|
|
5195
|
-
var imports = assignInWith({}, options2.imports,
|
|
5196
|
+
options2 = assignInWith({}, options2, settings, customDefaultsAssignIn);
|
|
5197
|
+
var imports = assignInWith({}, options2.imports, settings.imports, customDefaultsAssignIn), importsKeys = keys2(imports), importsValues = baseValues(imports, importsKeys);
|
|
5196
5198
|
var isEscaping, isEvaluating, index2 = 0, interpolate = options2.interpolate || reNoMatch, source = "__p += '";
|
|
5197
5199
|
var reDelimiters = RegExp2(
|
|
5198
5200
|
(options2.escape || reNoMatch).source + "|" + interpolate.source + "|" + (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + "|" + (options2.evaluate || reNoMatch).source + "|$",
|
|
@@ -28902,9 +28904,6 @@ const userController = {
|
|
|
28902
28904
|
}
|
|
28903
28905
|
};
|
|
28904
28906
|
const settingsController = {
|
|
28905
|
-
setToken: async (ctx) => {
|
|
28906
|
-
ctx.body = await strapi.plugin("firebase-authentication").service("settingsService").setToken(ctx);
|
|
28907
|
-
},
|
|
28908
28907
|
setFirebaseConfigJson: async (ctx) => {
|
|
28909
28908
|
ctx.body = await strapi.plugin("firebase-authentication").service("settingsService").setFirebaseConfigJson(ctx);
|
|
28910
28909
|
},
|
|
@@ -29059,13 +29058,7 @@ const controllers = {
|
|
|
29059
29058
|
};
|
|
29060
29059
|
const middlewares = {};
|
|
29061
29060
|
const policies = {};
|
|
29062
|
-
const
|
|
29063
|
-
{
|
|
29064
|
-
method: "POST",
|
|
29065
|
-
path: "/settings/token",
|
|
29066
|
-
handler: "settingsController.setToken",
|
|
29067
|
-
config: { policies: ["admin::isAuthenticatedAdmin"] }
|
|
29068
|
-
},
|
|
29061
|
+
const settingsRoute = [
|
|
29069
29062
|
{
|
|
29070
29063
|
method: "POST",
|
|
29071
29064
|
path: "/settings/firebase-config",
|
|
@@ -29099,75 +29092,9 @@ const settings = [
|
|
|
29099
29092
|
];
|
|
29100
29093
|
const admin = {
|
|
29101
29094
|
type: "admin",
|
|
29102
|
-
routes: [...settings]
|
|
29103
|
-
};
|
|
29104
|
-
const contentApi = {
|
|
29105
|
-
type: "content-api",
|
|
29106
|
-
routes: [
|
|
29107
|
-
{
|
|
29108
|
-
method: "POST",
|
|
29109
|
-
path: "/",
|
|
29110
|
-
handler: "firebaseController.validateToken",
|
|
29111
|
-
config: {
|
|
29112
|
-
auth: false,
|
|
29113
|
-
// Public endpoint - this IS the authentication endpoint
|
|
29114
|
-
policies: []
|
|
29115
|
-
}
|
|
29116
|
-
},
|
|
29117
|
-
{
|
|
29118
|
-
method: "POST",
|
|
29119
|
-
path: "/emailLogin",
|
|
29120
|
-
handler: "firebaseController.emailLogin",
|
|
29121
|
-
config: {
|
|
29122
|
-
auth: false,
|
|
29123
|
-
// Public endpoint - email/password login
|
|
29124
|
-
policies: []
|
|
29125
|
-
}
|
|
29126
|
-
},
|
|
29127
|
-
{
|
|
29128
|
-
method: "POST",
|
|
29129
|
-
path: "/forgotPassword",
|
|
29130
|
-
handler: "firebaseController.forgotPassword",
|
|
29131
|
-
config: {
|
|
29132
|
-
auth: false,
|
|
29133
|
-
// Public endpoint - no authentication required
|
|
29134
|
-
policies: []
|
|
29135
|
-
}
|
|
29136
|
-
},
|
|
29137
|
-
{
|
|
29138
|
-
method: "POST",
|
|
29139
|
-
path: "/resetPassword",
|
|
29140
|
-
handler: "firebaseController.resetPassword",
|
|
29141
|
-
config: {
|
|
29142
|
-
auth: false,
|
|
29143
|
-
// Public endpoint - uses JWT from Authorization header
|
|
29144
|
-
policies: []
|
|
29145
|
-
}
|
|
29146
|
-
},
|
|
29147
|
-
{
|
|
29148
|
-
method: "GET",
|
|
29149
|
-
path: "/config",
|
|
29150
|
-
handler: "settingsController.getPublicConfig",
|
|
29151
|
-
config: {
|
|
29152
|
-
auth: false,
|
|
29153
|
-
// Public endpoint - frontend needs this for validation
|
|
29154
|
-
policies: []
|
|
29155
|
-
}
|
|
29156
|
-
},
|
|
29157
|
-
{
|
|
29158
|
-
method: "POST",
|
|
29159
|
-
path: "/requestMagicLink",
|
|
29160
|
-
handler: "firebaseController.requestMagicLink",
|
|
29161
|
-
config: {
|
|
29162
|
-
auth: false,
|
|
29163
|
-
// Public endpoint - passwordless authentication
|
|
29164
|
-
policies: []
|
|
29165
|
-
}
|
|
29166
|
-
}
|
|
29167
|
-
]
|
|
29168
|
-
};
|
|
29169
|
-
const contentInternalApi = {
|
|
29170
29095
|
routes: [
|
|
29096
|
+
...settingsRoute,
|
|
29097
|
+
// User management routes
|
|
29171
29098
|
{
|
|
29172
29099
|
method: "GET",
|
|
29173
29100
|
path: "/users",
|
|
@@ -29231,23 +29158,77 @@ const contentInternalApi = {
|
|
|
29231
29158
|
config: {
|
|
29232
29159
|
policies: ["admin::isAuthenticatedAdmin"]
|
|
29233
29160
|
}
|
|
29161
|
+
}
|
|
29162
|
+
]
|
|
29163
|
+
};
|
|
29164
|
+
const contentApi = {
|
|
29165
|
+
type: "content-api",
|
|
29166
|
+
routes: [
|
|
29167
|
+
{
|
|
29168
|
+
method: "POST",
|
|
29169
|
+
path: "/",
|
|
29170
|
+
handler: "firebaseController.validateToken",
|
|
29171
|
+
config: {
|
|
29172
|
+
auth: false,
|
|
29173
|
+
// Public endpoint - this IS the authentication endpoint
|
|
29174
|
+
policies: []
|
|
29175
|
+
}
|
|
29176
|
+
},
|
|
29177
|
+
{
|
|
29178
|
+
method: "POST",
|
|
29179
|
+
path: "/emailLogin",
|
|
29180
|
+
handler: "firebaseController.emailLogin",
|
|
29181
|
+
config: {
|
|
29182
|
+
auth: false,
|
|
29183
|
+
// Public endpoint - email/password login
|
|
29184
|
+
policies: []
|
|
29185
|
+
}
|
|
29186
|
+
},
|
|
29187
|
+
{
|
|
29188
|
+
method: "POST",
|
|
29189
|
+
path: "/forgotPassword",
|
|
29190
|
+
handler: "firebaseController.forgotPassword",
|
|
29191
|
+
config: {
|
|
29192
|
+
auth: false,
|
|
29193
|
+
// Public endpoint - no authentication required
|
|
29194
|
+
policies: []
|
|
29195
|
+
}
|
|
29196
|
+
},
|
|
29197
|
+
{
|
|
29198
|
+
method: "POST",
|
|
29199
|
+
path: "/resetPassword",
|
|
29200
|
+
handler: "firebaseController.resetPassword",
|
|
29201
|
+
config: {
|
|
29202
|
+
auth: false,
|
|
29203
|
+
// Public endpoint - authenticated password change, requires valid JWT in Authorization header
|
|
29204
|
+
policies: []
|
|
29205
|
+
}
|
|
29234
29206
|
},
|
|
29235
29207
|
{
|
|
29236
29208
|
method: "GET",
|
|
29237
29209
|
path: "/config",
|
|
29238
29210
|
handler: "settingsController.getPublicConfig",
|
|
29239
29211
|
config: {
|
|
29212
|
+
auth: false,
|
|
29213
|
+
// Public endpoint - frontend needs this for validation
|
|
29240
29214
|
policies: []
|
|
29241
|
-
// This is intentionally public for frontend config
|
|
29242
29215
|
}
|
|
29243
29216
|
},
|
|
29244
|
-
|
|
29217
|
+
{
|
|
29218
|
+
method: "POST",
|
|
29219
|
+
path: "/requestMagicLink",
|
|
29220
|
+
handler: "firebaseController.requestMagicLink",
|
|
29221
|
+
config: {
|
|
29222
|
+
auth: false,
|
|
29223
|
+
// Public endpoint - passwordless authentication
|
|
29224
|
+
policies: []
|
|
29225
|
+
}
|
|
29226
|
+
}
|
|
29245
29227
|
]
|
|
29246
29228
|
};
|
|
29247
29229
|
const routes = {
|
|
29248
29230
|
admin,
|
|
29249
|
-
"content-api": contentApi
|
|
29250
|
-
"content-internal-api": contentInternalApi
|
|
29231
|
+
"content-api": contentApi
|
|
29251
29232
|
};
|
|
29252
29233
|
const checkValidJson = (jsonString) => {
|
|
29253
29234
|
try {
|
|
@@ -29539,7 +29520,7 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29539
29520
|
/**
|
|
29540
29521
|
* Updates only the magic link settings without affecting other configuration
|
|
29541
29522
|
*/
|
|
29542
|
-
async updateMagicLinkSettings(
|
|
29523
|
+
async updateMagicLinkSettings(settings) {
|
|
29543
29524
|
try {
|
|
29544
29525
|
const foundConfig = await strapi2.db.query(CONFIG_CONTENT_TYPE).findOne({ where: {} });
|
|
29545
29526
|
if (!foundConfig) {
|
|
@@ -29548,10 +29529,10 @@ const settingsService = ({ strapi: strapi2 }) => {
|
|
|
29548
29529
|
const result = await strapi2.db.query(CONFIG_CONTENT_TYPE).update({
|
|
29549
29530
|
where: { id: foundConfig.id },
|
|
29550
29531
|
data: {
|
|
29551
|
-
enableMagicLink:
|
|
29552
|
-
magicLinkUrl:
|
|
29553
|
-
magicLinkEmailSubject:
|
|
29554
|
-
magicLinkExpiryHours:
|
|
29532
|
+
enableMagicLink: settings.enableMagicLink,
|
|
29533
|
+
magicLinkUrl: settings.magicLinkUrl,
|
|
29534
|
+
magicLinkEmailSubject: settings.magicLinkEmailSubject,
|
|
29535
|
+
magicLinkExpiryHours: settings.magicLinkExpiryHours
|
|
29555
29536
|
}
|
|
29556
29537
|
});
|
|
29557
29538
|
return {
|
|
@@ -29967,7 +29948,7 @@ const userService = ({ strapi: strapi2 }) => {
|
|
|
29967
29948
|
try {
|
|
29968
29949
|
const actionCodeSettings = {
|
|
29969
29950
|
url: passwordResetUrl,
|
|
29970
|
-
handleCodeInApp:
|
|
29951
|
+
handleCodeInApp: false
|
|
29971
29952
|
};
|
|
29972
29953
|
const timeoutPromise = new Promise((_2, reject) => {
|
|
29973
29954
|
setTimeout(
|
|
@@ -30188,17 +30169,13 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30188
30169
|
});
|
|
30189
30170
|
if (userByEmail) {
|
|
30190
30171
|
validateUserNotBlocked(userByEmail);
|
|
30191
|
-
|
|
30192
|
-
|
|
30193
|
-
|
|
30194
|
-
|
|
30195
|
-
strapi2.log.info(`Auto-linked user ${userByEmail.username} to Firebase UID ${firebaseUID}`);
|
|
30196
|
-
} catch (error2) {
|
|
30197
|
-
if (error2.code !== "23505") {
|
|
30198
|
-
throw error2;
|
|
30199
|
-
}
|
|
30200
|
-
strapi2.log.info(`User ${userByEmail.username} already linked to Firebase (race condition handled)`);
|
|
30172
|
+
const existingLink = await strapi2.plugin("firebase-authentication").service("firebaseUserDataService").getByFirebaseUID(firebaseUID);
|
|
30173
|
+
if (existingLink && existingLink.user) {
|
|
30174
|
+
validateUserNotBlocked(existingLink.user);
|
|
30175
|
+
return existingLink.user;
|
|
30201
30176
|
}
|
|
30177
|
+
await strapi2.plugin("firebase-authentication").service("firebaseUserDataService").updateForUser(userByEmail.documentId, { firebaseUserID: firebaseUID });
|
|
30178
|
+
strapi2.log.info(`Auto-linked user ${userByEmail.username} to Firebase UID ${firebaseUID}`);
|
|
30202
30179
|
return userByEmail;
|
|
30203
30180
|
}
|
|
30204
30181
|
const firebaseDataByApple = await strapi2.documents("plugin::firebase-authentication.firebase-user-data").findMany({
|
|
@@ -30212,21 +30189,13 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30212
30189
|
const userByAppleEmail = firebaseDataByApple?.[0]?.user || null;
|
|
30213
30190
|
if (userByAppleEmail) {
|
|
30214
30191
|
validateUserNotBlocked(userByAppleEmail);
|
|
30215
|
-
|
|
30216
|
-
|
|
30217
|
-
|
|
30218
|
-
|
|
30219
|
-
strapi2.log.info(
|
|
30220
|
-
`Auto-linked Apple user ${userByAppleEmail.username} to Firebase UID ${firebaseUID}`
|
|
30221
|
-
);
|
|
30222
|
-
} catch (error2) {
|
|
30223
|
-
if (error2.code !== "23505") {
|
|
30224
|
-
throw error2;
|
|
30225
|
-
}
|
|
30226
|
-
strapi2.log.info(
|
|
30227
|
-
`User ${userByAppleEmail.username} already linked to Firebase (race condition handled)`
|
|
30228
|
-
);
|
|
30192
|
+
const existingLink = await strapi2.plugin("firebase-authentication").service("firebaseUserDataService").getByFirebaseUID(firebaseUID);
|
|
30193
|
+
if (existingLink && existingLink.user) {
|
|
30194
|
+
validateUserNotBlocked(existingLink.user);
|
|
30195
|
+
return existingLink.user;
|
|
30229
30196
|
}
|
|
30197
|
+
await strapi2.plugin("firebase-authentication").service("firebaseUserDataService").updateForUser(userByAppleEmail.documentId, { firebaseUserID: firebaseUID });
|
|
30198
|
+
strapi2.log.info(`Auto-linked Apple user ${userByAppleEmail.username} to Firebase UID ${firebaseUID}`);
|
|
30230
30199
|
return userByAppleEmail;
|
|
30231
30200
|
}
|
|
30232
30201
|
}
|
|
@@ -30237,17 +30206,13 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30237
30206
|
});
|
|
30238
30207
|
if (userByPhone) {
|
|
30239
30208
|
validateUserNotBlocked(userByPhone);
|
|
30240
|
-
|
|
30241
|
-
|
|
30242
|
-
|
|
30243
|
-
|
|
30244
|
-
strapi2.log.info(`Auto-linked phone user ${userByPhone.username} to Firebase UID ${firebaseUID}`);
|
|
30245
|
-
} catch (error2) {
|
|
30246
|
-
if (error2.code !== "23505") {
|
|
30247
|
-
throw error2;
|
|
30248
|
-
}
|
|
30249
|
-
strapi2.log.info(`User ${userByPhone.username} already linked to Firebase (race condition handled)`);
|
|
30209
|
+
const existingLink = await strapi2.plugin("firebase-authentication").service("firebaseUserDataService").getByFirebaseUID(firebaseUID);
|
|
30210
|
+
if (existingLink && existingLink.user) {
|
|
30211
|
+
validateUserNotBlocked(existingLink.user);
|
|
30212
|
+
return existingLink.user;
|
|
30250
30213
|
}
|
|
30214
|
+
await strapi2.plugin("firebase-authentication").service("firebaseUserDataService").updateForUser(userByPhone.documentId, { firebaseUserID: firebaseUID });
|
|
30215
|
+
strapi2.log.info(`Auto-linked phone user ${userByPhone.username} to Firebase UID ${firebaseUID}`);
|
|
30251
30216
|
return userByPhone;
|
|
30252
30217
|
}
|
|
30253
30218
|
}
|
|
@@ -30274,12 +30239,13 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30274
30239
|
type: "plugin",
|
|
30275
30240
|
name: "users-permissions"
|
|
30276
30241
|
});
|
|
30277
|
-
const
|
|
30242
|
+
const settings = await pluginStore.get({
|
|
30278
30243
|
key: "advanced"
|
|
30279
30244
|
});
|
|
30280
|
-
const role = await strapi2.db.query("plugin::users-permissions.role").findOne({ where: { type:
|
|
30245
|
+
const role = await strapi2.db.query("plugin::users-permissions.role").findOne({ where: { type: settings.default_role } });
|
|
30281
30246
|
userPayload.role = role.id;
|
|
30282
30247
|
userPayload.confirmed = true;
|
|
30248
|
+
userPayload.provider = "firebase";
|
|
30283
30249
|
userPayload.email = decodedToken.email;
|
|
30284
30250
|
userPayload.phoneNumber = decodedToken.phone_number;
|
|
30285
30251
|
if (profileMetaData) {
|
|
@@ -30315,7 +30281,22 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30315
30281
|
strapi2.log.info(`Created user ${newUser.username} and linked to Firebase UID ${decodedToken.uid}`);
|
|
30316
30282
|
return newUser;
|
|
30317
30283
|
} catch (error2) {
|
|
30318
|
-
if (
|
|
30284
|
+
if (error2.code === "23505") {
|
|
30285
|
+
strapi2.log.warn(
|
|
30286
|
+
`[Race Condition] User creation conflict for Firebase UID ${decodedToken.uid}. Another concurrent request created the user first. Retrying lookup...`
|
|
30287
|
+
);
|
|
30288
|
+
const existingUser = await strapi2.plugin("firebase-authentication").service("firebaseService").checkIfUserExists(decodedToken);
|
|
30289
|
+
if (existingUser) {
|
|
30290
|
+
strapi2.log.info(
|
|
30291
|
+
`[Race Condition] Successfully found user ${existingUser.username} created by concurrent request`
|
|
30292
|
+
);
|
|
30293
|
+
return existingUser;
|
|
30294
|
+
}
|
|
30295
|
+
strapi2.log.error(
|
|
30296
|
+
`[Race Condition] Failed to find user after race condition retry for Firebase UID ${decodedToken.uid}`
|
|
30297
|
+
);
|
|
30298
|
+
}
|
|
30299
|
+
if (error2.code !== "23505" && newUser) {
|
|
30319
30300
|
try {
|
|
30320
30301
|
await strapi2.db.query("plugin::users-permissions.user").delete({
|
|
30321
30302
|
where: { documentId: newUser.documentId }
|
|
@@ -30451,7 +30432,7 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30451
30432
|
},
|
|
30452
30433
|
/**
|
|
30453
30434
|
* Forgot password flow - sends reset email
|
|
30454
|
-
* Public endpoint that
|
|
30435
|
+
* Public endpoint that sends a Firebase-hosted password reset email using Firebase's secure hosted UI
|
|
30455
30436
|
*/
|
|
30456
30437
|
forgotPassword: async (ctx) => {
|
|
30457
30438
|
const { email: email2 } = ctx.request.body;
|
|
@@ -30497,13 +30478,30 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30497
30478
|
if (!strapiUser) {
|
|
30498
30479
|
return { message: "If an account with that email exists, a password reset link has been sent." };
|
|
30499
30480
|
}
|
|
30500
|
-
const
|
|
30501
|
-
|
|
30502
|
-
|
|
30503
|
-
|
|
30504
|
-
|
|
30505
|
-
)
|
|
30506
|
-
|
|
30481
|
+
const actionCodeSettings = {
|
|
30482
|
+
url: resetUrl,
|
|
30483
|
+
// Continue URL after reset completes on Firebase's page
|
|
30484
|
+
handleCodeInApp: false
|
|
30485
|
+
};
|
|
30486
|
+
const timeoutPromise = new Promise((_2, reject) => {
|
|
30487
|
+
setTimeout(
|
|
30488
|
+
() => reject(new Error("Firebase generatePasswordResetLink timeout after 10 seconds")),
|
|
30489
|
+
1e4
|
|
30490
|
+
);
|
|
30491
|
+
});
|
|
30492
|
+
let resetLink;
|
|
30493
|
+
try {
|
|
30494
|
+
resetLink = await Promise.race([
|
|
30495
|
+
strapi2.firebase.auth().generatePasswordResetLink(strapiUser.email, actionCodeSettings),
|
|
30496
|
+
timeoutPromise
|
|
30497
|
+
]);
|
|
30498
|
+
strapi2.log.info(
|
|
30499
|
+
`✅ Password reset link generated successfully for ${strapiUser.email}: ${resetLink}`
|
|
30500
|
+
);
|
|
30501
|
+
} catch (error2) {
|
|
30502
|
+
strapi2.log.error(`❌ Failed to generate password reset link for ${strapiUser.email}:`, error2);
|
|
30503
|
+
throw error2;
|
|
30504
|
+
}
|
|
30507
30505
|
await strapi2.plugin("firebase-authentication").service("emailService").sendPasswordResetEmail(strapiUser, resetLink);
|
|
30508
30506
|
return {
|
|
30509
30507
|
message: "If an account with that email exists, a password reset link has been sent."
|
|
@@ -30517,7 +30515,17 @@ const firebaseService = ({ strapi: strapi2 }) => ({
|
|
|
30517
30515
|
},
|
|
30518
30516
|
/**
|
|
30519
30517
|
* Reset password with authenticated JWT
|
|
30520
|
-
*
|
|
30518
|
+
* Allows authenticated users (or admins) to change a user's Firebase password
|
|
30519
|
+
*
|
|
30520
|
+
* @param ctx - Koa context with JWT in Authorization header and new password in body
|
|
30521
|
+
* @returns User object and fresh JWT for auto-login
|
|
30522
|
+
*
|
|
30523
|
+
* @remarks
|
|
30524
|
+
* Use cases:
|
|
30525
|
+
* 1. Admin-initiated password reset (via admin panel)
|
|
30526
|
+
* 2. User-initiated password change (when already authenticated)
|
|
30527
|
+
*
|
|
30528
|
+
* NOT used for forgot password email flow - that now uses Firebase's hosted UI
|
|
30521
30529
|
*/
|
|
30522
30530
|
resetPassword: async (ctx) => {
|
|
30523
30531
|
const { password } = ctx.request.body;
|
|
@@ -31252,7 +31260,7 @@ const firebaseUserDataService = ({ strapi: strapi2 }) => ({
|
|
|
31252
31260
|
if (!userId || typeof userId !== "string") {
|
|
31253
31261
|
throw new Error("Invalid user documentId");
|
|
31254
31262
|
}
|
|
31255
|
-
let firebaseData = await strapi2.documents("plugin::firebase-authentication.firebase-user-data").
|
|
31263
|
+
let firebaseData = await strapi2.documents("plugin::firebase-authentication.firebase-user-data").findFirst({
|
|
31256
31264
|
filters: { user: { documentId: { $eq: userId } } },
|
|
31257
31265
|
populate: ["user"]
|
|
31258
31266
|
});
|
|
@@ -31267,7 +31275,7 @@ const firebaseUserDataService = ({ strapi: strapi2 }) => ({
|
|
|
31267
31275
|
* @returns Firebase user data with populated user
|
|
31268
31276
|
*/
|
|
31269
31277
|
async getByFirebaseUID(firebaseUID) {
|
|
31270
|
-
return await strapi2.documents("plugin::firebase-authentication.firebase-user-data").
|
|
31278
|
+
return await strapi2.documents("plugin::firebase-authentication.firebase-user-data").findFirst({
|
|
31271
31279
|
filters: { firebaseUserID: { $eq: firebaseUID } },
|
|
31272
31280
|
populate: ["user"]
|
|
31273
31281
|
});
|
|
@@ -31278,7 +31286,7 @@ const firebaseUserDataService = ({ strapi: strapi2 }) => ({
|
|
|
31278
31286
|
* @param data - Fields to update
|
|
31279
31287
|
*/
|
|
31280
31288
|
async updateForUser(userId, data) {
|
|
31281
|
-
let firebaseData = await strapi2.documents("plugin::firebase-authentication.firebase-user-data").
|
|
31289
|
+
let firebaseData = await strapi2.documents("plugin::firebase-authentication.firebase-user-data").findFirst({
|
|
31282
31290
|
filters: { user: { documentId: { $eq: userId } } }
|
|
31283
31291
|
});
|
|
31284
31292
|
if (!firebaseData) {
|
|
@@ -31294,8 +31302,8 @@ const firebaseUserDataService = ({ strapi: strapi2 }) => ({
|
|
|
31294
31302
|
});
|
|
31295
31303
|
} catch (error2) {
|
|
31296
31304
|
if (error2.code === "23505") {
|
|
31297
|
-
strapi2.log.warn(`Race condition detected for user ${userId}, retrying
|
|
31298
|
-
firebaseData = await strapi2.documents("plugin::firebase-authentication.firebase-user-data").
|
|
31305
|
+
strapi2.log.warn(`Race condition detected for user ${userId}, retrying findFirst`);
|
|
31306
|
+
firebaseData = await strapi2.documents("plugin::firebase-authentication.firebase-user-data").findFirst({
|
|
31299
31307
|
filters: { user: { documentId: { $eq: userId } } }
|
|
31300
31308
|
});
|
|
31301
31309
|
if (firebaseData) {
|
|
@@ -27,9 +27,11 @@ declare const firebaseController: {
|
|
|
27
27
|
*/
|
|
28
28
|
forgotPassword(ctx: any): Promise<void>;
|
|
29
29
|
/**
|
|
30
|
-
* Reset password - authenticated
|
|
30
|
+
* Reset password - authenticated password change
|
|
31
31
|
* POST /api/firebase-authentication/resetPassword
|
|
32
|
-
* Public endpoint -
|
|
32
|
+
* Public endpoint - requires valid JWT in Authorization header
|
|
33
|
+
* Used for admin-initiated resets or user self-service password changes
|
|
34
|
+
* NOT used for forgot password email flow (which uses Firebase's hosted UI)
|
|
33
35
|
*/
|
|
34
36
|
resetPassword(ctx: any): Promise<void>;
|
|
35
37
|
requestMagicLink(ctx: Context): Promise<void>;
|
|
@@ -20,7 +20,6 @@ declare const _default: {
|
|
|
20
20
|
sendResetEmail: (ctx: any) => Promise<void>;
|
|
21
21
|
};
|
|
22
22
|
settingsController: {
|
|
23
|
-
setToken: (ctx: import("koa").Context | import("koa").DefaultContext) => Promise<void>;
|
|
24
23
|
setFirebaseConfigJson: (ctx: import("koa").Context | import("koa").DefaultContext) => Promise<void>;
|
|
25
24
|
getFirebaseConfigJson: (ctx: import("koa").Context | import("koa").DefaultContext) => Promise<any>;
|
|
26
25
|
delFirebaseConfigJson(ctx: import("koa").Context | import("koa").DefaultContext): Promise<void>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Context, DefaultContext } from "koa";
|
|
2
2
|
declare const _default: {
|
|
3
|
-
setToken: (ctx: DefaultContext | Context) => Promise<void>;
|
|
4
3
|
setFirebaseConfigJson: (ctx: DefaultContext | Context) => Promise<void>;
|
|
5
4
|
getFirebaseConfigJson: (ctx: DefaultContext | Context) => Promise<any>;
|
|
6
5
|
delFirebaseConfigJson(ctx: DefaultContext | Context): Promise<void>;
|
|
@@ -41,7 +41,6 @@ declare const _default: {
|
|
|
41
41
|
sendResetEmail: (ctx: any) => Promise<void>;
|
|
42
42
|
};
|
|
43
43
|
settingsController: {
|
|
44
|
-
setToken: (ctx: import("koa").Context | import("koa").DefaultContext) => Promise<void>;
|
|
45
44
|
setFirebaseConfigJson: (ctx: import("koa").Context | import("koa").DefaultContext) => Promise<void>;
|
|
46
45
|
getFirebaseConfigJson: (ctx: import("koa").Context | import("koa").DefaultContext) => Promise<any>;
|
|
47
46
|
delFirebaseConfigJson(ctx: import("koa").Context | import("koa").DefaultContext): Promise<void>;
|
|
@@ -57,6 +56,9 @@ declare const _default: {
|
|
|
57
56
|
method: string;
|
|
58
57
|
path: string;
|
|
59
58
|
handler: string;
|
|
59
|
+
/**
|
|
60
|
+
* Plugin server methods
|
|
61
|
+
*/
|
|
60
62
|
config: {
|
|
61
63
|
policies: string[];
|
|
62
64
|
};
|
|
@@ -74,16 +76,6 @@ declare const _default: {
|
|
|
74
76
|
};
|
|
75
77
|
}[];
|
|
76
78
|
};
|
|
77
|
-
"content-internal-api": {
|
|
78
|
-
routes: {
|
|
79
|
-
method: string;
|
|
80
|
-
path: string;
|
|
81
|
-
handler: string;
|
|
82
|
-
config: {
|
|
83
|
-
policies: string[];
|
|
84
|
-
};
|
|
85
|
-
}[];
|
|
86
|
-
};
|
|
87
79
|
};
|
|
88
80
|
services: {
|
|
89
81
|
settingsService: ({ strapi }: {
|
|
@@ -66,14 +66,24 @@ declare const _default: ({ strapi }: {
|
|
|
66
66
|
}>;
|
|
67
67
|
/**
|
|
68
68
|
* Forgot password flow - sends reset email
|
|
69
|
-
* Public endpoint that
|
|
69
|
+
* Public endpoint that sends a Firebase-hosted password reset email using Firebase's secure hosted UI
|
|
70
70
|
*/
|
|
71
71
|
forgotPassword: (ctx: any) => Promise<{
|
|
72
72
|
message: string;
|
|
73
73
|
}>;
|
|
74
74
|
/**
|
|
75
75
|
* Reset password with authenticated JWT
|
|
76
|
-
*
|
|
76
|
+
* Allows authenticated users (or admins) to change a user's Firebase password
|
|
77
|
+
*
|
|
78
|
+
* @param ctx - Koa context with JWT in Authorization header and new password in body
|
|
79
|
+
* @returns User object and fresh JWT for auto-login
|
|
80
|
+
*
|
|
81
|
+
* @remarks
|
|
82
|
+
* Use cases:
|
|
83
|
+
* 1. Admin-initiated password reset (via admin panel)
|
|
84
|
+
* 2. User-initiated password change (when already authenticated)
|
|
85
|
+
*
|
|
86
|
+
* NOT used for forgot password email flow - that now uses Firebase's hosted UI
|
|
77
87
|
*/
|
|
78
88
|
resetPassword: (ctx: any) => Promise<{
|
|
79
89
|
user: any;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-plugin-firebase-authentication",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.7",
|
|
4
4
|
"description": "Allows easy integration between clients utilizing Firebase for authentication and Strapi",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"format": "npx prettier --write .",
|
|
44
44
|
"format:check": "npx prettier -c .",
|
|
45
45
|
"release": "npm run format && npm run build && npm run verify && changelogen --release && npm publish && git push --follow-tags",
|
|
46
|
+
"changelog": "changelogen --release",
|
|
46
47
|
"test:ts:back": "run -T tsc -p server/tsconfig.json",
|
|
47
48
|
"test:ts:front": "run -T tsc -p admin/tsconfig.json",
|
|
48
49
|
"verify": "strapi-plugin verify",
|
|
@@ -50,8 +51,6 @@
|
|
|
50
51
|
"watch:link": "strapi-plugin watch:link"
|
|
51
52
|
},
|
|
52
53
|
"dependencies": {
|
|
53
|
-
"@strapi/design-system": "^2.0.0-rc.16",
|
|
54
|
-
"@strapi/icons": "^2.0.0-rc.25",
|
|
55
54
|
"@tanstack/react-query": "^5.90.2",
|
|
56
55
|
"crypto-js": "^4.2.0",
|
|
57
56
|
"destr": "^2.0.5",
|
|
@@ -67,8 +66,9 @@
|
|
|
67
66
|
},
|
|
68
67
|
"devDependencies": {
|
|
69
68
|
"@strapi/sdk-plugin": "^5.3.2",
|
|
70
|
-
"@strapi/strapi": "^5.26.0",
|
|
71
69
|
"@strapi/typescript-utils": "^5.23.3",
|
|
70
|
+
"@strapi/design-system": "^2.0.0-rc.16",
|
|
71
|
+
"@strapi/icons": "^2.0.0-rc.25",
|
|
72
72
|
"@types/react": "^19.2.2",
|
|
73
73
|
"@types/react-dom": "^19.2.1",
|
|
74
74
|
"@types/react-syntax-highlighter": "^15.5.13",
|
|
@@ -82,12 +82,13 @@
|
|
|
82
82
|
"typescript": "^5.7.3"
|
|
83
83
|
},
|
|
84
84
|
"peerDependencies": {
|
|
85
|
-
"@strapi/
|
|
86
|
-
"@strapi/
|
|
87
|
-
"
|
|
88
|
-
"react
|
|
89
|
-
"react-
|
|
90
|
-
"
|
|
85
|
+
"@strapi/strapi": "^5.0.0",
|
|
86
|
+
"@strapi/design-system": "^2.0.0-rc.0",
|
|
87
|
+
"@strapi/icons": "^2.0.0-rc.0",
|
|
88
|
+
"react": "^18.0.0",
|
|
89
|
+
"react-dom": "^18.0.0",
|
|
90
|
+
"react-router-dom": "^6.0.0",
|
|
91
|
+
"styled-components": "^6.0.0"
|
|
91
92
|
},
|
|
92
93
|
"engines": {
|
|
93
94
|
"node": ">=18.0.0 <=22.x.x",
|