strapi-plugin-firebase-authentication 1.0.13 → 1.1.0
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 +415 -191
- package/dist/_chunks/App-Bl6D4TFu.mjs +6197 -0
- package/dist/_chunks/App-HfsY_18f.js +6181 -0
- package/dist/_chunks/{api-4hcml0jk.mjs → api-B01IAVEC.mjs} +14 -3
- package/dist/_chunks/{api-DPtT1Bpu.js → api-BSejy8nn.js} +14 -3
- package/dist/_chunks/index-4hUrKd7Y.mjs +815 -0
- package/dist/_chunks/index-BbVqBI3M.js +859 -0
- package/dist/_chunks/index-BqF9RRVF.mjs +859 -0
- package/dist/_chunks/index-DgfRCyyQ.js +814 -0
- package/dist/admin/index.js +6 -1
- package/dist/admin/index.mjs +7 -2
- package/dist/admin/src/components/PasswordResetButton/PasswordResetButton.d.ts +11 -0
- package/dist/admin/src/components/PasswordResetButton/index.d.ts +1 -0
- package/dist/admin/src/components/common/Header/Header.d.ts +12 -0
- package/dist/admin/src/components/common/index.d.ts +3 -0
- package/dist/admin/src/components/forms/CreateUserForm/CreateUserForm.d.ts +2 -0
- package/dist/admin/src/components/forms/EditUserForm/EditUserForm.d.ts +6 -0
- package/dist/admin/src/components/forms/index.d.ts +4 -0
- package/dist/admin/src/components/forms/shared/UserFormFields/UserFormFields.d.ts +17 -0
- package/dist/admin/src/components/forms/shared/UserFormLayout/UserFormLayout.d.ts +7 -0
- package/dist/admin/src/components/index.d.ts +5 -0
- package/dist/admin/src/components/search/SearchURLQuery/SearchURLQuery.d.ts +6 -0
- package/dist/admin/src/components/search/index.d.ts +1 -0
- package/dist/admin/src/components/{DynamicTable → table/FirebaseTable}/FirebaseTable.d.ts +3 -2
- package/dist/admin/src/components/{DynamicTable → table}/FirebaseTableRows/FirebaseTableRows.d.ts +2 -2
- package/dist/admin/src/components/table/index.d.ts +3 -0
- package/dist/admin/src/components/user-management/ResetPassword/ResetPassword.d.ts +12 -0
- package/dist/admin/src/components/user-management/index.d.ts +2 -0
- package/dist/admin/src/hooks/useBulkSelection.d.ts +14 -0
- package/dist/admin/src/hooks/useUserForm.d.ts +18 -0
- package/dist/admin/src/pages/CreateView.d.ts +1 -0
- package/dist/admin/src/pages/EditView.d.ts +1 -0
- package/dist/admin/src/pages/ListView/index.d.ts +3 -5
- package/dist/admin/src/pages/Settings/api.d.ts +20 -1
- package/dist/admin/src/pages/utils/api.d.ts +4 -1
- package/dist/admin/src/utils/hasPasswordProvider.d.ts +15 -0
- package/dist/admin/src/utils/validation.d.ts +16 -0
- package/dist/model/Request.d.ts +2 -0
- package/dist/model/User.d.ts +6 -0
- package/dist/server/index.js +6564 -931
- package/dist/server/index.mjs +6518 -902
- package/dist/server/src/config/index.d.ts +36 -1
- package/dist/server/src/constants/index.d.ts +107 -0
- package/dist/server/src/content-types/index.d.ts +82 -0
- package/dist/server/src/controllers/firebaseController.d.ts +32 -2
- package/dist/server/src/controllers/index.d.ts +16 -10
- package/dist/server/src/controllers/settingsController.d.ts +2 -0
- package/dist/server/src/controllers/userController.d.ts +1 -0
- package/dist/server/src/index.d.ts +194 -27
- package/dist/server/src/migrations/migrate-firebase-user-data.d.ts +30 -0
- package/dist/server/src/routes/admin.d.ts +1 -1
- package/dist/server/src/routes/content-api.d.ts +1 -0
- package/dist/server/src/routes/content-internal-api.d.ts +1 -1
- package/dist/server/src/routes/index.d.ts +3 -2
- package/dist/server/src/routes/settingsRoutes.d.ts +1 -1
- package/dist/server/src/services/autoLinkService.d.ts +29 -0
- package/dist/server/src/services/emailService.d.ts +32 -0
- package/dist/server/src/services/firebaseService.d.ts +87 -13
- package/dist/server/src/services/firebaseStrapiLinkService.d.ts +34 -0
- package/dist/server/src/services/firebaseUserDataService.d.ts +26 -0
- package/dist/server/src/services/index.d.ts +91 -16
- package/dist/server/src/services/settingsService.d.ts +55 -0
- package/dist/server/src/services/templateService.d.ts +21 -0
- package/dist/server/src/services/userService.d.ts +2 -1
- package/dist/server/src/templates/defaults/index.d.ts +2 -0
- package/dist/server/src/templates/defaults/magic-link.d.ts +2 -0
- package/dist/server/src/templates/defaults/password-reset.d.ts +2 -0
- package/dist/server/src/templates/types.d.ts +30 -0
- package/dist/server/src/utils/fetch-me.d.ts +2 -10
- package/dist/server/src/utils/get-user-by-id.d.ts +1 -5
- package/dist/server/src/utils/users.d.ts +14 -0
- package/package.json +32 -22
- package/dist/_chunks/App-B2LtHk9g.js +0 -5062
- package/dist/_chunks/App-B3HcVa5j.mjs +0 -5081
- package/dist/_chunks/index-CALp4X47.mjs +0 -110
- package/dist/_chunks/index-CrGRt-Ya.mjs +0 -191
- package/dist/_chunks/index-DMBPEFUy.js +0 -109
- package/dist/_chunks/index-DmTKNKJB.js +0 -191
- package/dist/admin/src/components/SearchURLQuery/SearchURLQuery.d.ts +0 -18
- package/dist/admin/src/components/UserManagement/ResetPassword.d.ts +0 -8
- /package/dist/admin/src/components/{Initializer.d.ts → common/Initializer/Initializer.d.ts} +0 -0
- /package/dist/admin/src/components/{PluginIcon.d.ts → common/PluginIcon/PluginIcon.d.ts} +0 -0
- /package/dist/admin/src/components/{DynamicTable → table/FirebaseTable}/TableHeaders.d.ts +0 -0
- /package/dist/admin/src/components/{UserManagement → user-management/DeleteAccount}/DeleteAccount.d.ts +0 -0
|
@@ -0,0 +1,859 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
import { Flex, Box, Typography, JSONInput, Button, TextInput, Badge, Textarea, Toggle, NumberInput, Modal } from "@strapi/design-system";
|
|
4
|
+
import { useNotification, Page } from "@strapi/strapi/admin";
|
|
5
|
+
import { g as getFirebaseConfig, s as saveFirebaseConfig, d as delFirebaseConfig, a as savePasswordSettings } from "./api-B01IAVEC.mjs";
|
|
6
|
+
import { useNavigate } from "react-router-dom";
|
|
7
|
+
function SettingsPage() {
|
|
8
|
+
const { toggleNotification } = useNotification();
|
|
9
|
+
const [firebaseJsonValue, setFirebaseJsonValue] = useState(null);
|
|
10
|
+
const [firebaseJsonValueInput, setFirebaseJsonValueInput] = useState("");
|
|
11
|
+
const [firebaseWebApiKey, setFirebaseWebApiKey] = useState("");
|
|
12
|
+
const [showOptionalSettings, setShowOptionalSettings] = useState(false);
|
|
13
|
+
const [passwordRequirementsRegex, setPasswordRequirementsRegex] = useState("^.{6,}$");
|
|
14
|
+
const [passwordRequirementsMessage, setPasswordRequirementsMessage] = useState(
|
|
15
|
+
"Password must be at least 6 characters long"
|
|
16
|
+
);
|
|
17
|
+
const [passwordResetUrl, setPasswordResetUrl] = useState("http://localhost:3000/reset-password");
|
|
18
|
+
const [passwordResetEmailSubject, setPasswordResetEmailSubject] = useState("Reset Your Password");
|
|
19
|
+
const [enableMagicLink, setEnableMagicLink] = useState(false);
|
|
20
|
+
const [magicLinkUrl, setMagicLinkUrl] = useState("http://localhost:1338/verify-magic-link.html");
|
|
21
|
+
const [magicLinkEmailSubject, setMagicLinkEmailSubject] = useState("Sign in to Your Application");
|
|
22
|
+
const [magicLinkExpiryHours, setMagicLinkExpiryHours] = useState(1);
|
|
23
|
+
const [loading, setLoading] = useState(true);
|
|
24
|
+
const [showEditModal, setShowEditModal] = useState(false);
|
|
25
|
+
const [editWebApiKey, setEditWebApiKey] = useState("");
|
|
26
|
+
useNavigate();
|
|
27
|
+
const handleRetrieveFirebaseJsonConfig = () => {
|
|
28
|
+
setLoading(true);
|
|
29
|
+
getFirebaseConfig().then((data) => {
|
|
30
|
+
setLoading(false);
|
|
31
|
+
if (!data || Array.isArray(data) && data.length === 0) {
|
|
32
|
+
setFirebaseJsonValue(null);
|
|
33
|
+
setFirebaseJsonValueInput("");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
setFirebaseJsonValue(data);
|
|
37
|
+
setFirebaseJsonValueInput(typeof data === "string" ? data : JSON.stringify(data));
|
|
38
|
+
setFirebaseWebApiKey(data?.firebaseWebApiKey || "");
|
|
39
|
+
setPasswordRequirementsRegex(data?.passwordRequirementsRegex || "^.{6,}$");
|
|
40
|
+
setPasswordRequirementsMessage(
|
|
41
|
+
data?.passwordRequirementsMessage || "Password must be at least 6 characters long"
|
|
42
|
+
);
|
|
43
|
+
setPasswordResetUrl(data?.passwordResetUrl || "http://localhost:3000/reset-password");
|
|
44
|
+
setPasswordResetEmailSubject(data?.passwordResetEmailSubject || "Reset Your Password");
|
|
45
|
+
setEnableMagicLink(data?.enableMagicLink || false);
|
|
46
|
+
setMagicLinkUrl(data?.magicLinkUrl || "http://localhost:1338/verify-magic-link.html");
|
|
47
|
+
setMagicLinkEmailSubject(data?.magicLinkEmailSubject || "Sign in to Your Application");
|
|
48
|
+
setMagicLinkExpiryHours(data?.magicLinkExpiryHours || 1);
|
|
49
|
+
}).catch((error) => {
|
|
50
|
+
setLoading(false);
|
|
51
|
+
console.error("Error retrieving Firebase config:", error);
|
|
52
|
+
setFirebaseJsonValue(null);
|
|
53
|
+
setFirebaseJsonValueInput("");
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
handleRetrieveFirebaseJsonConfig();
|
|
58
|
+
}, []);
|
|
59
|
+
const handleDeleteFirebaseJsonConfig = async () => {
|
|
60
|
+
try {
|
|
61
|
+
setLoading(true);
|
|
62
|
+
await delFirebaseConfig();
|
|
63
|
+
setFirebaseJsonValue(null);
|
|
64
|
+
setFirebaseJsonValueInput("");
|
|
65
|
+
setFirebaseWebApiKey("");
|
|
66
|
+
setPasswordRequirementsRegex("^.{6,}$");
|
|
67
|
+
setPasswordRequirementsMessage("Password must be at least 6 characters long");
|
|
68
|
+
setPasswordResetUrl("http://localhost:3000/reset-password");
|
|
69
|
+
setPasswordResetEmailSubject("Reset Your Password");
|
|
70
|
+
setEnableMagicLink(false);
|
|
71
|
+
setMagicLinkUrl("http://localhost:1338/verify-magic-link.html");
|
|
72
|
+
setMagicLinkEmailSubject("Sign in to Your Application");
|
|
73
|
+
setMagicLinkExpiryHours(1);
|
|
74
|
+
setLoading(false);
|
|
75
|
+
toggleNotification({
|
|
76
|
+
type: "success",
|
|
77
|
+
message: "Firebase configuration has successfully been removed"
|
|
78
|
+
});
|
|
79
|
+
} catch (err) {
|
|
80
|
+
setLoading(false);
|
|
81
|
+
toggleNotification({
|
|
82
|
+
type: "warning",
|
|
83
|
+
message: "An error occurred, please try again"
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const handleFirebaseJsonSubmit = async () => {
|
|
88
|
+
try {
|
|
89
|
+
setLoading(true);
|
|
90
|
+
const jsonToSubmit = typeof firebaseJsonValueInput === "string" ? firebaseJsonValueInput : JSON.stringify(firebaseJsonValueInput);
|
|
91
|
+
const data = await saveFirebaseConfig(jsonToSubmit, firebaseWebApiKey, {
|
|
92
|
+
passwordRequirementsRegex,
|
|
93
|
+
passwordRequirementsMessage,
|
|
94
|
+
passwordResetUrl,
|
|
95
|
+
passwordResetEmailSubject,
|
|
96
|
+
enableMagicLink,
|
|
97
|
+
magicLinkUrl,
|
|
98
|
+
magicLinkEmailSubject,
|
|
99
|
+
magicLinkExpiryHours
|
|
100
|
+
});
|
|
101
|
+
if (!data || !data.firebase_config_json) {
|
|
102
|
+
throw new Error("Invalid response from server");
|
|
103
|
+
}
|
|
104
|
+
setFirebaseJsonValue(data);
|
|
105
|
+
setLoading(false);
|
|
106
|
+
toggleNotification({
|
|
107
|
+
type: "success",
|
|
108
|
+
message: "Data submitted successfully"
|
|
109
|
+
});
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error("Error submitting Firebase JSON:", error);
|
|
112
|
+
toggleNotification({
|
|
113
|
+
type: "warning",
|
|
114
|
+
message: "Something went wrong"
|
|
115
|
+
});
|
|
116
|
+
setLoading(false);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const handleSavePasswordSettings = async () => {
|
|
120
|
+
try {
|
|
121
|
+
setLoading(true);
|
|
122
|
+
const data = await savePasswordSettings({
|
|
123
|
+
passwordRequirementsRegex,
|
|
124
|
+
passwordRequirementsMessage,
|
|
125
|
+
passwordResetUrl,
|
|
126
|
+
passwordResetEmailSubject,
|
|
127
|
+
enableMagicLink,
|
|
128
|
+
magicLinkUrl,
|
|
129
|
+
magicLinkEmailSubject,
|
|
130
|
+
magicLinkExpiryHours
|
|
131
|
+
});
|
|
132
|
+
if (data) {
|
|
133
|
+
setPasswordRequirementsRegex(data.passwordRequirementsRegex || passwordRequirementsRegex);
|
|
134
|
+
setPasswordRequirementsMessage(data.passwordRequirementsMessage || passwordRequirementsMessage);
|
|
135
|
+
setPasswordResetUrl(data.passwordResetUrl || passwordResetUrl);
|
|
136
|
+
setPasswordResetEmailSubject(data.passwordResetEmailSubject || passwordResetEmailSubject);
|
|
137
|
+
setEnableMagicLink(data.enableMagicLink || enableMagicLink);
|
|
138
|
+
setMagicLinkUrl(data.magicLinkUrl || magicLinkUrl);
|
|
139
|
+
setMagicLinkEmailSubject(data.magicLinkEmailSubject || magicLinkEmailSubject);
|
|
140
|
+
setMagicLinkExpiryHours(data.magicLinkExpiryHours || magicLinkExpiryHours);
|
|
141
|
+
}
|
|
142
|
+
setLoading(false);
|
|
143
|
+
toggleNotification({
|
|
144
|
+
type: "success",
|
|
145
|
+
message: "Password settings saved successfully"
|
|
146
|
+
});
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error("Error saving password settings:", error);
|
|
149
|
+
toggleNotification({
|
|
150
|
+
type: "warning",
|
|
151
|
+
message: "Failed to save password settings"
|
|
152
|
+
});
|
|
153
|
+
setLoading(false);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const handleSaveMagicLinkSettings = async () => {
|
|
157
|
+
try {
|
|
158
|
+
setLoading(true);
|
|
159
|
+
const data = await savePasswordSettings({
|
|
160
|
+
passwordRequirementsRegex,
|
|
161
|
+
passwordRequirementsMessage,
|
|
162
|
+
passwordResetUrl,
|
|
163
|
+
passwordResetEmailSubject,
|
|
164
|
+
enableMagicLink,
|
|
165
|
+
magicLinkUrl,
|
|
166
|
+
magicLinkEmailSubject,
|
|
167
|
+
magicLinkExpiryHours
|
|
168
|
+
});
|
|
169
|
+
if (data) {
|
|
170
|
+
setEnableMagicLink(data.enableMagicLink || enableMagicLink);
|
|
171
|
+
setMagicLinkUrl(data.magicLinkUrl || magicLinkUrl);
|
|
172
|
+
setMagicLinkEmailSubject(data.magicLinkEmailSubject || magicLinkEmailSubject);
|
|
173
|
+
setMagicLinkExpiryHours(data.magicLinkExpiryHours || magicLinkExpiryHours);
|
|
174
|
+
}
|
|
175
|
+
setLoading(false);
|
|
176
|
+
toggleNotification({
|
|
177
|
+
type: "success",
|
|
178
|
+
message: "Magic link settings saved successfully"
|
|
179
|
+
});
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.error("Error saving magic link settings:", error);
|
|
182
|
+
toggleNotification({
|
|
183
|
+
type: "warning",
|
|
184
|
+
message: "Failed to save magic link settings"
|
|
185
|
+
});
|
|
186
|
+
setLoading(false);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
const handleAddWebApiKey = () => {
|
|
190
|
+
setEditWebApiKey("");
|
|
191
|
+
setShowEditModal(true);
|
|
192
|
+
};
|
|
193
|
+
const handleRemoveWebApiKey = async () => {
|
|
194
|
+
const confirmed = window.confirm(
|
|
195
|
+
"Are you sure you want to remove the Web API Key? The emailLogin endpoint will stop working until you add it again."
|
|
196
|
+
);
|
|
197
|
+
if (!confirmed) return;
|
|
198
|
+
try {
|
|
199
|
+
setLoading(true);
|
|
200
|
+
const jsonToSubmit = firebaseJsonValue.firebaseConfigJson;
|
|
201
|
+
const data = await saveFirebaseConfig(jsonToSubmit, "", {
|
|
202
|
+
passwordRequirementsRegex,
|
|
203
|
+
passwordRequirementsMessage,
|
|
204
|
+
passwordResetUrl,
|
|
205
|
+
passwordResetEmailSubject,
|
|
206
|
+
enableMagicLink,
|
|
207
|
+
magicLinkUrl,
|
|
208
|
+
magicLinkEmailSubject,
|
|
209
|
+
magicLinkExpiryHours
|
|
210
|
+
});
|
|
211
|
+
if (!data || !data.firebase_config_json) {
|
|
212
|
+
throw new Error("Invalid response from server");
|
|
213
|
+
}
|
|
214
|
+
setFirebaseJsonValue(data);
|
|
215
|
+
setFirebaseWebApiKey("");
|
|
216
|
+
setLoading(false);
|
|
217
|
+
toggleNotification({
|
|
218
|
+
type: "success",
|
|
219
|
+
message: "Web API Key removed successfully"
|
|
220
|
+
});
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.error("Error removing Web API Key:", error);
|
|
223
|
+
toggleNotification({
|
|
224
|
+
type: "warning",
|
|
225
|
+
message: "Failed to remove Web API Key"
|
|
226
|
+
});
|
|
227
|
+
setLoading(false);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
const handleSaveEditConfiguration = async () => {
|
|
231
|
+
try {
|
|
232
|
+
setLoading(true);
|
|
233
|
+
const jsonToSubmit = firebaseJsonValue.firebaseConfigJson;
|
|
234
|
+
const data = await saveFirebaseConfig(jsonToSubmit, editWebApiKey, {
|
|
235
|
+
passwordRequirementsRegex,
|
|
236
|
+
passwordRequirementsMessage,
|
|
237
|
+
passwordResetUrl,
|
|
238
|
+
passwordResetEmailSubject,
|
|
239
|
+
enableMagicLink,
|
|
240
|
+
magicLinkUrl,
|
|
241
|
+
magicLinkEmailSubject,
|
|
242
|
+
magicLinkExpiryHours
|
|
243
|
+
});
|
|
244
|
+
if (!data || !data.firebase_config_json) {
|
|
245
|
+
throw new Error("Invalid response from server");
|
|
246
|
+
}
|
|
247
|
+
setFirebaseJsonValue(data);
|
|
248
|
+
setFirebaseWebApiKey(editWebApiKey);
|
|
249
|
+
setShowEditModal(false);
|
|
250
|
+
setLoading(false);
|
|
251
|
+
toggleNotification({
|
|
252
|
+
type: "success",
|
|
253
|
+
message: "Web API Key added successfully"
|
|
254
|
+
});
|
|
255
|
+
} catch (error) {
|
|
256
|
+
console.error("Error adding Web API Key:", error);
|
|
257
|
+
toggleNotification({
|
|
258
|
+
type: "warning",
|
|
259
|
+
message: "Failed to add Web API Key"
|
|
260
|
+
});
|
|
261
|
+
setLoading(false);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
if (loading) {
|
|
265
|
+
return /* @__PURE__ */ jsx(Page.Loading, {});
|
|
266
|
+
}
|
|
267
|
+
const isJsonString = (str) => {
|
|
268
|
+
try {
|
|
269
|
+
JSON.parse(str);
|
|
270
|
+
return true;
|
|
271
|
+
} catch (e) {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
276
|
+
/* @__PURE__ */ jsx(Flex, { style: { padding: 32 }, direction: "column", alignItems: "flex-start", gap: 4, children: /* @__PURE__ */ jsxs(Box, { style: { width: "100%" }, children: [
|
|
277
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 6, padding: 4, background: "neutral0", borderRadius: "4px", shadow: "filterShadow", children: [
|
|
278
|
+
/* @__PURE__ */ jsx(Typography, { variant: "alpha", as: "h2", style: { display: "block", marginBottom: "8px" }, children: "Firebase Authentication" }),
|
|
279
|
+
/* @__PURE__ */ jsx(
|
|
280
|
+
Typography,
|
|
281
|
+
{
|
|
282
|
+
variant: "omega",
|
|
283
|
+
textColor: "neutral600",
|
|
284
|
+
style: { display: "block", marginBottom: "24px" },
|
|
285
|
+
children: "Configure your Firebase service account (API key optional)"
|
|
286
|
+
}
|
|
287
|
+
),
|
|
288
|
+
(() => {
|
|
289
|
+
return !firebaseJsonValue || !firebaseJsonValue.firebaseConfigJson ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
290
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
291
|
+
/* @__PURE__ */ jsx(
|
|
292
|
+
Typography,
|
|
293
|
+
{
|
|
294
|
+
variant: "omega",
|
|
295
|
+
fontWeight: "bold",
|
|
296
|
+
style: { display: "block", marginBottom: "8px" },
|
|
297
|
+
children: "Firebase Service Account JSON *"
|
|
298
|
+
}
|
|
299
|
+
),
|
|
300
|
+
/* @__PURE__ */ jsx(
|
|
301
|
+
JSONInput,
|
|
302
|
+
{
|
|
303
|
+
value: firebaseJsonValueInput,
|
|
304
|
+
height: 400,
|
|
305
|
+
style: { height: 400 },
|
|
306
|
+
onChange: setFirebaseJsonValueInput,
|
|
307
|
+
error: firebaseJsonValueInput && !isJsonString(firebaseJsonValueInput) ? "Please enter a valid JSON string" : ""
|
|
308
|
+
}
|
|
309
|
+
)
|
|
310
|
+
] }),
|
|
311
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 4, children: [
|
|
312
|
+
/* @__PURE__ */ jsxs(
|
|
313
|
+
Button,
|
|
314
|
+
{
|
|
315
|
+
variant: "tertiary",
|
|
316
|
+
onClick: () => setShowOptionalSettings(!showOptionalSettings),
|
|
317
|
+
style: { marginBottom: 16 },
|
|
318
|
+
children: [
|
|
319
|
+
showOptionalSettings ? "▼" : "▶",
|
|
320
|
+
" Optional: Email/Password Authentication"
|
|
321
|
+
]
|
|
322
|
+
}
|
|
323
|
+
),
|
|
324
|
+
showOptionalSettings && /* @__PURE__ */ jsxs(Box, { padding: 3, background: "neutral100", borderRadius: "4px", children: [
|
|
325
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", marginBottom: 3, children: "The Web API Key is only needed if you want to use the emailLogin endpoint. Most users should authenticate with Firebase Client SDK instead." }),
|
|
326
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 3, children: [
|
|
327
|
+
/* @__PURE__ */ jsx(
|
|
328
|
+
Typography,
|
|
329
|
+
{
|
|
330
|
+
variant: "omega",
|
|
331
|
+
fontWeight: "bold",
|
|
332
|
+
style: { display: "block", marginBottom: "8px" },
|
|
333
|
+
children: "Firebase Web API Key (Optional)"
|
|
334
|
+
}
|
|
335
|
+
),
|
|
336
|
+
/* @__PURE__ */ jsx(
|
|
337
|
+
TextInput,
|
|
338
|
+
{
|
|
339
|
+
name: "firebaseWebApiKey",
|
|
340
|
+
value: firebaseWebApiKey,
|
|
341
|
+
onChange: (e) => setFirebaseWebApiKey(e.target.value),
|
|
342
|
+
placeholder: "AIzaSy... (your Web API Key)",
|
|
343
|
+
hint: "Only required for email/password login endpoint"
|
|
344
|
+
}
|
|
345
|
+
)
|
|
346
|
+
] }),
|
|
347
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 3, padding: 2, background: "primary100", borderRadius: "4px", children: [
|
|
348
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", children: "📍 Where to find your Web API Key:" }),
|
|
349
|
+
/* @__PURE__ */ jsxs("ol", { style: { marginLeft: 20, marginTop: 8 }, children: [
|
|
350
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
|
|
351
|
+
"Go to",
|
|
352
|
+
" ",
|
|
353
|
+
/* @__PURE__ */ jsx(
|
|
354
|
+
"a",
|
|
355
|
+
{
|
|
356
|
+
href: "https://console.firebase.google.com",
|
|
357
|
+
target: "_blank",
|
|
358
|
+
rel: "noreferrer",
|
|
359
|
+
children: "Firebase Console"
|
|
360
|
+
}
|
|
361
|
+
)
|
|
362
|
+
] }) }),
|
|
363
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: "Select your project" }) }),
|
|
364
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
|
|
365
|
+
"Click the gear icon → ",
|
|
366
|
+
/* @__PURE__ */ jsx("strong", { children: "Project Settings" })
|
|
367
|
+
] }) }),
|
|
368
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
|
|
369
|
+
"In the ",
|
|
370
|
+
/* @__PURE__ */ jsx("strong", { children: "General" }),
|
|
371
|
+
" tab, scroll down to ",
|
|
372
|
+
/* @__PURE__ */ jsx("strong", { children: "Your apps" })
|
|
373
|
+
] }) }),
|
|
374
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
|
|
375
|
+
"Find ",
|
|
376
|
+
/* @__PURE__ */ jsx("strong", { children: "Web API Key" }),
|
|
377
|
+
" (looks like: AIzaSyB3Xd...)"
|
|
378
|
+
] }) })
|
|
379
|
+
] })
|
|
380
|
+
] }),
|
|
381
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 3, padding: 2, background: "warning100", borderRadius: "4px", children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", textColor: "warning700", children: [
|
|
382
|
+
"⚠️ ",
|
|
383
|
+
/* @__PURE__ */ jsx("strong", { children: "Recommendation:" }),
|
|
384
|
+
" Instead of using emailLogin endpoint, authenticate users on the client side using Firebase SDK and exchange the ID token. This is more secure and doesn't require the Web API Key configuration."
|
|
385
|
+
] }) })
|
|
386
|
+
] })
|
|
387
|
+
] }),
|
|
388
|
+
/* @__PURE__ */ jsx(
|
|
389
|
+
Flex,
|
|
390
|
+
{
|
|
391
|
+
style: {
|
|
392
|
+
marginTop: 24,
|
|
393
|
+
width: "100%"
|
|
394
|
+
},
|
|
395
|
+
justifyContent: "flex-end",
|
|
396
|
+
children: /* @__PURE__ */ jsx(
|
|
397
|
+
Button,
|
|
398
|
+
{
|
|
399
|
+
size: "L",
|
|
400
|
+
onClick: handleFirebaseJsonSubmit,
|
|
401
|
+
disabled: !isJsonString(firebaseJsonValueInput),
|
|
402
|
+
children: "Submit Firebase Config"
|
|
403
|
+
}
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
),
|
|
407
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 6, paddingTop: 4, style: { borderTop: "1px solid #eaeaef" }, children: [
|
|
408
|
+
/* @__PURE__ */ jsx(Typography, { variant: "beta", marginBottom: 3, children: "How to setup Firebase Service Account JSON:" }),
|
|
409
|
+
/* @__PURE__ */ jsxs(Box, { padding: 3, background: "warning100", borderRadius: "4px", marginBottom: 4, children: [
|
|
410
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", textColor: "warning700", children: "⚠️ Security Warning" }),
|
|
411
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "warning700", marginTop: 1, children: "The Service Account JSON contains sensitive credentials with full admin access to your Firebase project. Never commit this file to version control or share it publicly." })
|
|
412
|
+
] }),
|
|
413
|
+
/* @__PURE__ */ jsx(Box, { marginLeft: 4, children: /* @__PURE__ */ jsxs("ol", { style: { listStyle: "decimal", paddingLeft: "20px" }, children: [
|
|
414
|
+
/* @__PURE__ */ jsx("li", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsxs(Typography, { children: [
|
|
415
|
+
"Go to the",
|
|
416
|
+
" ",
|
|
417
|
+
/* @__PURE__ */ jsx(
|
|
418
|
+
"a",
|
|
419
|
+
{
|
|
420
|
+
href: "https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk",
|
|
421
|
+
target: "_blank",
|
|
422
|
+
rel: "noreferrer",
|
|
423
|
+
children: "Service Accounts tab"
|
|
424
|
+
}
|
|
425
|
+
),
|
|
426
|
+
" ",
|
|
427
|
+
"in your Firebase Console (Project Settings → Service Accounts)"
|
|
428
|
+
] }) }),
|
|
429
|
+
/* @__PURE__ */ jsx("li", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsxs(Typography, { children: [
|
|
430
|
+
"Click the ",
|
|
431
|
+
/* @__PURE__ */ jsx("strong", { children: '"Generate New Private Key"' }),
|
|
432
|
+
" button"
|
|
433
|
+
] }) }),
|
|
434
|
+
/* @__PURE__ */ jsx("li", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsxs(Typography, { children: [
|
|
435
|
+
"Confirm by clicking ",
|
|
436
|
+
/* @__PURE__ */ jsx("strong", { children: '"Generate Key"' }),
|
|
437
|
+
" in the confirmation dialog"
|
|
438
|
+
] }) }),
|
|
439
|
+
/* @__PURE__ */ jsx("li", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsxs(Typography, { children: [
|
|
440
|
+
"A JSON file will be downloaded (e.g.,",
|
|
441
|
+
" ",
|
|
442
|
+
/* @__PURE__ */ jsx("code", { children: "your-project-firebase-adminsdk-xxxxx.json" }),
|
|
443
|
+
")"
|
|
444
|
+
] }) }),
|
|
445
|
+
/* @__PURE__ */ jsx("li", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsxs(Typography, { children: [
|
|
446
|
+
"Open the downloaded JSON file, copy its ",
|
|
447
|
+
/* @__PURE__ */ jsx("strong", { children: "entire contents" }),
|
|
448
|
+
', and paste it in the "Firebase Service Account JSON" field above'
|
|
449
|
+
] }) }),
|
|
450
|
+
/* @__PURE__ */ jsx("li", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsxs(Typography, { children: [
|
|
451
|
+
"Click ",
|
|
452
|
+
/* @__PURE__ */ jsx("strong", { children: "Submit" }),
|
|
453
|
+
" to save your configuration"
|
|
454
|
+
] }) }),
|
|
455
|
+
/* @__PURE__ */ jsx("li", { style: { marginTop: 16 }, children: /* @__PURE__ */ jsxs(Typography, { children: [
|
|
456
|
+
/* @__PURE__ */ jsx("em", { children: "(Optional)" }),
|
|
457
|
+
' If you need email/password authentication, expand "Optional: Email/Password Authentication" and add your Web API Key'
|
|
458
|
+
] }) })
|
|
459
|
+
] }) }),
|
|
460
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 4, padding: 3, background: "neutral100", borderRadius: "4px", children: [
|
|
461
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", children: "📝 Note: Service Account JSON vs Web App Config" }),
|
|
462
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "omega", marginTop: 2, children: [
|
|
463
|
+
/* @__PURE__ */ jsx("strong", { children: "Service Account JSON" }),
|
|
464
|
+
" (what you need here): Contains",
|
|
465
|
+
" ",
|
|
466
|
+
/* @__PURE__ */ jsx("code", { children: "private_key" }),
|
|
467
|
+
", ",
|
|
468
|
+
/* @__PURE__ */ jsx("code", { children: "client_email" }),
|
|
469
|
+
", etc. Used for server-side Firebase Admin SDK operations. Download from ",
|
|
470
|
+
/* @__PURE__ */ jsx("strong", { children: "Service Accounts tab" }),
|
|
471
|
+
"."
|
|
472
|
+
] }),
|
|
473
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "omega", marginTop: 2, children: [
|
|
474
|
+
/* @__PURE__ */ jsx("strong", { children: "Web App Config" }),
|
|
475
|
+
" (NOT what you need): Contains ",
|
|
476
|
+
/* @__PURE__ */ jsx("code", { children: "apiKey" }),
|
|
477
|
+
",",
|
|
478
|
+
" ",
|
|
479
|
+
/* @__PURE__ */ jsx("code", { children: "authDomain" }),
|
|
480
|
+
", etc. Used for client-side web apps. Found in SDK snippet - this is the wrong file!"
|
|
481
|
+
] })
|
|
482
|
+
] })
|
|
483
|
+
] })
|
|
484
|
+
] }) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Box, { padding: 4, background: "neutral0", children: [
|
|
485
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 4, children: [
|
|
486
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", style: { marginBottom: "8px" }, children: "Service Account Configuration" }),
|
|
487
|
+
/* @__PURE__ */ jsx(Box, { marginBottom: 3, children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", component: "span", children: [
|
|
488
|
+
/* @__PURE__ */ jsx("strong", { children: "Required" }),
|
|
489
|
+
" - Enables Firebase Admin SDK for server-side authentication"
|
|
490
|
+
] }) }),
|
|
491
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", justifyContent: "space-between", children: [
|
|
492
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
|
|
493
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "omega", textColor: "neutral600", children: [
|
|
494
|
+
"Project:",
|
|
495
|
+
" ",
|
|
496
|
+
firebaseJsonValue?.firebaseConfigJson && (() => {
|
|
497
|
+
try {
|
|
498
|
+
const config = JSON.parse(firebaseJsonValue.firebaseConfigJson);
|
|
499
|
+
return config.project_id || config.projectId || "Unknown Project";
|
|
500
|
+
} catch (e) {
|
|
501
|
+
return "Invalid Config";
|
|
502
|
+
}
|
|
503
|
+
})()
|
|
504
|
+
] }),
|
|
505
|
+
/* @__PURE__ */ jsx(Badge, { backgroundColor: "success200", textColor: "success700", size: "S", children: "✓ CONFIGURED" })
|
|
506
|
+
] }),
|
|
507
|
+
/* @__PURE__ */ jsx(Button, { variant: "danger-light", size: "S", onClick: handleDeleteFirebaseJsonConfig, children: "Delete Config" })
|
|
508
|
+
] })
|
|
509
|
+
] }),
|
|
510
|
+
/* @__PURE__ */ jsxs(Box, { paddingTop: 4, style: { borderTop: "1px solid #eaeaef" }, children: [
|
|
511
|
+
/* @__PURE__ */ jsx(Typography, { variant: "delta", fontWeight: "bold", style: { marginBottom: "8px" }, children: "Web API Key Configuration" }),
|
|
512
|
+
/* @__PURE__ */ jsx(Box, { marginBottom: 3, children: /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", component: "span", children: [
|
|
513
|
+
/* @__PURE__ */ jsx("strong", { children: "Optional" }),
|
|
514
|
+
" - Only needed for email/password login via emailLogin endpoint"
|
|
515
|
+
] }) }),
|
|
516
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", justifyContent: "space-between", children: [
|
|
517
|
+
/* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
|
|
518
|
+
firebaseWebApiKey?.trim() && /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral600", children: `${firebaseWebApiKey.substring(0, 10)}...` }),
|
|
519
|
+
firebaseWebApiKey?.trim() ? /* @__PURE__ */ jsx(Badge, { backgroundColor: "success200", textColor: "success700", size: "S", children: "✓ CONFIGURED" }) : /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral200", textColor: "neutral700", size: "S", children: "NOT SET" })
|
|
520
|
+
] }),
|
|
521
|
+
firebaseWebApiKey?.trim() ? /* @__PURE__ */ jsx(Button, { variant: "danger-light", size: "S", onClick: handleRemoveWebApiKey, children: "Delete Config" }) : /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "S", onClick: handleAddWebApiKey, children: "+ Add Web API Key" })
|
|
522
|
+
] })
|
|
523
|
+
] })
|
|
524
|
+
] }) });
|
|
525
|
+
})()
|
|
526
|
+
] }),
|
|
527
|
+
/* @__PURE__ */ jsxs(Box, { padding: 4, background: "neutral0", borderRadius: "4px", shadow: "filterShadow", marginBottom: 6, children: [
|
|
528
|
+
/* @__PURE__ */ jsx(Typography, { variant: "alpha", as: "h2", style: { display: "block", marginBottom: "8px" }, children: "Password Reset Settings" }),
|
|
529
|
+
/* @__PURE__ */ jsx(
|
|
530
|
+
Typography,
|
|
531
|
+
{
|
|
532
|
+
variant: "omega",
|
|
533
|
+
textColor: "neutral600",
|
|
534
|
+
style: { display: "block", marginBottom: "24px" },
|
|
535
|
+
children: "Configure password requirements and email settings for password reset functionality"
|
|
536
|
+
}
|
|
537
|
+
),
|
|
538
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 3, children: [
|
|
539
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", style: { display: "block", marginBottom: "8px" }, children: "Password Requirements (Regex)" }),
|
|
540
|
+
/* @__PURE__ */ jsx(
|
|
541
|
+
TextInput,
|
|
542
|
+
{
|
|
543
|
+
name: "passwordRequirementsRegex",
|
|
544
|
+
value: passwordRequirementsRegex,
|
|
545
|
+
onChange: (e) => setPasswordRequirementsRegex(e.target.value),
|
|
546
|
+
placeholder: "^.{6,}$",
|
|
547
|
+
hint: "Regular expression to validate password strength. Default: ^.{6,}$ (min 6 characters)"
|
|
548
|
+
}
|
|
549
|
+
)
|
|
550
|
+
] }),
|
|
551
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 3, children: [
|
|
552
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", style: { display: "block", marginBottom: "8px" }, children: "Password Requirements Message" }),
|
|
553
|
+
/* @__PURE__ */ jsx(
|
|
554
|
+
Textarea,
|
|
555
|
+
{
|
|
556
|
+
name: "passwordRequirementsMessage",
|
|
557
|
+
value: passwordRequirementsMessage,
|
|
558
|
+
onChange: (e) => setPasswordRequirementsMessage(e.target.value),
|
|
559
|
+
placeholder: "Password must be at least 6 characters long",
|
|
560
|
+
hint: "Error message shown when password doesn't meet requirements"
|
|
561
|
+
}
|
|
562
|
+
)
|
|
563
|
+
] }),
|
|
564
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 3, children: [
|
|
565
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", style: { display: "block", marginBottom: "8px" }, children: "Password Reset URL *" }),
|
|
566
|
+
/* @__PURE__ */ jsx(
|
|
567
|
+
TextInput,
|
|
568
|
+
{
|
|
569
|
+
name: "passwordResetUrl",
|
|
570
|
+
value: passwordResetUrl,
|
|
571
|
+
onChange: (e) => setPasswordResetUrl(e.target.value),
|
|
572
|
+
placeholder: "https://yourapp.com/reset-password",
|
|
573
|
+
hint: "URL where users will reset their password (your frontend application)",
|
|
574
|
+
required: true
|
|
575
|
+
}
|
|
576
|
+
)
|
|
577
|
+
] }),
|
|
578
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 3, children: [
|
|
579
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", style: { display: "block", marginBottom: "8px" }, children: "Reset Email Subject" }),
|
|
580
|
+
/* @__PURE__ */ jsx(
|
|
581
|
+
TextInput,
|
|
582
|
+
{
|
|
583
|
+
name: "passwordResetEmailSubject",
|
|
584
|
+
value: passwordResetEmailSubject,
|
|
585
|
+
onChange: (e) => setPasswordResetEmailSubject(e.target.value),
|
|
586
|
+
placeholder: "Reset Your Password",
|
|
587
|
+
hint: "Subject line for password reset emails"
|
|
588
|
+
}
|
|
589
|
+
)
|
|
590
|
+
] }),
|
|
591
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 4, padding: 3, background: "neutral100", borderRadius: "4px", children: [
|
|
592
|
+
/* @__PURE__ */ jsx(
|
|
593
|
+
Typography,
|
|
594
|
+
{
|
|
595
|
+
variant: "omega",
|
|
596
|
+
fontWeight: "bold",
|
|
597
|
+
style: { display: "block", marginBottom: "12px" },
|
|
598
|
+
children: "Common Password Patterns:"
|
|
599
|
+
}
|
|
600
|
+
),
|
|
601
|
+
/* @__PURE__ */ jsxs(Box, { marginLeft: 2, children: [
|
|
602
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "omega", style: { display: "block", marginBottom: "8px" }, children: [
|
|
603
|
+
"• ",
|
|
604
|
+
/* @__PURE__ */ jsx("code", { children: "^.{6,}$" }),
|
|
605
|
+
" - Minimum 6 characters (simple)"
|
|
606
|
+
] }),
|
|
607
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "omega", style: { display: "block", marginBottom: "8px" }, children: [
|
|
608
|
+
"• ",
|
|
609
|
+
/* @__PURE__ */ jsx("code", { children: "^.{8,}$" }),
|
|
610
|
+
" - Minimum 8 characters"
|
|
611
|
+
] }),
|
|
612
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "omega", style: { display: "block", marginBottom: "8px" }, children: [
|
|
613
|
+
"• ",
|
|
614
|
+
/* @__PURE__ */ jsx("code", { children: "^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$" }),
|
|
615
|
+
" - At least 8 chars with letters and numbers"
|
|
616
|
+
] }),
|
|
617
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "omega", style: { display: "block" }, children: [
|
|
618
|
+
"• ",
|
|
619
|
+
/* @__PURE__ */ jsx("code", { children: "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$" }),
|
|
620
|
+
" - Complex (upper, lower, number, special)"
|
|
621
|
+
] })
|
|
622
|
+
] })
|
|
623
|
+
] }),
|
|
624
|
+
/* @__PURE__ */ jsx(
|
|
625
|
+
Flex,
|
|
626
|
+
{
|
|
627
|
+
style: {
|
|
628
|
+
marginTop: 24,
|
|
629
|
+
width: "100%"
|
|
630
|
+
},
|
|
631
|
+
justifyContent: "flex-end",
|
|
632
|
+
children: /* @__PURE__ */ jsx(Button, { size: "L", variant: "secondary", onClick: handleSavePasswordSettings, children: "Save Password Settings" })
|
|
633
|
+
}
|
|
634
|
+
)
|
|
635
|
+
] }),
|
|
636
|
+
/* @__PURE__ */ jsxs(Box, { padding: 4, background: "neutral0", borderRadius: "4px", shadow: "filterShadow", marginBottom: 6, children: [
|
|
637
|
+
/* @__PURE__ */ jsx(Typography, { variant: "alpha", as: "h2", style: { display: "block", marginBottom: "8px" }, children: "Magic Link Authentication" }),
|
|
638
|
+
/* @__PURE__ */ jsx(
|
|
639
|
+
Typography,
|
|
640
|
+
{
|
|
641
|
+
variant: "omega",
|
|
642
|
+
textColor: "neutral600",
|
|
643
|
+
style: { display: "block", marginBottom: "24px" },
|
|
644
|
+
children: "Configure passwordless authentication via email magic links"
|
|
645
|
+
}
|
|
646
|
+
),
|
|
647
|
+
/* @__PURE__ */ jsx(Box, { marginBottom: 3, children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, children: [
|
|
648
|
+
/* @__PURE__ */ jsx(
|
|
649
|
+
Toggle,
|
|
650
|
+
{
|
|
651
|
+
name: "enableMagicLink",
|
|
652
|
+
label: "Enable Magic Link Authentication",
|
|
653
|
+
checked: enableMagicLink,
|
|
654
|
+
onChange: (e) => setEnableMagicLink(e.target.checked),
|
|
655
|
+
hint: "Allow users to sign in using magic links sent to their email"
|
|
656
|
+
}
|
|
657
|
+
),
|
|
658
|
+
/* @__PURE__ */ jsx(
|
|
659
|
+
Typography,
|
|
660
|
+
{
|
|
661
|
+
variant: "pi",
|
|
662
|
+
fontWeight: "bold",
|
|
663
|
+
textColor: enableMagicLink ? "success700" : "neutral400",
|
|
664
|
+
children: enableMagicLink ? "Active" : "Inactive"
|
|
665
|
+
}
|
|
666
|
+
)
|
|
667
|
+
] }) }),
|
|
668
|
+
enableMagicLink && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
669
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 3, children: [
|
|
670
|
+
/* @__PURE__ */ jsx(
|
|
671
|
+
Typography,
|
|
672
|
+
{
|
|
673
|
+
variant: "omega",
|
|
674
|
+
fontWeight: "bold",
|
|
675
|
+
style: { display: "block", marginBottom: "8px" },
|
|
676
|
+
children: "Verification URL *"
|
|
677
|
+
}
|
|
678
|
+
),
|
|
679
|
+
/* @__PURE__ */ jsx(
|
|
680
|
+
TextInput,
|
|
681
|
+
{
|
|
682
|
+
name: "magicLinkUrl",
|
|
683
|
+
value: magicLinkUrl,
|
|
684
|
+
onChange: (e) => setMagicLinkUrl(e.target.value),
|
|
685
|
+
placeholder: "https://yourapp.com/verify-magic-link",
|
|
686
|
+
hint: "URL where users complete magic link authentication (must handle client-side verification)",
|
|
687
|
+
required: true
|
|
688
|
+
}
|
|
689
|
+
)
|
|
690
|
+
] }),
|
|
691
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 3, children: [
|
|
692
|
+
/* @__PURE__ */ jsx(
|
|
693
|
+
Typography,
|
|
694
|
+
{
|
|
695
|
+
variant: "omega",
|
|
696
|
+
fontWeight: "bold",
|
|
697
|
+
style: { display: "block", marginBottom: "8px" },
|
|
698
|
+
children: "Email Subject"
|
|
699
|
+
}
|
|
700
|
+
),
|
|
701
|
+
/* @__PURE__ */ jsx(
|
|
702
|
+
TextInput,
|
|
703
|
+
{
|
|
704
|
+
name: "magicLinkEmailSubject",
|
|
705
|
+
value: magicLinkEmailSubject,
|
|
706
|
+
onChange: (e) => setMagicLinkEmailSubject(e.target.value),
|
|
707
|
+
placeholder: "Sign in to Your Application",
|
|
708
|
+
hint: "Subject line for magic link emails"
|
|
709
|
+
}
|
|
710
|
+
)
|
|
711
|
+
] }),
|
|
712
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 3, children: [
|
|
713
|
+
/* @__PURE__ */ jsx(
|
|
714
|
+
Typography,
|
|
715
|
+
{
|
|
716
|
+
variant: "omega",
|
|
717
|
+
fontWeight: "bold",
|
|
718
|
+
style: { display: "block", marginBottom: "8px" },
|
|
719
|
+
children: "Link Expiry (hours)"
|
|
720
|
+
}
|
|
721
|
+
),
|
|
722
|
+
/* @__PURE__ */ jsx(
|
|
723
|
+
NumberInput,
|
|
724
|
+
{
|
|
725
|
+
name: "magicLinkExpiryHours",
|
|
726
|
+
value: magicLinkExpiryHours,
|
|
727
|
+
onValueChange: (value) => setMagicLinkExpiryHours(value),
|
|
728
|
+
min: 1,
|
|
729
|
+
max: 72,
|
|
730
|
+
hint: "How long the magic link remains valid (1-72 hours)"
|
|
731
|
+
}
|
|
732
|
+
)
|
|
733
|
+
] }),
|
|
734
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 4, padding: 3, background: "primary100", borderRadius: "4px", children: [
|
|
735
|
+
/* @__PURE__ */ jsx(
|
|
736
|
+
Typography,
|
|
737
|
+
{
|
|
738
|
+
variant: "omega",
|
|
739
|
+
fontWeight: "bold",
|
|
740
|
+
style: { display: "block", marginBottom: "12px" },
|
|
741
|
+
children: "Setup Requirements:"
|
|
742
|
+
}
|
|
743
|
+
),
|
|
744
|
+
/* @__PURE__ */ jsxs(Box, { marginLeft: 2, children: [
|
|
745
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "omega", style: { display: "block", marginBottom: "8px" }, children: [
|
|
746
|
+
"1. ",
|
|
747
|
+
/* @__PURE__ */ jsx("strong", { children: "Firebase Console:" }),
|
|
748
|
+
' Enable "Email link (passwordless sign-in)" in Authentication → Sign-in method'
|
|
749
|
+
] }),
|
|
750
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "omega", style: { display: "block", marginBottom: "8px" }, children: [
|
|
751
|
+
"2. ",
|
|
752
|
+
/* @__PURE__ */ jsx("strong", { children: "Authorized Domains:" }),
|
|
753
|
+
" Add your domain to Firebase authorized domains list"
|
|
754
|
+
] }),
|
|
755
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "omega", style: { display: "block", marginBottom: "8px" }, children: [
|
|
756
|
+
"3. ",
|
|
757
|
+
/* @__PURE__ */ jsx("strong", { children: "Verification Page:" }),
|
|
758
|
+
" Deploy the client-side verification handler at the URL above"
|
|
759
|
+
] }),
|
|
760
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "omega", style: { display: "block", marginBottom: "8px" }, children: [
|
|
761
|
+
"4. ",
|
|
762
|
+
/* @__PURE__ */ jsx("strong", { children: "Email Service:" }),
|
|
763
|
+
" Configure Strapi Email plugin or custom email hook"
|
|
764
|
+
] })
|
|
765
|
+
] })
|
|
766
|
+
] }),
|
|
767
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 3, padding: 3, background: "neutral100", borderRadius: "4px", children: [
|
|
768
|
+
/* @__PURE__ */ jsx(
|
|
769
|
+
Typography,
|
|
770
|
+
{
|
|
771
|
+
variant: "omega",
|
|
772
|
+
fontWeight: "bold",
|
|
773
|
+
style: { display: "block", marginBottom: "8px" },
|
|
774
|
+
children: "🧪 Testing:"
|
|
775
|
+
}
|
|
776
|
+
),
|
|
777
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "omega", style: { display: "block" }, children: [
|
|
778
|
+
"Test page available at:",
|
|
779
|
+
" ",
|
|
780
|
+
/* @__PURE__ */ jsx("a", { href: "/test-magic-link.html", target: "_blank", rel: "noreferrer", children: "/test-magic-link.html" })
|
|
781
|
+
] }),
|
|
782
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", style: { display: "block", marginTop: "4px" }, children: "In development mode, magic links are logged to the server console." })
|
|
783
|
+
] })
|
|
784
|
+
] }),
|
|
785
|
+
/* @__PURE__ */ jsx(
|
|
786
|
+
Flex,
|
|
787
|
+
{
|
|
788
|
+
style: {
|
|
789
|
+
marginTop: 24,
|
|
790
|
+
width: "100%"
|
|
791
|
+
},
|
|
792
|
+
justifyContent: "flex-end",
|
|
793
|
+
children: /* @__PURE__ */ jsx(Button, { size: "L", variant: "secondary", onClick: handleSaveMagicLinkSettings, disabled: loading, children: "Save Magic Link Settings" })
|
|
794
|
+
}
|
|
795
|
+
)
|
|
796
|
+
] })
|
|
797
|
+
] }) }),
|
|
798
|
+
/* @__PURE__ */ jsx(Modal.Root, { open: showEditModal, onOpenChange: (open) => !open && setShowEditModal(false), children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
|
|
799
|
+
/* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: "Add Firebase Web API Key" }) }),
|
|
800
|
+
/* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsxs(Box, { padding: 4, children: [
|
|
801
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", marginBottom: 2, children: "Add your Firebase Web API Key to enable the emailLogin endpoint. This is optional and only needed if you want to authenticate users with email/password directly through your backend." }),
|
|
802
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 3, children: [
|
|
803
|
+
/* @__PURE__ */ jsx(
|
|
804
|
+
Typography,
|
|
805
|
+
{
|
|
806
|
+
variant: "omega",
|
|
807
|
+
fontWeight: "bold",
|
|
808
|
+
style: { display: "block", marginBottom: "8px" },
|
|
809
|
+
children: "Firebase Web API Key (Optional)"
|
|
810
|
+
}
|
|
811
|
+
),
|
|
812
|
+
/* @__PURE__ */ jsx(
|
|
813
|
+
TextInput,
|
|
814
|
+
{
|
|
815
|
+
name: "editWebApiKey",
|
|
816
|
+
value: editWebApiKey,
|
|
817
|
+
onChange: (e) => setEditWebApiKey(e.target.value),
|
|
818
|
+
placeholder: "AIzaSy... (your Web API Key)",
|
|
819
|
+
hint: "Only required for email/password login endpoint"
|
|
820
|
+
}
|
|
821
|
+
)
|
|
822
|
+
] }),
|
|
823
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 3, padding: 2, background: "primary100", borderRadius: "4px", children: [
|
|
824
|
+
/* @__PURE__ */ jsx(Typography, { variant: "omega", fontWeight: "bold", children: "📍 Where to find your Web API Key:" }),
|
|
825
|
+
/* @__PURE__ */ jsxs("ol", { style: { marginLeft: 20, marginTop: 8 }, children: [
|
|
826
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
|
|
827
|
+
"Go to",
|
|
828
|
+
" ",
|
|
829
|
+
/* @__PURE__ */ jsx("a", { href: "https://console.firebase.google.com", target: "_blank", rel: "noreferrer", children: "Firebase Console" })
|
|
830
|
+
] }) }),
|
|
831
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Typography, { variant: "omega", children: "Select your project" }) }),
|
|
832
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
|
|
833
|
+
"Click the gear icon ⚙️ → ",
|
|
834
|
+
/* @__PURE__ */ jsx("strong", { children: "Project Settings" })
|
|
835
|
+
] }) }),
|
|
836
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
|
|
837
|
+
"In the ",
|
|
838
|
+
/* @__PURE__ */ jsx("strong", { children: "General" }),
|
|
839
|
+
" tab, scroll down to ",
|
|
840
|
+
/* @__PURE__ */ jsx("strong", { children: "Your apps" })
|
|
841
|
+
] }) }),
|
|
842
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
|
|
843
|
+
"Find ",
|
|
844
|
+
/* @__PURE__ */ jsx("strong", { children: "Web API Key" }),
|
|
845
|
+
" (looks like: AIzaSyB3Xd...)"
|
|
846
|
+
] }) })
|
|
847
|
+
] })
|
|
848
|
+
] })
|
|
849
|
+
] }) }),
|
|
850
|
+
/* @__PURE__ */ jsx(Modal.Footer, { children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "flex-end", gap: 2, children: [
|
|
851
|
+
/* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: () => setShowEditModal(false), children: "Cancel" }),
|
|
852
|
+
/* @__PURE__ */ jsx(Button, { variant: "default", onClick: handleSaveEditConfiguration, children: "Add API Key" })
|
|
853
|
+
] }) })
|
|
854
|
+
] }) })
|
|
855
|
+
] });
|
|
856
|
+
}
|
|
857
|
+
export {
|
|
858
|
+
SettingsPage as default
|
|
859
|
+
};
|