@rebasepro/auth 0.0.1-canary.c53f5db → 0.0.1-canary.cbdd980
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 +117 -76
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +117 -76
- 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 +68 -39
- package/src/hooks/useBackendUserManagement.ts +53 -8
- package/src/hooks/useRebaseAuthController.ts +11 -3
- package/src/types.ts +5 -2
package/dist/api.d.ts
CHANGED
|
@@ -20,9 +20,24 @@ 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
|
|
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>;
|
|
40
|
+
export declare function googleLogin(token: string, tokenType?: "idToken" | "accessToken"): Promise<AuthResponse>;
|
|
26
41
|
/**
|
|
27
42
|
* Login with LinkedIn OAuth code
|
|
28
43
|
*/
|
|
@@ -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
|
@@ -72,11 +72,12 @@ async function login(email, password) {
|
|
|
72
72
|
});
|
|
73
73
|
return handleResponse(response);
|
|
74
74
|
}
|
|
75
|
-
async function googleLogin(idToken) {
|
|
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(idToken);
|
|
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,8 @@ 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);
|
|
814
|
+
const apiRequestRef = useRef(null);
|
|
798
815
|
const apiRequest = useCallback(async (endpoint, method = "GET", body, retryCount = 6, signal) => {
|
|
799
816
|
let lastError = null;
|
|
800
817
|
for (let attempt = 0; attempt < retryCount; attempt++) {
|
|
@@ -862,6 +879,7 @@ function useBackendUserManagement(config) {
|
|
|
862
879
|
}
|
|
863
880
|
throw lastError;
|
|
864
881
|
}, [apiUrl, getAuthToken]);
|
|
882
|
+
apiRequestRef.current = apiRequest;
|
|
865
883
|
useCallback(async (signal) => {
|
|
866
884
|
try {
|
|
867
885
|
const data = await apiRequest("/roles", "GET", void 0, 6, signal);
|
|
@@ -890,23 +908,43 @@ function useBackendUserManagement(config) {
|
|
|
890
908
|
setLoading(false);
|
|
891
909
|
return;
|
|
892
910
|
}
|
|
911
|
+
if (lastLoadedUidRef.current === currentUser.uid) {
|
|
912
|
+
setLoading(false);
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
893
915
|
const abortController = new AbortController();
|
|
894
916
|
const load = async () => {
|
|
895
917
|
setLoading(true);
|
|
918
|
+
const request = apiRequestRef.current;
|
|
896
919
|
try {
|
|
897
|
-
const data = await
|
|
920
|
+
const data = await request("/roles", "GET", void 0, 6, abortController.signal);
|
|
898
921
|
setRoles(data.roles.map(convertRole));
|
|
899
922
|
setRolesError(void 0);
|
|
900
923
|
} catch (error) {
|
|
901
|
-
if (error instanceof Error && error.name
|
|
902
|
-
|
|
903
|
-
|
|
924
|
+
if (error instanceof Error && error.name === "AbortError") return;
|
|
925
|
+
console.error("Failed to load roles:", error);
|
|
926
|
+
setRolesError(error instanceof Error ? error : new Error(String(error)));
|
|
927
|
+
const status = error.status;
|
|
928
|
+
if (status === 403 || status === 401) {
|
|
929
|
+
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
930
|
+
setLoading(false);
|
|
931
|
+
return;
|
|
904
932
|
}
|
|
905
933
|
}
|
|
906
934
|
if (!abortController.signal.aborted) {
|
|
907
|
-
|
|
935
|
+
try {
|
|
936
|
+
const data = await request("/users", "GET", void 0, 6, abortController.signal);
|
|
937
|
+
const allUsers = data.users.map((u) => convertUser(u));
|
|
938
|
+
setUsers(allUsers);
|
|
939
|
+
setUsersError(void 0);
|
|
940
|
+
} catch (error) {
|
|
941
|
+
if (error instanceof Error && error.name === "AbortError") return;
|
|
942
|
+
console.error("Failed to load users:", error);
|
|
943
|
+
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
944
|
+
}
|
|
908
945
|
}
|
|
909
946
|
if (!abortController.signal.aborted) {
|
|
947
|
+
lastLoadedUidRef.current = currentUser.uid;
|
|
910
948
|
setLoading(false);
|
|
911
949
|
}
|
|
912
950
|
};
|
|
@@ -914,7 +952,7 @@ function useBackendUserManagement(config) {
|
|
|
914
952
|
return () => {
|
|
915
953
|
abortController.abort();
|
|
916
954
|
};
|
|
917
|
-
}, [currentUser
|
|
955
|
+
}, [currentUser?.uid]);
|
|
918
956
|
const searchUsers = useCallback(async (options) => {
|
|
919
957
|
const params = new URLSearchParams();
|
|
920
958
|
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
@@ -1115,7 +1153,7 @@ function RebaseLoginView({
|
|
|
1115
1153
|
"div",
|
|
1116
1154
|
{
|
|
1117
1155
|
className: cls(
|
|
1118
|
-
"relative flex items-center justify-center h-screen w-screen p-4 transition-opacity duration-500 bg-white dark:bg-surface-
|
|
1156
|
+
"relative flex items-center justify-center h-screen w-screen p-4 transition-opacity duration-500 bg-white dark:bg-surface-900",
|
|
1119
1157
|
fadeIn ? "opacity-100" : "opacity-0"
|
|
1120
1158
|
),
|
|
1121
1159
|
children: [
|
|
@@ -1269,74 +1307,79 @@ function LoginButton({
|
|
|
1269
1307
|
}
|
|
1270
1308
|
);
|
|
1271
1309
|
}
|
|
1310
|
+
const GoogleIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "20", height: "20", children: [
|
|
1311
|
+
/* @__PURE__ */ jsx(
|
|
1312
|
+
"path",
|
|
1313
|
+
{
|
|
1314
|
+
fill: "#4285F4",
|
|
1315
|
+
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"
|
|
1316
|
+
}
|
|
1317
|
+
),
|
|
1318
|
+
/* @__PURE__ */ jsx(
|
|
1319
|
+
"path",
|
|
1320
|
+
{
|
|
1321
|
+
fill: "#34A853",
|
|
1322
|
+
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"
|
|
1323
|
+
}
|
|
1324
|
+
),
|
|
1325
|
+
/* @__PURE__ */ jsx(
|
|
1326
|
+
"path",
|
|
1327
|
+
{
|
|
1328
|
+
fill: "#FBBC05",
|
|
1329
|
+
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"
|
|
1330
|
+
}
|
|
1331
|
+
),
|
|
1332
|
+
/* @__PURE__ */ jsx(
|
|
1333
|
+
"path",
|
|
1334
|
+
{
|
|
1335
|
+
fill: "#EA4335",
|
|
1336
|
+
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"
|
|
1337
|
+
}
|
|
1338
|
+
)
|
|
1339
|
+
] });
|
|
1272
1340
|
function GoogleLoginButton({
|
|
1273
1341
|
disabled,
|
|
1274
1342
|
googleClientId,
|
|
1275
1343
|
authController
|
|
1276
1344
|
}) {
|
|
1277
|
-
const
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
} catch (err) {
|
|
1290
|
-
console.error("Google login error:", err);
|
|
1291
|
-
}
|
|
1345
|
+
const codeClientRef = useRef(null);
|
|
1346
|
+
useEffect(() => {
|
|
1347
|
+
const google = window.google;
|
|
1348
|
+
if (!google || codeClientRef.current) return;
|
|
1349
|
+
codeClientRef.current = google.accounts.oauth2.initCodeClient({
|
|
1350
|
+
client_id: googleClientId,
|
|
1351
|
+
scope: "openid email profile",
|
|
1352
|
+
ux_mode: "popup",
|
|
1353
|
+
callback: async (response) => {
|
|
1354
|
+
if (response.error || !response.code) {
|
|
1355
|
+
console.error("Google login error:", response.error);
|
|
1356
|
+
return;
|
|
1292
1357
|
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1358
|
+
try {
|
|
1359
|
+
await authController.googleLogin({
|
|
1360
|
+
code: response.code,
|
|
1361
|
+
redirectUri: "postmessage"
|
|
1362
|
+
});
|
|
1363
|
+
} catch (err) {
|
|
1364
|
+
console.error("Google login error:", err);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
});
|
|
1368
|
+
}, [googleClientId, authController]);
|
|
1369
|
+
const handleClick = () => {
|
|
1370
|
+
if (!codeClientRef.current) {
|
|
1371
|
+
console.error("Google Sign-In not loaded");
|
|
1372
|
+
return;
|
|
1297
1373
|
}
|
|
1374
|
+
codeClientRef.current.requestCode();
|
|
1298
1375
|
};
|
|
1299
1376
|
return /* @__PURE__ */ jsx(
|
|
1300
|
-
|
|
1377
|
+
LoginButton,
|
|
1301
1378
|
{
|
|
1302
1379
|
disabled,
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
onClick: handleGoogleLogin,
|
|
1307
|
-
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center w-full gap-3 py-1", children: [
|
|
1308
|
-
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "20", height: "20", children: [
|
|
1309
|
-
/* @__PURE__ */ jsx(
|
|
1310
|
-
"path",
|
|
1311
|
-
{
|
|
1312
|
-
fill: "#4285F4",
|
|
1313
|
-
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"
|
|
1314
|
-
}
|
|
1315
|
-
),
|
|
1316
|
-
/* @__PURE__ */ jsx(
|
|
1317
|
-
"path",
|
|
1318
|
-
{
|
|
1319
|
-
fill: "#34A853",
|
|
1320
|
-
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"
|
|
1321
|
-
}
|
|
1322
|
-
),
|
|
1323
|
-
/* @__PURE__ */ jsx(
|
|
1324
|
-
"path",
|
|
1325
|
-
{
|
|
1326
|
-
fill: "#FBBC05",
|
|
1327
|
-
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"
|
|
1328
|
-
}
|
|
1329
|
-
),
|
|
1330
|
-
/* @__PURE__ */ jsx(
|
|
1331
|
-
"path",
|
|
1332
|
-
{
|
|
1333
|
-
fill: "#EA4335",
|
|
1334
|
-
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"
|
|
1335
|
-
}
|
|
1336
|
-
)
|
|
1337
|
-
] }),
|
|
1338
|
-
/* @__PURE__ */ jsx(Typography, { variant: "button", children: "Continue with Google" })
|
|
1339
|
-
] })
|
|
1380
|
+
text: "Sign in with Google",
|
|
1381
|
+
icon: /* @__PURE__ */ jsx(GoogleIcon, {}),
|
|
1382
|
+
onClick: handleClick
|
|
1340
1383
|
}
|
|
1341
1384
|
);
|
|
1342
1385
|
}
|
|
@@ -1607,14 +1650,12 @@ function createUserManagementAdminViews({ userManagement, apiUrl, getAuthToken,
|
|
|
1607
1650
|
{
|
|
1608
1651
|
slug: "dev/users",
|
|
1609
1652
|
name: "CMS Users",
|
|
1610
|
-
group: "Admin",
|
|
1611
1653
|
icon: "face",
|
|
1612
1654
|
view: /* @__PURE__ */ jsx(UsersView, { userManagement, apiUrl, getAuthToken })
|
|
1613
1655
|
},
|
|
1614
1656
|
{
|
|
1615
1657
|
slug: "dev/roles",
|
|
1616
1658
|
name: "Roles",
|
|
1617
|
-
group: "Admin",
|
|
1618
1659
|
icon: "gpp_good",
|
|
1619
1660
|
view: /* @__PURE__ */ jsx(RolesView, { userManagement, collections })
|
|
1620
1661
|
}
|
|
@@ -1712,7 +1753,7 @@ function UsersView({ userManagement, apiUrl, getAuthToken }) {
|
|
|
1712
1753
|
return /* @__PURE__ */ jsx(CenteredView, { children: /* @__PURE__ */ jsx(CircularProgress, {}) });
|
|
1713
1754
|
}
|
|
1714
1755
|
return /* @__PURE__ */ jsxs(Container, { className: "w-full flex flex-col py-4 gap-4", maxWidth: "6xl", children: [
|
|
1715
|
-
!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: [
|
|
1716
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." }) }),
|
|
1717
1758
|
/* @__PURE__ */ jsx(
|
|
1718
1759
|
Button,
|