@rebasepro/auth 0.2.1 → 0.2.4
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 +5 -1
- package/dist/hooks/useBackendUserManagement.d.ts +5 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +96 -74
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +96 -74
- package/dist/index.umd.js.map +1 -1
- package/dist/types.d.ts +10 -1
- package/package.json +35 -4
- package/src/api.ts +5 -1
- package/src/hooks/useBackendUserManagement.ts +121 -86
- package/src/hooks/useRebaseAuthController.ts +14 -10
- package/src/index.ts +1 -1
- package/src/types.ts +8 -1
package/dist/index.umd.js
CHANGED
|
@@ -217,11 +217,7 @@
|
|
|
217
217
|
authConfigInflight = null;
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
|
-
|
|
221
|
-
authConfigCached = null;
|
|
222
|
-
authConfigInflight = null;
|
|
223
|
-
}
|
|
224
|
-
const STORAGE_KEY = "rebase_auth";
|
|
220
|
+
const STORAGE_KEY = "rebase_react_auth";
|
|
225
221
|
const TOKEN_REFRESH_BUFFER_MS = 2 * 60 * 1e3;
|
|
226
222
|
function convertToUser(userInfo) {
|
|
227
223
|
return {
|
|
@@ -231,7 +227,8 @@
|
|
|
231
227
|
photoURL: userInfo.photoURL || null,
|
|
232
228
|
providerId: "custom",
|
|
233
229
|
isAnonymous: false,
|
|
234
|
-
roles: userInfo.roles || []
|
|
230
|
+
roles: userInfo.roles || [],
|
|
231
|
+
metadata: userInfo.metadata
|
|
235
232
|
};
|
|
236
233
|
}
|
|
237
234
|
function saveAuthToStorage(tokens, user) {
|
|
@@ -278,12 +275,11 @@
|
|
|
278
275
|
const refreshPromiseRef = react.useRef(null);
|
|
279
276
|
const isMountedRef = react.useRef(true);
|
|
280
277
|
react.useEffect(() => {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
setApiUrl(apiUrl);
|
|
278
|
+
const url = client?.baseUrl || apiUrl;
|
|
279
|
+
if (url) {
|
|
280
|
+
setApiUrl(url);
|
|
285
281
|
}
|
|
286
|
-
}, [client, apiUrl]);
|
|
282
|
+
}, [client, client?.baseUrl, apiUrl]);
|
|
287
283
|
const clearError = react.useCallback(() => {
|
|
288
284
|
setAuthProviderError(null);
|
|
289
285
|
}, []);
|
|
@@ -399,7 +395,7 @@
|
|
|
399
395
|
}, [initialLoading, refreshAccessToken$1, clearSessionAndSignOut]);
|
|
400
396
|
react.useEffect(() => {
|
|
401
397
|
if (client) {
|
|
402
|
-
client.setAuthTokenGetter(async () => {
|
|
398
|
+
client.setAuthTokenGetter?.(async () => {
|
|
403
399
|
try {
|
|
404
400
|
return await getAuthToken();
|
|
405
401
|
} catch {
|
|
@@ -675,6 +671,10 @@
|
|
|
675
671
|
}
|
|
676
672
|
} catch (meError) {
|
|
677
673
|
if (!isMountedRef.current) return;
|
|
674
|
+
if (meError instanceof AuthApiError && (meError.code === "NOT_FOUND" || meError.code === "UNAUTHORIZED")) {
|
|
675
|
+
clearSessionAndSignOut();
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
678
|
userToSet = convertToUser(stored.user);
|
|
679
679
|
}
|
|
680
680
|
if (!isMountedRef.current) return;
|
|
@@ -774,10 +774,10 @@
|
|
|
774
774
|
emailPasswordLogin: true,
|
|
775
775
|
googleLogin: !!props.googleClientId,
|
|
776
776
|
registration: authConfig?.registrationEnabled ?? false,
|
|
777
|
-
passwordReset:
|
|
777
|
+
passwordReset: authConfig?.passwordReset ?? false,
|
|
778
778
|
sessionManagement: true,
|
|
779
779
|
profileUpdate: true,
|
|
780
|
-
emailVerification: false,
|
|
780
|
+
emailVerification: authConfig?.emailVerification ?? false,
|
|
781
781
|
enabledProviders: authConfig?.enabledProviders ?? []
|
|
782
782
|
}
|
|
783
783
|
};
|
|
@@ -798,18 +798,34 @@
|
|
|
798
798
|
return {
|
|
799
799
|
id: apiRole.id,
|
|
800
800
|
name: apiRole.name,
|
|
801
|
-
isAdmin: apiRole.isAdmin ?? false
|
|
802
|
-
config: apiRole.config ?? void 0
|
|
801
|
+
isAdmin: apiRole.isAdmin ?? false
|
|
803
802
|
};
|
|
804
803
|
}
|
|
805
804
|
function useBackendUserManagement(config) {
|
|
806
805
|
const { client, apiUrl, getAuthToken, currentUser } = config;
|
|
807
|
-
const [
|
|
806
|
+
const [userCache, setUserCache] = react.useState(/* @__PURE__ */ new Map());
|
|
807
|
+
const [hasAdminUsers, setHasAdminUsers] = react.useState(false);
|
|
808
808
|
const [roles, setRoles] = react.useState([]);
|
|
809
|
-
const
|
|
809
|
+
const userRoles = currentUser?.roles ?? [];
|
|
810
|
+
const isUserAdmin = userRoles.some((r) => r === "admin" || r === "schema-admin");
|
|
811
|
+
const [loading, setLoading] = react.useState(() => {
|
|
812
|
+
if (!currentUser) return false;
|
|
813
|
+
if (!isUserAdmin) return false;
|
|
814
|
+
return true;
|
|
815
|
+
});
|
|
810
816
|
const [usersError, setUsersError] = react.useState();
|
|
811
817
|
const [rolesError, setRolesError] = react.useState();
|
|
812
818
|
const lastLoadedUidRef = react.useRef(null);
|
|
819
|
+
const effectiveLoading = loading || !!(currentUser && isUserAdmin && lastLoadedUidRef.current !== currentUser.uid);
|
|
820
|
+
const mergeIntoCache = react.useCallback((incoming) => {
|
|
821
|
+
setUserCache((prev) => {
|
|
822
|
+
const next = new Map(prev);
|
|
823
|
+
for (const u of incoming) {
|
|
824
|
+
next.set(u.uid, u);
|
|
825
|
+
}
|
|
826
|
+
return next;
|
|
827
|
+
});
|
|
828
|
+
}, []);
|
|
813
829
|
const apiRequestRef = react.useRef(null);
|
|
814
830
|
const apiRequest = react.useCallback(async (endpoint, method = "GET", body, retryCount = 6, signal) => {
|
|
815
831
|
let lastError = null;
|
|
@@ -820,7 +836,7 @@
|
|
|
820
836
|
throw error;
|
|
821
837
|
}
|
|
822
838
|
try {
|
|
823
|
-
const token = getAuthToken ? await getAuthToken() : client ? await client.resolveToken() : null;
|
|
839
|
+
const token = getAuthToken ? await getAuthToken() : client?.resolveToken ? await client.resolveToken() : null;
|
|
824
840
|
const baseUrl = apiUrl || (client?.baseUrl ? client.baseUrl : "");
|
|
825
841
|
const response = await fetch(`${baseUrl}/api/admin${endpoint}`, {
|
|
826
842
|
method,
|
|
@@ -890,26 +906,29 @@
|
|
|
890
906
|
setRolesError(error instanceof Error ? error : new Error(String(error)));
|
|
891
907
|
}
|
|
892
908
|
}, [apiRequest]);
|
|
893
|
-
const
|
|
909
|
+
const checkAdminExists = react.useCallback(async (signal) => {
|
|
894
910
|
try {
|
|
895
|
-
const data = await apiRequest("/users", "GET", void 0, 6, signal);
|
|
896
|
-
const
|
|
897
|
-
|
|
911
|
+
const data = await apiRequest("/users?role=admin&limit=1", "GET", void 0, 6, signal);
|
|
912
|
+
const adminUsers = data.users.map((u) => convertUser(u));
|
|
913
|
+
setHasAdminUsers(adminUsers.length > 0);
|
|
914
|
+
if (adminUsers.length > 0) {
|
|
915
|
+
mergeIntoCache(adminUsers);
|
|
916
|
+
}
|
|
898
917
|
setUsersError(void 0);
|
|
899
918
|
} catch (error) {
|
|
900
919
|
if (error instanceof Error && error.name === "AbortError") return;
|
|
901
|
-
console.error("Failed to
|
|
920
|
+
console.error("Failed to check admin users:", error);
|
|
902
921
|
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
903
922
|
}
|
|
904
|
-
}, [apiRequest]);
|
|
923
|
+
}, [apiRequest, mergeIntoCache]);
|
|
905
924
|
react.useEffect(() => {
|
|
906
925
|
if (!currentUser) {
|
|
907
926
|
setLoading(false);
|
|
908
927
|
return;
|
|
909
928
|
}
|
|
910
|
-
const
|
|
911
|
-
const
|
|
912
|
-
if (!
|
|
929
|
+
const userRoles2 = currentUser.roles ?? [];
|
|
930
|
+
const isUserAdmin2 = userRoles2.some((r) => r === "admin" || r === "schema-admin");
|
|
931
|
+
if (!isUserAdmin2) {
|
|
913
932
|
setLoading(false);
|
|
914
933
|
return;
|
|
915
934
|
}
|
|
@@ -938,13 +957,16 @@
|
|
|
938
957
|
}
|
|
939
958
|
if (!abortController.signal.aborted) {
|
|
940
959
|
try {
|
|
941
|
-
const data = await request("/users", "GET", void 0, 6, abortController.signal);
|
|
942
|
-
const
|
|
943
|
-
|
|
960
|
+
const data = await request("/users?role=admin&limit=1", "GET", void 0, 6, abortController.signal);
|
|
961
|
+
const adminUsers = data.users.map((u) => convertUser(u));
|
|
962
|
+
setHasAdminUsers(adminUsers.length > 0);
|
|
963
|
+
if (adminUsers.length > 0) {
|
|
964
|
+
mergeIntoCache(adminUsers);
|
|
965
|
+
}
|
|
944
966
|
setUsersError(void 0);
|
|
945
967
|
} catch (error) {
|
|
946
968
|
if (error instanceof Error && error.name === "AbortError") return;
|
|
947
|
-
console.error("Failed to
|
|
969
|
+
console.error("Failed to check admin users:", error);
|
|
948
970
|
setUsersError(error instanceof Error ? error : new Error(String(error)));
|
|
949
971
|
}
|
|
950
972
|
}
|
|
@@ -968,34 +990,24 @@
|
|
|
968
990
|
if (options.roleId) params.set("role", options.roleId);
|
|
969
991
|
const qs = params.toString();
|
|
970
992
|
const data = await apiRequest("/users" + (qs ? "?" + qs : ""), "GET");
|
|
993
|
+
const converted = data.users.map((u) => convertUser(u));
|
|
994
|
+
mergeIntoCache(converted);
|
|
971
995
|
return {
|
|
972
|
-
users:
|
|
996
|
+
users: converted,
|
|
973
997
|
total: data.total
|
|
974
998
|
};
|
|
975
|
-
}, [apiRequest]);
|
|
999
|
+
}, [apiRequest, mergeIntoCache]);
|
|
976
1000
|
const saveUser = react.useCallback(async (user) => {
|
|
977
1001
|
const roleIds = user.roles ?? [];
|
|
978
|
-
const
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
return updated;
|
|
988
|
-
} else {
|
|
989
|
-
const data = await apiRequest("/users", "POST", {
|
|
990
|
-
email: user.email,
|
|
991
|
-
displayName: user.displayName,
|
|
992
|
-
roles: roleIds
|
|
993
|
-
});
|
|
994
|
-
const created = convertUser(data.user);
|
|
995
|
-
setUsers((prev) => [...prev, created]);
|
|
996
|
-
return created;
|
|
997
|
-
}
|
|
998
|
-
}, [apiRequest, users, roles]);
|
|
1002
|
+
const data = await apiRequest(`/users/${user.uid}`, "PUT", {
|
|
1003
|
+
email: user.email,
|
|
1004
|
+
displayName: user.displayName,
|
|
1005
|
+
roles: roleIds
|
|
1006
|
+
});
|
|
1007
|
+
const updated = convertUser(data.user);
|
|
1008
|
+
mergeIntoCache([updated]);
|
|
1009
|
+
return updated;
|
|
1010
|
+
}, [apiRequest, mergeIntoCache]);
|
|
999
1011
|
const createUser = react.useCallback(async (user) => {
|
|
1000
1012
|
const roleIds = user.roles ?? [];
|
|
1001
1013
|
const data = await apiRequest("/users", "POST", {
|
|
@@ -1004,34 +1016,37 @@
|
|
|
1004
1016
|
roles: roleIds
|
|
1005
1017
|
});
|
|
1006
1018
|
const created = convertUser(data.user);
|
|
1007
|
-
|
|
1019
|
+
mergeIntoCache([created]);
|
|
1008
1020
|
return {
|
|
1009
1021
|
user: created,
|
|
1010
1022
|
invitationSent: data.invitationSent ?? false,
|
|
1011
1023
|
temporaryPassword: data.temporaryPassword
|
|
1012
1024
|
};
|
|
1013
|
-
}, [apiRequest,
|
|
1025
|
+
}, [apiRequest, mergeIntoCache]);
|
|
1014
1026
|
const resetPassword2 = react.useCallback(async (user) => {
|
|
1015
1027
|
const data = await apiRequest(`/users/${user.uid}/reset-password`, "POST");
|
|
1016
1028
|
const updatedUser = convertUser(data.user);
|
|
1017
|
-
|
|
1029
|
+
mergeIntoCache([updatedUser]);
|
|
1018
1030
|
return {
|
|
1019
1031
|
user: updatedUser,
|
|
1020
1032
|
invitationSent: data.invitationSent ?? false,
|
|
1021
1033
|
temporaryPassword: data.temporaryPassword
|
|
1022
1034
|
};
|
|
1023
|
-
}, [apiRequest]);
|
|
1035
|
+
}, [apiRequest, mergeIntoCache]);
|
|
1024
1036
|
const deleteUser = react.useCallback(async (user) => {
|
|
1025
1037
|
await apiRequest(`/users/${user.uid}`, "DELETE");
|
|
1026
|
-
|
|
1038
|
+
setUserCache((prev) => {
|
|
1039
|
+
const next = new Map(prev);
|
|
1040
|
+
next.delete(user.uid);
|
|
1041
|
+
return next;
|
|
1042
|
+
});
|
|
1027
1043
|
}, [apiRequest]);
|
|
1028
1044
|
const saveRole = react.useCallback(async (role) => {
|
|
1029
1045
|
const existingRole = roles.find((r) => r.id === role.id);
|
|
1030
1046
|
if (existingRole) {
|
|
1031
1047
|
const data = await apiRequest(`/roles/${role.id}`, "PUT", {
|
|
1032
1048
|
name: role.name,
|
|
1033
|
-
isAdmin: role.isAdmin
|
|
1034
|
-
config: role.config
|
|
1049
|
+
isAdmin: role.isAdmin
|
|
1035
1050
|
});
|
|
1036
1051
|
const updated = convertRole(data.role);
|
|
1037
1052
|
setRoles((prev) => prev.map((r) => r.id === updated.id ? updated : r));
|
|
@@ -1039,8 +1054,7 @@
|
|
|
1039
1054
|
const data = await apiRequest("/roles", "POST", {
|
|
1040
1055
|
id: role.id,
|
|
1041
1056
|
name: role.name,
|
|
1042
|
-
isAdmin: role.isAdmin ?? false
|
|
1043
|
-
config: role.config
|
|
1057
|
+
isAdmin: role.isAdmin ?? false
|
|
1044
1058
|
});
|
|
1045
1059
|
const created = convertRole(data.role);
|
|
1046
1060
|
setRoles((prev) => [...prev, created]);
|
|
@@ -1051,14 +1065,22 @@
|
|
|
1051
1065
|
setRoles((prev) => prev.filter((r) => r.id !== role.id));
|
|
1052
1066
|
}, [apiRequest]);
|
|
1053
1067
|
const getUser = react.useCallback((uid) => {
|
|
1054
|
-
return
|
|
1055
|
-
}, [
|
|
1068
|
+
return userCache.get(uid) ?? null;
|
|
1069
|
+
}, [userCache]);
|
|
1056
1070
|
const defineRolesFor = react.useCallback(async (user) => {
|
|
1057
|
-
|
|
1058
|
-
if (!existingUser)
|
|
1071
|
+
let existingUser = userCache.get(user.uid) ?? Array.from(userCache.values()).find((u) => u.email === user.email);
|
|
1072
|
+
if (!existingUser) {
|
|
1073
|
+
try {
|
|
1074
|
+
const data = await apiRequest(`/users/${user.uid}`, "GET");
|
|
1075
|
+
existingUser = convertUser(data.user);
|
|
1076
|
+
mergeIntoCache([existingUser]);
|
|
1077
|
+
} catch {
|
|
1078
|
+
return void 0;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1059
1081
|
const userRoleIds = existingUser.roles ?? [];
|
|
1060
1082
|
return roles.filter((r) => userRoleIds.includes(r.id));
|
|
1061
|
-
}, [
|
|
1083
|
+
}, [userCache, roles, apiRequest, mergeIntoCache]);
|
|
1062
1084
|
const isAdmin = currentUser?.roles?.includes("admin") ?? false;
|
|
1063
1085
|
const bootstrapAdmin = react.useCallback(async () => {
|
|
1064
1086
|
try {
|
|
@@ -1066,15 +1088,17 @@
|
|
|
1066
1088
|
const data = await apiRequest("/roles");
|
|
1067
1089
|
const loadedRoles = data.roles.map(convertRole);
|
|
1068
1090
|
setRoles(loadedRoles);
|
|
1069
|
-
await
|
|
1091
|
+
await checkAdminExists();
|
|
1070
1092
|
} catch (error) {
|
|
1071
1093
|
console.error("Failed to bootstrap admin:", error);
|
|
1072
1094
|
throw error;
|
|
1073
1095
|
}
|
|
1074
|
-
}, [apiRequest,
|
|
1096
|
+
}, [apiRequest, checkAdminExists]);
|
|
1097
|
+
const users = Array.from(userCache.values());
|
|
1075
1098
|
return {
|
|
1076
|
-
loading,
|
|
1099
|
+
loading: effectiveLoading,
|
|
1077
1100
|
users,
|
|
1101
|
+
hasAdminUsers,
|
|
1078
1102
|
saveUser,
|
|
1079
1103
|
createUser,
|
|
1080
1104
|
resetPassword: resetPassword2,
|
|
@@ -1084,7 +1108,6 @@
|
|
|
1084
1108
|
deleteRole,
|
|
1085
1109
|
isAdmin,
|
|
1086
1110
|
allowDefaultRolesCreation: isAdmin,
|
|
1087
|
-
includeCollectionConfigPermissions: true,
|
|
1088
1111
|
defineRolesFor,
|
|
1089
1112
|
getUser,
|
|
1090
1113
|
searchUsers,
|
|
@@ -1094,7 +1117,6 @@
|
|
|
1094
1117
|
};
|
|
1095
1118
|
}
|
|
1096
1119
|
exports2.AuthApiError = AuthApiError;
|
|
1097
|
-
exports2.clearAuthConfigCache = clearAuthConfigCache;
|
|
1098
1120
|
exports2.fetchAuthConfig = fetchAuthConfig;
|
|
1099
1121
|
exports2.getApiUrl = getApiUrl;
|
|
1100
1122
|
exports2.setApiUrl = setApiUrl;
|