@rebasepro/auth 0.0.1-canary.892f711 → 0.0.1-canary.a6becfb
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 +20 -2
- package/dist/components/RebaseLoginView.d.ts +22 -0
- package/dist/index.es.js +59 -31
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +58 -30
- 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 +1 -3
- package/src/components/RebaseLoginView.tsx +48 -18
- package/src/hooks/useBackendUserManagement.ts +12 -0
- package/src/hooks/useRebaseAuthController.ts +11 -3
- package/src/types.ts +5 -2
package/dist/api.d.ts
CHANGED
|
@@ -20,8 +20,23 @@ export declare function register(email: string, password: string, displayName?:
|
|
|
20
20
|
*/
|
|
21
21
|
export declare function login(email: string, password: string): Promise<AuthResponse>;
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* Google login payload — one of the three supported flows.
|
|
24
24
|
*/
|
|
25
|
+
export type GoogleLoginPayload = {
|
|
26
|
+
idToken: string;
|
|
27
|
+
} | {
|
|
28
|
+
accessToken: string;
|
|
29
|
+
} | {
|
|
30
|
+
code: string;
|
|
31
|
+
redirectUri: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Login with Google.
|
|
35
|
+
*
|
|
36
|
+
* Overload 1 (legacy): `googleLogin("token", "idToken" | "accessToken")`
|
|
37
|
+
* Overload 2 (code flow): `googleLogin({ code, redirectUri })`
|
|
38
|
+
*/
|
|
39
|
+
export declare function googleLogin(payload: GoogleLoginPayload): Promise<AuthResponse>;
|
|
25
40
|
export declare function googleLogin(token: string, tokenType?: "idToken" | "accessToken"): Promise<AuthResponse>;
|
|
26
41
|
/**
|
|
27
42
|
* Login with LinkedIn OAuth code
|
|
@@ -126,7 +141,10 @@ export interface AuthConfigResponse {
|
|
|
126
141
|
}
|
|
127
142
|
/**
|
|
128
143
|
* Fetch auth configuration / status from the backend
|
|
129
|
-
* This is an unauthenticated endpoint used to detect bootstrap mode
|
|
144
|
+
* This is an unauthenticated endpoint used to detect bootstrap mode.
|
|
145
|
+
*
|
|
146
|
+
* Concurrent calls are deduplicated: only one network request is made
|
|
147
|
+
* and all callers share the same promise.
|
|
130
148
|
*/
|
|
131
149
|
export declare function fetchAuthConfig(): Promise<AuthConfigResponse>;
|
|
132
150
|
export { AuthApiError };
|
|
@@ -49,3 +49,25 @@ export interface RebaseLoginViewProps {
|
|
|
49
49
|
* Login view component for custom JWT authentication
|
|
50
50
|
*/
|
|
51
51
|
export declare function RebaseLoginView({ logo, authController, noUserComponent, disableSignupScreen, disabled, notAllowedError, googleEnabled, googleClientId }: RebaseLoginViewProps): import("react/jsx-runtime").JSX.Element;
|
|
52
|
+
/** Google Identity Services SDK — injected by the GIS <script> tag. */
|
|
53
|
+
declare global {
|
|
54
|
+
interface Window {
|
|
55
|
+
google?: {
|
|
56
|
+
accounts: {
|
|
57
|
+
oauth2: {
|
|
58
|
+
initCodeClient(config: {
|
|
59
|
+
client_id: string;
|
|
60
|
+
scope: string;
|
|
61
|
+
ux_mode: "popup" | "redirect";
|
|
62
|
+
callback: (response: {
|
|
63
|
+
code?: string;
|
|
64
|
+
error?: string;
|
|
65
|
+
}) => void;
|
|
66
|
+
}): {
|
|
67
|
+
requestCode(): void;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
package/dist/index.es.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState, useRef, useEffect, useCallback, useLayoutEffect } from "react";
|
|
2
2
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { Menu, MenuItem, iconSize, IconButton,
|
|
3
|
+
import { Menu, MenuItem, iconSize, IconButton, Typography, cls, TextField, LoadingButton, Button, CenteredView, CircularProgress, Container, Table, TableHeader, TableCell, TableBody, TableRow, Tooltip, Checkbox, getColorSchemeForSeed, Chip, Dialog, DialogTitle, DialogContent, DialogActions, MultiSelect, MultiSelectItem } from "@rebasepro/ui";
|
|
4
4
|
import { MoonIcon, SunIcon, SunMoonIcon, MailIcon, ArrowLeftIcon, PlusIcon, Trash2Icon } from "lucide-react";
|
|
5
5
|
import { useModeController, useTranslation, LanguageToggle, ErrorView, RebaseLogo, useRebaseRegistryDispatch, useSnackbarController, ConfirmationDialog, useAuthController } from "@rebasepro/core";
|
|
6
6
|
let baseApiUrl = "";
|
|
@@ -72,11 +72,12 @@ async function login(email, password) {
|
|
|
72
72
|
});
|
|
73
73
|
return handleResponse(response);
|
|
74
74
|
}
|
|
75
|
-
async function googleLogin(
|
|
75
|
+
async function googleLogin(tokenOrPayload, tokenType = "idToken") {
|
|
76
|
+
const body = typeof tokenOrPayload === "string" ? { [tokenType]: tokenOrPayload } : tokenOrPayload;
|
|
76
77
|
const response = await fetchWithHandling(`${baseApiUrl}/api/auth/google`, {
|
|
77
78
|
method: "POST",
|
|
78
79
|
headers: { "Content-Type": "application/json" },
|
|
79
|
-
body: JSON.stringify(
|
|
80
|
+
body: JSON.stringify(body)
|
|
80
81
|
});
|
|
81
82
|
return handleResponse(response);
|
|
82
83
|
}
|
|
@@ -196,12 +197,23 @@ async function revokeAllSessions(accessToken) {
|
|
|
196
197
|
});
|
|
197
198
|
return handleResponse(response);
|
|
198
199
|
}
|
|
200
|
+
let authConfigInflight = null;
|
|
199
201
|
async function fetchAuthConfig() {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
if (authConfigInflight) {
|
|
203
|
+
return authConfigInflight;
|
|
204
|
+
}
|
|
205
|
+
authConfigInflight = (async () => {
|
|
206
|
+
const response = await fetchWithHandling(`${baseApiUrl}/api/auth/config`, {
|
|
207
|
+
method: "GET",
|
|
208
|
+
headers: { "Content-Type": "application/json" }
|
|
209
|
+
});
|
|
210
|
+
return handleResponse(response);
|
|
211
|
+
})();
|
|
212
|
+
try {
|
|
213
|
+
return await authConfigInflight;
|
|
214
|
+
} finally {
|
|
215
|
+
authConfigInflight = null;
|
|
216
|
+
}
|
|
205
217
|
}
|
|
206
218
|
const STORAGE_KEY = "rebase_auth";
|
|
207
219
|
const TOKEN_REFRESH_BUFFER_MS = 2 * 60 * 1e3;
|
|
@@ -412,6 +424,7 @@ function useRebaseAuthController(props = {}) {
|
|
|
412
424
|
}
|
|
413
425
|
}, [client, getAuthToken, refreshAccessToken$1, clearSessionAndSignOut]);
|
|
414
426
|
const handleAuthSuccess = useCallback(async (userInfo, tokens) => {
|
|
427
|
+
console.log("[Auth] handleAuthSuccess called, user:", userInfo.email, "uid:", userInfo.uid);
|
|
415
428
|
tokensRef.current = tokens;
|
|
416
429
|
let convertedUser = convertToUser(userInfo);
|
|
417
430
|
if (defineRolesFor) {
|
|
@@ -424,11 +437,13 @@ function useRebaseAuthController(props = {}) {
|
|
|
424
437
|
}
|
|
425
438
|
}
|
|
426
439
|
saveAuthToStorage(tokens, userInfo);
|
|
440
|
+
console.log("[Auth] Calling setUser, roles:", convertedUser.roles);
|
|
427
441
|
setUser(convertedUser);
|
|
428
442
|
setAuthError(null);
|
|
429
443
|
setAuthProviderError(null);
|
|
430
444
|
setLoginSkipped(false);
|
|
431
445
|
scheduleTokenRefresh(tokens);
|
|
446
|
+
console.log("[Auth] handleAuthSuccess completed");
|
|
432
447
|
}, [scheduleTokenRefresh, defineRolesFor]);
|
|
433
448
|
const emailPasswordLogin = useCallback(async (email, password) => {
|
|
434
449
|
setAuthLoading(true);
|
|
@@ -456,11 +471,11 @@ function useRebaseAuthController(props = {}) {
|
|
|
456
471
|
setAuthLoading(false);
|
|
457
472
|
}
|
|
458
473
|
}, [handleAuthSuccess]);
|
|
459
|
-
const googleLogin$1 = useCallback(async (
|
|
474
|
+
const googleLogin$1 = useCallback(async (tokenOrPayload, tokenType) => {
|
|
460
475
|
setAuthLoading(true);
|
|
461
476
|
setAuthProviderError(null);
|
|
462
477
|
try {
|
|
463
|
-
const response = await googleLogin(
|
|
478
|
+
const response = typeof tokenOrPayload === "string" ? await googleLogin(tokenOrPayload, tokenType ?? "idToken") : await googleLogin(tokenOrPayload);
|
|
464
479
|
await handleAuthSuccess(response.user, response.tokens);
|
|
465
480
|
} catch (error) {
|
|
466
481
|
setAuthProviderError(error);
|
|
@@ -795,6 +810,7 @@ function useBackendUserManagement(config) {
|
|
|
795
810
|
const [loading, setLoading] = useState(true);
|
|
796
811
|
const [usersError, setUsersError] = useState();
|
|
797
812
|
const [rolesError, setRolesError] = useState();
|
|
813
|
+
const lastLoadedUidRef = useRef(null);
|
|
798
814
|
const apiRequestRef = useRef(null);
|
|
799
815
|
const apiRequest = useCallback(async (endpoint, method = "GET", body, retryCount = 6, signal) => {
|
|
800
816
|
let lastError = null;
|
|
@@ -892,6 +908,10 @@ function useBackendUserManagement(config) {
|
|
|
892
908
|
setLoading(false);
|
|
893
909
|
return;
|
|
894
910
|
}
|
|
911
|
+
if (lastLoadedUidRef.current === currentUser.uid) {
|
|
912
|
+
setLoading(false);
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
895
915
|
const abortController = new AbortController();
|
|
896
916
|
const load = async () => {
|
|
897
917
|
setLoading(true);
|
|
@@ -924,6 +944,7 @@ function useBackendUserManagement(config) {
|
|
|
924
944
|
}
|
|
925
945
|
}
|
|
926
946
|
if (!abortController.signal.aborted) {
|
|
947
|
+
lastLoadedUidRef.current = currentUser.uid;
|
|
927
948
|
setLoading(false);
|
|
928
949
|
}
|
|
929
950
|
};
|
|
@@ -1210,17 +1231,22 @@ function RebaseLoginView({
|
|
|
1210
1231
|
authController
|
|
1211
1232
|
}
|
|
1212
1233
|
),
|
|
1213
|
-
showRegistration && /* @__PURE__ */ jsx(
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1234
|
+
showRegistration && /* @__PURE__ */ jsx("div", { className: "mt-2 text-center", children: /* @__PURE__ */ jsxs(Typography, { variant: "body2", color: "secondary", children: [
|
|
1235
|
+
"Don't have an account?",
|
|
1236
|
+
" ",
|
|
1237
|
+
/* @__PURE__ */ jsx(
|
|
1238
|
+
"button",
|
|
1239
|
+
{
|
|
1240
|
+
type: "button",
|
|
1241
|
+
className: cls(
|
|
1242
|
+
"font-semibold hover:underline cursor-pointer",
|
|
1243
|
+
"text-primary-600 dark:text-primary-400"
|
|
1244
|
+
),
|
|
1245
|
+
onClick: () => switchMode("register"),
|
|
1246
|
+
children: "Create one"
|
|
1247
|
+
}
|
|
1248
|
+
)
|
|
1249
|
+
] }) })
|
|
1224
1250
|
] }),
|
|
1225
1251
|
mode === "login" && /* @__PURE__ */ jsx(
|
|
1226
1252
|
LoginForm,
|
|
@@ -1316,20 +1342,24 @@ function GoogleLoginButton({
|
|
|
1316
1342
|
googleClientId,
|
|
1317
1343
|
authController
|
|
1318
1344
|
}) {
|
|
1319
|
-
const
|
|
1345
|
+
const codeClientRef = useRef(null);
|
|
1320
1346
|
useEffect(() => {
|
|
1321
1347
|
const google = window.google;
|
|
1322
|
-
if (!google ||
|
|
1323
|
-
|
|
1348
|
+
if (!google || codeClientRef.current) return;
|
|
1349
|
+
codeClientRef.current = google.accounts.oauth2.initCodeClient({
|
|
1324
1350
|
client_id: googleClientId,
|
|
1325
1351
|
scope: "openid email profile",
|
|
1352
|
+
ux_mode: "popup",
|
|
1326
1353
|
callback: async (response) => {
|
|
1327
|
-
if (response.error || !response.
|
|
1354
|
+
if (response.error || !response.code) {
|
|
1328
1355
|
console.error("Google login error:", response.error);
|
|
1329
1356
|
return;
|
|
1330
1357
|
}
|
|
1331
1358
|
try {
|
|
1332
|
-
await authController.googleLogin(
|
|
1359
|
+
await authController.googleLogin({
|
|
1360
|
+
code: response.code,
|
|
1361
|
+
redirectUri: "postmessage"
|
|
1362
|
+
});
|
|
1333
1363
|
} catch (err) {
|
|
1334
1364
|
console.error("Google login error:", err);
|
|
1335
1365
|
}
|
|
@@ -1337,11 +1367,11 @@ function GoogleLoginButton({
|
|
|
1337
1367
|
});
|
|
1338
1368
|
}, [googleClientId, authController]);
|
|
1339
1369
|
const handleClick = () => {
|
|
1340
|
-
if (!
|
|
1370
|
+
if (!codeClientRef.current) {
|
|
1341
1371
|
console.error("Google Sign-In not loaded");
|
|
1342
1372
|
return;
|
|
1343
1373
|
}
|
|
1344
|
-
|
|
1374
|
+
codeClientRef.current.requestCode();
|
|
1345
1375
|
};
|
|
1346
1376
|
return /* @__PURE__ */ jsx(
|
|
1347
1377
|
LoginButton,
|
|
@@ -1620,14 +1650,12 @@ function createUserManagementAdminViews({ userManagement, apiUrl, getAuthToken,
|
|
|
1620
1650
|
{
|
|
1621
1651
|
slug: "dev/users",
|
|
1622
1652
|
name: "CMS Users",
|
|
1623
|
-
group: "Admin",
|
|
1624
1653
|
icon: "face",
|
|
1625
1654
|
view: /* @__PURE__ */ jsx(UsersView, { userManagement, apiUrl, getAuthToken })
|
|
1626
1655
|
},
|
|
1627
1656
|
{
|
|
1628
1657
|
slug: "dev/roles",
|
|
1629
1658
|
name: "Roles",
|
|
1630
|
-
group: "Admin",
|
|
1631
1659
|
icon: "gpp_good",
|
|
1632
1660
|
view: /* @__PURE__ */ jsx(RolesView, { userManagement, collections })
|
|
1633
1661
|
}
|
|
@@ -1725,7 +1753,7 @@ function UsersView({ userManagement, apiUrl, getAuthToken }) {
|
|
|
1725
1753
|
return /* @__PURE__ */ jsx(CenteredView, { children: /* @__PURE__ */ jsx(CircularProgress, {}) });
|
|
1726
1754
|
}
|
|
1727
1755
|
return /* @__PURE__ */ jsxs(Container, { className: "w-full flex flex-col py-4 gap-4", maxWidth: "6xl", children: [
|
|
1728
|
-
!hasAdmin &&
|
|
1756
|
+
!hasAdmin && loggedInUser && /* @__PURE__ */ 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: [
|
|
1729
1757
|
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Typography, { variant: "label", className: "text-yellow-800 dark:text-yellow-200", children: "No admin users exist. You can make yourself an admin." }) }),
|
|
1730
1758
|
/* @__PURE__ */ jsx(
|
|
1731
1759
|
Button,
|