@rebasepro/auth 0.0.1-canary.eae7889 → 0.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/dist/api.d.ts +21 -3
- package/dist/components/RebaseLoginView.d.ts +22 -0
- package/dist/index.es.js +127 -78
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +127 -78
- package/dist/index.umd.js.map +1 -1
- package/dist/types.d.ts +8 -2
- package/package.json +4 -4
- package/src/api.ts +49 -9
- package/src/components/AdminViews.tsx +19 -5
- package/src/components/RebaseLoginView.tsx +70 -39
- package/src/hooks/useBackendUserManagement.ts +53 -8
- package/src/hooks/useRebaseAuthController.ts +11 -3
- package/src/types.ts +5 -2
package/dist/index.umd.js
CHANGED
|
@@ -71,11 +71,12 @@
|
|
|
71
71
|
});
|
|
72
72
|
return handleResponse(response);
|
|
73
73
|
}
|
|
74
|
-
async function googleLogin(idToken) {
|
|
74
|
+
async function googleLogin(tokenOrPayload, tokenType = "idToken") {
|
|
75
|
+
const body = typeof tokenOrPayload === "string" ? { [tokenType]: tokenOrPayload } : tokenOrPayload;
|
|
75
76
|
const response = await fetchWithHandling(`${baseApiUrl}/api/auth/google`, {
|
|
76
77
|
method: "POST",
|
|
77
78
|
headers: { "Content-Type": "application/json" },
|
|
78
|
-
body: JSON.stringify(
|
|
79
|
+
body: JSON.stringify(body)
|
|
79
80
|
});
|
|
80
81
|
return handleResponse(response);
|
|
81
82
|
}
|
|
@@ -195,12 +196,23 @@
|
|
|
195
196
|
});
|
|
196
197
|
return handleResponse(response);
|
|
197
198
|
}
|
|
199
|
+
let authConfigInflight = null;
|
|
198
200
|
async function fetchAuthConfig() {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
201
|
+
if (authConfigInflight) {
|
|
202
|
+
return authConfigInflight;
|
|
203
|
+
}
|
|
204
|
+
authConfigInflight = (async () => {
|
|
205
|
+
const response = await fetchWithHandling(`${baseApiUrl}/api/auth/config`, {
|
|
206
|
+
method: "GET",
|
|
207
|
+
headers: { "Content-Type": "application/json" }
|
|
208
|
+
});
|
|
209
|
+
return handleResponse(response);
|
|
210
|
+
})();
|
|
211
|
+
try {
|
|
212
|
+
return await authConfigInflight;
|
|
213
|
+
} finally {
|
|
214
|
+
authConfigInflight = null;
|
|
215
|
+
}
|
|
204
216
|
}
|
|
205
217
|
const STORAGE_KEY = "rebase_auth";
|
|
206
218
|
const TOKEN_REFRESH_BUFFER_MS = 2 * 60 * 1e3;
|
|
@@ -411,6 +423,7 @@
|
|
|
411
423
|
}
|
|
412
424
|
}, [client, getAuthToken, refreshAccessToken$1, clearSessionAndSignOut]);
|
|
413
425
|
const handleAuthSuccess = react.useCallback(async (userInfo, tokens) => {
|
|
426
|
+
console.log("[Auth] handleAuthSuccess called, user:", userInfo.email, "uid:", userInfo.uid);
|
|
414
427
|
tokensRef.current = tokens;
|
|
415
428
|
let convertedUser = convertToUser(userInfo);
|
|
416
429
|
if (defineRolesFor) {
|
|
@@ -423,11 +436,13 @@
|
|
|
423
436
|
}
|
|
424
437
|
}
|
|
425
438
|
saveAuthToStorage(tokens, userInfo);
|
|
439
|
+
console.log("[Auth] Calling setUser, roles:", convertedUser.roles);
|
|
426
440
|
setUser(convertedUser);
|
|
427
441
|
setAuthError(null);
|
|
428
442
|
setAuthProviderError(null);
|
|
429
443
|
setLoginSkipped(false);
|
|
430
444
|
scheduleTokenRefresh(tokens);
|
|
445
|
+
console.log("[Auth] handleAuthSuccess completed");
|
|
431
446
|
}, [scheduleTokenRefresh, defineRolesFor]);
|
|
432
447
|
const emailPasswordLogin = react.useCallback(async (email, password) => {
|
|
433
448
|
setAuthLoading(true);
|
|
@@ -455,11 +470,11 @@
|
|
|
455
470
|
setAuthLoading(false);
|
|
456
471
|
}
|
|
457
472
|
}, [handleAuthSuccess]);
|
|
458
|
-
const googleLogin$1 = react.useCallback(async (
|
|
473
|
+
const googleLogin$1 = react.useCallback(async (tokenOrPayload, tokenType) => {
|
|
459
474
|
setAuthLoading(true);
|
|
460
475
|
setAuthProviderError(null);
|
|
461
476
|
try {
|
|
462
|
-
const response = await googleLogin(idToken);
|
|
477
|
+
const response = typeof tokenOrPayload === "string" ? await googleLogin(tokenOrPayload, tokenType ?? "idToken") : await googleLogin(tokenOrPayload);
|
|
463
478
|
await handleAuthSuccess(response.user, response.tokens);
|
|
464
479
|
} catch (error) {
|
|
465
480
|
setAuthProviderError(error);
|
|
@@ -794,6 +809,8 @@
|
|
|
794
809
|
const [loading, setLoading] = react.useState(true);
|
|
795
810
|
const [usersError, setUsersError] = react.useState();
|
|
796
811
|
const [rolesError, setRolesError] = react.useState();
|
|
812
|
+
const lastLoadedUidRef = react.useRef(null);
|
|
813
|
+
const apiRequestRef = react.useRef(null);
|
|
797
814
|
const apiRequest = react.useCallback(async (endpoint, method = "GET", body, retryCount = 6, signal) => {
|
|
798
815
|
let lastError = null;
|
|
799
816
|
for (let attempt = 0; attempt < retryCount; attempt++) {
|
|
@@ -861,6 +878,7 @@
|
|
|
861
878
|
}
|
|
862
879
|
throw lastError;
|
|
863
880
|
}, [apiUrl, getAuthToken]);
|
|
881
|
+
apiRequestRef.current = apiRequest;
|
|
864
882
|
react.useCallback(async (signal) => {
|
|
865
883
|
try {
|
|
866
884
|
const data = await apiRequest("/roles", "GET", void 0, 6, signal);
|
|
@@ -889,23 +907,43 @@
|
|
|
889
907
|
setLoading(false);
|
|
890
908
|
return;
|
|
891
909
|
}
|
|
910
|
+
if (lastLoadedUidRef.current === currentUser.uid) {
|
|
911
|
+
setLoading(false);
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
892
914
|
const abortController = new AbortController();
|
|
893
915
|
const load = async () => {
|
|
894
916
|
setLoading(true);
|
|
917
|
+
const request = apiRequestRef.current;
|
|
895
918
|
try {
|
|
896
|
-
const data = await
|
|
919
|
+
const data = await request("/roles", "GET", void 0, 6, abortController.signal);
|
|
897
920
|
setRoles(data.roles.map(convertRole));
|
|
898
921
|
setRolesError(void 0);
|
|
899
922
|
} catch (error) {
|
|
900
|
-
if (error instanceof Error && error.name
|
|
901
|
-
|
|
902
|
-
|
|
923
|
+
if (error instanceof Error && error.name === "AbortError") return;
|
|
924
|
+
console.error("Failed to load roles:", error);
|
|
925
|
+
setRolesError(error instanceof Error ? error : new Error(String(error)));
|
|
926
|
+
const status = error.status;
|
|
927
|
+
if (status === 403 || status === 401) {
|
|
928
|
+
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
929
|
+
setLoading(false);
|
|
930
|
+
return;
|
|
903
931
|
}
|
|
904
932
|
}
|
|
905
933
|
if (!abortController.signal.aborted) {
|
|
906
|
-
|
|
934
|
+
try {
|
|
935
|
+
const data = await request("/users", "GET", void 0, 6, abortController.signal);
|
|
936
|
+
const allUsers = data.users.map((u) => convertUser(u));
|
|
937
|
+
setUsers(allUsers);
|
|
938
|
+
setUsersError(void 0);
|
|
939
|
+
} catch (error) {
|
|
940
|
+
if (error instanceof Error && error.name === "AbortError") return;
|
|
941
|
+
console.error("Failed to load users:", error);
|
|
942
|
+
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
943
|
+
}
|
|
907
944
|
}
|
|
908
945
|
if (!abortController.signal.aborted) {
|
|
946
|
+
lastLoadedUidRef.current = currentUser.uid;
|
|
909
947
|
setLoading(false);
|
|
910
948
|
}
|
|
911
949
|
};
|
|
@@ -913,7 +951,7 @@
|
|
|
913
951
|
return () => {
|
|
914
952
|
abortController.abort();
|
|
915
953
|
};
|
|
916
|
-
}, [currentUser
|
|
954
|
+
}, [currentUser?.uid]);
|
|
917
955
|
const searchUsers = react.useCallback(async (options) => {
|
|
918
956
|
const params = new URLSearchParams();
|
|
919
957
|
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
@@ -1114,7 +1152,7 @@
|
|
|
1114
1152
|
"div",
|
|
1115
1153
|
{
|
|
1116
1154
|
className: ui.cls(
|
|
1117
|
-
"relative flex items-center justify-center h-screen w-screen p-4 transition-opacity duration-500 bg-white dark:bg-surface-
|
|
1155
|
+
"relative flex items-center justify-center h-screen w-screen p-4 transition-opacity duration-500 bg-white dark:bg-surface-900",
|
|
1118
1156
|
fadeIn ? "opacity-100" : "opacity-0"
|
|
1119
1157
|
),
|
|
1120
1158
|
children: [
|
|
@@ -1268,74 +1306,79 @@
|
|
|
1268
1306
|
}
|
|
1269
1307
|
);
|
|
1270
1308
|
}
|
|
1309
|
+
const GoogleIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", width: "20", height: "20", children: [
|
|
1310
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1311
|
+
"path",
|
|
1312
|
+
{
|
|
1313
|
+
fill: "#4285F4",
|
|
1314
|
+
d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
1315
|
+
}
|
|
1316
|
+
),
|
|
1317
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1318
|
+
"path",
|
|
1319
|
+
{
|
|
1320
|
+
fill: "#34A853",
|
|
1321
|
+
d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
1322
|
+
}
|
|
1323
|
+
),
|
|
1324
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1325
|
+
"path",
|
|
1326
|
+
{
|
|
1327
|
+
fill: "#FBBC05",
|
|
1328
|
+
d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
|
1329
|
+
}
|
|
1330
|
+
),
|
|
1331
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1332
|
+
"path",
|
|
1333
|
+
{
|
|
1334
|
+
fill: "#EA4335",
|
|
1335
|
+
d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
1336
|
+
}
|
|
1337
|
+
)
|
|
1338
|
+
] });
|
|
1271
1339
|
function GoogleLoginButton({
|
|
1272
1340
|
disabled,
|
|
1273
1341
|
googleClientId,
|
|
1274
1342
|
authController
|
|
1275
1343
|
}) {
|
|
1276
|
-
const
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
} catch (err) {
|
|
1289
|
-
console.error("Google login error:", err);
|
|
1290
|
-
}
|
|
1344
|
+
const codeClientRef = react.useRef(null);
|
|
1345
|
+
react.useEffect(() => {
|
|
1346
|
+
const google = window.google;
|
|
1347
|
+
if (!google || codeClientRef.current) return;
|
|
1348
|
+
codeClientRef.current = google.accounts.oauth2.initCodeClient({
|
|
1349
|
+
client_id: googleClientId,
|
|
1350
|
+
scope: "openid email profile",
|
|
1351
|
+
ux_mode: "popup",
|
|
1352
|
+
callback: async (response) => {
|
|
1353
|
+
if (response.error || !response.code) {
|
|
1354
|
+
console.error("Google login error:", response.error);
|
|
1355
|
+
return;
|
|
1291
1356
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1357
|
+
try {
|
|
1358
|
+
await authController.googleLogin({
|
|
1359
|
+
code: response.code,
|
|
1360
|
+
redirectUri: "postmessage"
|
|
1361
|
+
});
|
|
1362
|
+
} catch (err) {
|
|
1363
|
+
console.error("Google login error:", err);
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
}, [googleClientId, authController]);
|
|
1368
|
+
const handleClick = () => {
|
|
1369
|
+
if (!codeClientRef.current) {
|
|
1370
|
+
console.error("Google Sign-In not loaded");
|
|
1371
|
+
return;
|
|
1296
1372
|
}
|
|
1373
|
+
codeClientRef.current.requestCode();
|
|
1297
1374
|
};
|
|
1298
1375
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1299
|
-
|
|
1376
|
+
LoginButton,
|
|
1300
1377
|
{
|
|
1301
1378
|
disabled,
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
onClick: handleGoogleLogin,
|
|
1306
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center w-full gap-3 py-1", children: [
|
|
1307
|
-
/* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", width: "20", height: "20", children: [
|
|
1308
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1309
|
-
"path",
|
|
1310
|
-
{
|
|
1311
|
-
fill: "#4285F4",
|
|
1312
|
-
d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
|
|
1313
|
-
}
|
|
1314
|
-
),
|
|
1315
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1316
|
-
"path",
|
|
1317
|
-
{
|
|
1318
|
-
fill: "#34A853",
|
|
1319
|
-
d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
|
|
1320
|
-
}
|
|
1321
|
-
),
|
|
1322
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1323
|
-
"path",
|
|
1324
|
-
{
|
|
1325
|
-
fill: "#FBBC05",
|
|
1326
|
-
d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
|
|
1327
|
-
}
|
|
1328
|
-
),
|
|
1329
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1330
|
-
"path",
|
|
1331
|
-
{
|
|
1332
|
-
fill: "#EA4335",
|
|
1333
|
-
d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
|
|
1334
|
-
}
|
|
1335
|
-
)
|
|
1336
|
-
] }),
|
|
1337
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "button", children: "Continue with Google" })
|
|
1338
|
-
] })
|
|
1379
|
+
text: "Sign in with Google",
|
|
1380
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(GoogleIcon, {}),
|
|
1381
|
+
onClick: handleClick
|
|
1339
1382
|
}
|
|
1340
1383
|
);
|
|
1341
1384
|
}
|
|
@@ -1456,6 +1499,7 @@
|
|
|
1456
1499
|
{
|
|
1457
1500
|
type: "submit",
|
|
1458
1501
|
variant: "filled",
|
|
1502
|
+
color: "primary",
|
|
1459
1503
|
className: "w-full mt-1",
|
|
1460
1504
|
size: "large",
|
|
1461
1505
|
loading: authController.authLoading,
|
|
@@ -1577,6 +1621,7 @@
|
|
|
1577
1621
|
{
|
|
1578
1622
|
type: "submit",
|
|
1579
1623
|
variant: "filled",
|
|
1624
|
+
color: "primary",
|
|
1580
1625
|
className: "w-full",
|
|
1581
1626
|
size: "large",
|
|
1582
1627
|
loading: authController.authLoading,
|
|
@@ -1604,14 +1649,12 @@
|
|
|
1604
1649
|
{
|
|
1605
1650
|
slug: "dev/users",
|
|
1606
1651
|
name: "CMS Users",
|
|
1607
|
-
group: "Admin",
|
|
1608
1652
|
icon: "face",
|
|
1609
1653
|
view: /* @__PURE__ */ jsxRuntime.jsx(UsersView, { userManagement, apiUrl, getAuthToken })
|
|
1610
1654
|
},
|
|
1611
1655
|
{
|
|
1612
1656
|
slug: "dev/roles",
|
|
1613
1657
|
name: "Roles",
|
|
1614
|
-
group: "Admin",
|
|
1615
1658
|
icon: "gpp_good",
|
|
1616
1659
|
view: /* @__PURE__ */ jsxRuntime.jsx(RolesView, { userManagement, collections })
|
|
1617
1660
|
}
|
|
@@ -1632,6 +1675,7 @@
|
|
|
1632
1675
|
}
|
|
1633
1676
|
function UsersView({ userManagement, apiUrl, getAuthToken }) {
|
|
1634
1677
|
const { users, roles, saveUser, deleteUser, loading } = userManagement;
|
|
1678
|
+
const usersError = "usersError" in userManagement ? userManagement.usersError : void 0;
|
|
1635
1679
|
const snackbarController = core.useSnackbarController();
|
|
1636
1680
|
const { user: loggedInUser } = core.useAuthController();
|
|
1637
1681
|
const [dialogOpen, setDialogOpen] = react.useState(false);
|
|
@@ -1751,7 +1795,10 @@
|
|
|
1751
1795
|
return role ? /* @__PURE__ */ jsxRuntime.jsx(RoleChip, { role }, roleId) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: roleId }, roleId);
|
|
1752
1796
|
}) }) })
|
|
1753
1797
|
] }, user.uid)),
|
|
1754
|
-
users.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.TableRow, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.TableCell, { colspan: 4, children: /* @__PURE__ */ jsxRuntime.
|
|
1798
|
+
users.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.TableRow, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.TableCell, { colspan: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.CenteredView, { className: "flex flex-col gap-4 my-8 items-center", children: [
|
|
1799
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "label", children: usersError ? "You don't have permission to view users" : "There are no users yet" }),
|
|
1800
|
+
usersError && /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", className: "text-surface-500", children: "Contact an administrator if you need access to this section." })
|
|
1801
|
+
] }) }) })
|
|
1755
1802
|
] })
|
|
1756
1803
|
] }) }),
|
|
1757
1804
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1908,6 +1955,7 @@
|
|
|
1908
1955
|
}
|
|
1909
1956
|
function RolesView({ userManagement, collections = [] }) {
|
|
1910
1957
|
const { roles, saveRole, deleteRole, loading, allowDefaultRolesCreation } = userManagement;
|
|
1958
|
+
const rolesError = "rolesError" in userManagement ? userManagement.rolesError : void 0;
|
|
1911
1959
|
const snackbarController = core.useSnackbarController();
|
|
1912
1960
|
const [dialogOpen, setDialogOpen] = react.useState(false);
|
|
1913
1961
|
const [selectedRole, setSelectedRole] = react.useState();
|
|
@@ -2000,8 +2048,9 @@
|
|
|
2000
2048
|
] }, role.id);
|
|
2001
2049
|
}),
|
|
2002
2050
|
roles.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.TableRow, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.TableCell, { colspan: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.CenteredView, { className: "flex flex-col gap-4 my-8 items-center", children: [
|
|
2003
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "label", children: "You don't have any roles yet." }),
|
|
2004
|
-
|
|
2051
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "label", children: rolesError ? "You don't have permission to view roles" : "You don't have any roles yet." }),
|
|
2052
|
+
rolesError && /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "caption", className: "text-surface-500", children: "Contact an administrator if you need access to this section." }),
|
|
2053
|
+
!rolesError && allowDefaultRolesCreation && /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: createDefaultRoles, children: "Create default roles" })
|
|
2005
2054
|
] }) }) })
|
|
2006
2055
|
] })
|
|
2007
2056
|
] }) }),
|