@rebasepro/auth 0.0.1-canary.f81da60 → 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/index.umd.js CHANGED
@@ -71,11 +71,12 @@
71
71
  });
72
72
  return handleResponse(response);
73
73
  }
74
- async function googleLogin(token, tokenType = "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({ [tokenType]: token })
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
- const response = await fetchWithHandling(`${baseApiUrl}/api/auth/config`, {
200
- method: "GET",
201
- headers: { "Content-Type": "application/json" }
202
- });
203
- return handleResponse(response);
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 (token, tokenType = "idToken") => {
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(token, tokenType);
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 apiRequest("/roles", "GET", void 0, 6, abortController.signal);
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 !== "AbortError") {
901
- console.error("Failed to load roles:", error);
902
- setRolesError(error);
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
- await loadUsers(abortController.signal);
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, apiRequest, loadUsers]);
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));
@@ -1192,17 +1230,22 @@
1192
1230
  authController
1193
1231
  }
1194
1232
  ),
1195
- showRegistration && /* @__PURE__ */ jsxRuntime.jsx(
1196
- ui.Button,
1197
- {
1198
- className: "w-full",
1199
- variant: "filled",
1200
- color: "primary",
1201
- size: "large",
1202
- onClick: () => switchMode("register"),
1203
- children: "Create an account"
1204
- }
1205
- )
1233
+ showRegistration && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-center", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Typography, { variant: "body2", color: "secondary", children: [
1234
+ "Don't have an account?",
1235
+ " ",
1236
+ /* @__PURE__ */ jsxRuntime.jsx(
1237
+ "button",
1238
+ {
1239
+ type: "button",
1240
+ className: ui.cls(
1241
+ "font-semibold hover:underline cursor-pointer",
1242
+ "text-primary-600 dark:text-primary-400"
1243
+ ),
1244
+ onClick: () => switchMode("register"),
1245
+ children: "Create one"
1246
+ }
1247
+ )
1248
+ ] }) })
1206
1249
  ] }),
1207
1250
  mode === "login" && /* @__PURE__ */ jsxRuntime.jsx(
1208
1251
  LoginForm,
@@ -1298,20 +1341,24 @@
1298
1341
  googleClientId,
1299
1342
  authController
1300
1343
  }) {
1301
- const tokenClientRef = react.useRef(null);
1344
+ const codeClientRef = react.useRef(null);
1302
1345
  react.useEffect(() => {
1303
1346
  const google = window.google;
1304
- if (!google || tokenClientRef.current) return;
1305
- tokenClientRef.current = google.accounts.oauth2.initTokenClient({
1347
+ if (!google || codeClientRef.current) return;
1348
+ codeClientRef.current = google.accounts.oauth2.initCodeClient({
1306
1349
  client_id: googleClientId,
1307
1350
  scope: "openid email profile",
1351
+ ux_mode: "popup",
1308
1352
  callback: async (response) => {
1309
- if (response.error || !response.access_token) {
1353
+ if (response.error || !response.code) {
1310
1354
  console.error("Google login error:", response.error);
1311
1355
  return;
1312
1356
  }
1313
1357
  try {
1314
- await authController.googleLogin(response.access_token, "accessToken");
1358
+ await authController.googleLogin({
1359
+ code: response.code,
1360
+ redirectUri: "postmessage"
1361
+ });
1315
1362
  } catch (err) {
1316
1363
  console.error("Google login error:", err);
1317
1364
  }
@@ -1319,11 +1366,11 @@
1319
1366
  });
1320
1367
  }, [googleClientId, authController]);
1321
1368
  const handleClick = () => {
1322
- if (!tokenClientRef.current) {
1369
+ if (!codeClientRef.current) {
1323
1370
  console.error("Google Sign-In not loaded");
1324
1371
  return;
1325
1372
  }
1326
- tokenClientRef.current.requestAccessToken();
1373
+ codeClientRef.current.requestCode();
1327
1374
  };
1328
1375
  return /* @__PURE__ */ jsxRuntime.jsx(
1329
1376
  LoginButton,
@@ -1602,14 +1649,12 @@
1602
1649
  {
1603
1650
  slug: "dev/users",
1604
1651
  name: "CMS Users",
1605
- group: "Admin",
1606
1652
  icon: "face",
1607
1653
  view: /* @__PURE__ */ jsxRuntime.jsx(UsersView, { userManagement, apiUrl, getAuthToken })
1608
1654
  },
1609
1655
  {
1610
1656
  slug: "dev/roles",
1611
1657
  name: "Roles",
1612
- group: "Admin",
1613
1658
  icon: "gpp_good",
1614
1659
  view: /* @__PURE__ */ jsxRuntime.jsx(RolesView, { userManagement, collections })
1615
1660
  }
@@ -1707,7 +1752,7 @@
1707
1752
  return /* @__PURE__ */ jsxRuntime.jsx(ui.CenteredView, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.CircularProgress, {}) });
1708
1753
  }
1709
1754
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "w-full flex flex-col py-4 gap-4", maxWidth: "6xl", children: [
1710
- !hasAdmin && !usersError && loggedInUser && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-yellow-100 dark:bg-yellow-900 border border-yellow-400 dark:border-yellow-700 rounded p-4 flex items-center justify-between", children: [
1755
+ !hasAdmin && loggedInUser && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-yellow-100 dark:bg-yellow-900 border border-yellow-400 dark:border-yellow-700 rounded p-4 flex items-center justify-between", children: [
1711
1756
  /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Typography, { variant: "label", className: "text-yellow-800 dark:text-yellow-200", children: "No admin users exist. You can make yourself an admin." }) }),
1712
1757
  /* @__PURE__ */ jsxRuntime.jsx(
1713
1758
  ui.Button,