@checkstack/auth-frontend 0.2.0 → 0.3.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/CHANGELOG.md +62 -0
- package/package.json +1 -1
- package/src/components/ApplicationsTab.tsx +88 -89
- package/src/components/AuthSettingsPage.tsx +60 -50
- package/src/components/LoginPage.tsx +7 -15
- package/src/components/RegisterPage.tsx +12 -20
- package/src/components/RoleDialog.tsx +12 -16
- package/src/components/RolesTab.tsx +52 -39
- package/src/components/StrategiesTab.tsx +88 -94
- package/src/components/TeamAccessEditor.tsx +125 -114
- package/src/components/TeamsTab.tsx +179 -161
- package/src/components/UsersTab.tsx +41 -30
- package/src/hooks/useAccessRules.ts +23 -34
- package/src/hooks/useEnabledStrategies.ts +10 -41
- package/src/lib/auth-client.ts +17 -22
|
@@ -16,8 +16,7 @@ import {
|
|
|
16
16
|
useToast,
|
|
17
17
|
} from "@checkstack/ui";
|
|
18
18
|
import { Plus, Edit, Trash2 } from "lucide-react";
|
|
19
|
-
import {
|
|
20
|
-
import { rpcApiRef } from "@checkstack/frontend-api";
|
|
19
|
+
import { usePluginClient } from "@checkstack/frontend-api";
|
|
21
20
|
import { AuthApi } from "@checkstack/auth-common";
|
|
22
21
|
import type { Role, AccessRuleEntry } from "../api";
|
|
23
22
|
import { RoleDialog } from "./RoleDialog";
|
|
@@ -43,14 +42,51 @@ export const RolesTab: React.FC<RolesTabProps> = ({
|
|
|
43
42
|
canDeleteRoles,
|
|
44
43
|
onDataChange,
|
|
45
44
|
}) => {
|
|
46
|
-
const
|
|
47
|
-
const authClient = rpcApi.forPlugin(AuthApi);
|
|
45
|
+
const authClient = usePluginClient(AuthApi);
|
|
48
46
|
const toast = useToast();
|
|
49
47
|
|
|
50
48
|
const [roleToDelete, setRoleToDelete] = useState<string>();
|
|
51
49
|
const [roleDialogOpen, setRoleDialogOpen] = useState(false);
|
|
52
50
|
const [editingRole, setEditingRole] = useState<Role | undefined>();
|
|
53
51
|
|
|
52
|
+
// Mutations
|
|
53
|
+
const createRoleMutation = authClient.createRole.useMutation({
|
|
54
|
+
onSuccess: () => {
|
|
55
|
+
toast.success("Role created successfully");
|
|
56
|
+
void onDataChange();
|
|
57
|
+
},
|
|
58
|
+
onError: (error) => {
|
|
59
|
+
toast.error(
|
|
60
|
+
error instanceof Error ? error.message : "Failed to create role"
|
|
61
|
+
);
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const updateRoleMutation = authClient.updateRole.useMutation({
|
|
66
|
+
onSuccess: () => {
|
|
67
|
+
toast.success("Role updated successfully");
|
|
68
|
+
void onDataChange();
|
|
69
|
+
},
|
|
70
|
+
onError: (error) => {
|
|
71
|
+
toast.error(
|
|
72
|
+
error instanceof Error ? error.message : "Failed to update role"
|
|
73
|
+
);
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const deleteRoleMutation = authClient.deleteRole.useMutation({
|
|
78
|
+
onSuccess: () => {
|
|
79
|
+
toast.success("Role deleted successfully");
|
|
80
|
+
setRoleToDelete(undefined);
|
|
81
|
+
void onDataChange();
|
|
82
|
+
},
|
|
83
|
+
onError: (error) => {
|
|
84
|
+
toast.error(
|
|
85
|
+
error instanceof Error ? error.message : "Failed to delete role"
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
54
90
|
const handleCreateRole = () => {
|
|
55
91
|
setEditingRole(undefined);
|
|
56
92
|
setRoleDialogOpen(true);
|
|
@@ -67,44 +103,21 @@ export const RolesTab: React.FC<RolesTabProps> = ({
|
|
|
67
103
|
description?: string;
|
|
68
104
|
accessRules: string[];
|
|
69
105
|
}) => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
await authClient.createRole({
|
|
81
|
-
name: params.name,
|
|
82
|
-
description: params.description,
|
|
83
|
-
accessRules: params.accessRules,
|
|
84
|
-
});
|
|
85
|
-
toast.success("Role created successfully");
|
|
86
|
-
}
|
|
87
|
-
await onDataChange();
|
|
88
|
-
} catch (error: unknown) {
|
|
89
|
-
toast.error(
|
|
90
|
-
error instanceof Error ? error.message : "Failed to save role"
|
|
91
|
-
);
|
|
92
|
-
throw error;
|
|
93
|
-
}
|
|
106
|
+
await (params.id ? updateRoleMutation.mutateAsync({
|
|
107
|
+
id: params.id,
|
|
108
|
+
name: params.name,
|
|
109
|
+
description: params.description,
|
|
110
|
+
accessRules: params.accessRules,
|
|
111
|
+
}) : createRoleMutation.mutateAsync({
|
|
112
|
+
name: params.name,
|
|
113
|
+
description: params.description,
|
|
114
|
+
accessRules: params.accessRules,
|
|
115
|
+
}));
|
|
94
116
|
};
|
|
95
117
|
|
|
96
|
-
const handleDeleteRole =
|
|
118
|
+
const handleDeleteRole = () => {
|
|
97
119
|
if (!roleToDelete) return;
|
|
98
|
-
|
|
99
|
-
await authClient.deleteRole(roleToDelete);
|
|
100
|
-
toast.success("Role deleted successfully");
|
|
101
|
-
setRoleToDelete(undefined);
|
|
102
|
-
await onDataChange();
|
|
103
|
-
} catch (error: unknown) {
|
|
104
|
-
toast.error(
|
|
105
|
-
error instanceof Error ? error.message : "Failed to delete role"
|
|
106
|
-
);
|
|
107
|
-
}
|
|
120
|
+
deleteRoleMutation.mutate(roleToDelete);
|
|
108
121
|
};
|
|
109
122
|
|
|
110
123
|
return (
|
|
@@ -15,8 +15,7 @@ import {
|
|
|
15
15
|
useToast,
|
|
16
16
|
} from "@checkstack/ui";
|
|
17
17
|
import { Shield, RefreshCw } from "lucide-react";
|
|
18
|
-
import {
|
|
19
|
-
import { rpcApiRef } from "@checkstack/frontend-api";
|
|
18
|
+
import { usePluginClient } from "@checkstack/frontend-api";
|
|
20
19
|
import { AuthApi } from "@checkstack/auth-common";
|
|
21
20
|
import type { AuthStrategy } from "../api";
|
|
22
21
|
import { AuthStrategyCard } from "./AuthStrategyCard";
|
|
@@ -34,8 +33,7 @@ export const StrategiesTab: React.FC<StrategiesTabProps> = ({
|
|
|
34
33
|
canManageRegistration,
|
|
35
34
|
onDataChange,
|
|
36
35
|
}) => {
|
|
37
|
-
const
|
|
38
|
-
const authClient = rpcApi.forPlugin(AuthApi);
|
|
36
|
+
const authClient = usePluginClient(AuthApi);
|
|
39
37
|
const toast = useToast();
|
|
40
38
|
|
|
41
39
|
const [reloading, setReloading] = useState(false);
|
|
@@ -45,14 +43,9 @@ export const StrategiesTab: React.FC<StrategiesTabProps> = ({
|
|
|
45
43
|
>({});
|
|
46
44
|
|
|
47
45
|
// Registration state
|
|
48
|
-
const [registrationSchema, setRegistrationSchema] = useState<
|
|
49
|
-
Record<string, unknown> | undefined
|
|
50
|
-
>();
|
|
51
46
|
const [registrationSettings, setRegistrationSettings] = useState<{
|
|
52
47
|
allowRegistration: boolean;
|
|
53
48
|
}>({ allowRegistration: true });
|
|
54
|
-
const [loadingRegistration, setLoadingRegistration] = useState(true);
|
|
55
|
-
const [savingRegistration, setSavingRegistration] = useState(false);
|
|
56
49
|
const [registrationValid, setRegistrationValid] = useState(true);
|
|
57
50
|
|
|
58
51
|
// Initialize strategy configs when strategies change
|
|
@@ -64,104 +57,101 @@ export const StrategiesTab: React.FC<StrategiesTabProps> = ({
|
|
|
64
57
|
setStrategyConfigs(configs);
|
|
65
58
|
}, [strategies]);
|
|
66
59
|
|
|
67
|
-
//
|
|
60
|
+
// Query: Registration schema (admin only)
|
|
61
|
+
const { data: registrationSchema, isLoading: schemaLoading } =
|
|
62
|
+
authClient.getRegistrationSchema.useQuery(
|
|
63
|
+
{},
|
|
64
|
+
{ enabled: canManageRegistration }
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Query: Registration status (admin only)
|
|
68
|
+
const { data: registrationStatus, isLoading: statusLoading } =
|
|
69
|
+
authClient.getRegistrationStatus.useQuery(
|
|
70
|
+
{},
|
|
71
|
+
{ enabled: canManageRegistration }
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
// Sync fetched settings to local state
|
|
68
75
|
useEffect(() => {
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
return;
|
|
76
|
+
if (registrationStatus) {
|
|
77
|
+
setRegistrationSettings(registrationStatus);
|
|
72
78
|
}
|
|
79
|
+
}, [registrationStatus]);
|
|
73
80
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
81
|
+
const loadingRegistration = schemaLoading || statusLoading;
|
|
82
|
+
|
|
83
|
+
// Mutations
|
|
84
|
+
const updateStrategyMutation = authClient.updateStrategy.useMutation({
|
|
85
|
+
onSuccess: () => {
|
|
86
|
+
toast.success("Strategy updated");
|
|
87
|
+
void onDataChange();
|
|
88
|
+
},
|
|
89
|
+
onError: (error) => {
|
|
90
|
+
toast.error(
|
|
91
|
+
error instanceof Error ? error.message : "Failed to update strategy"
|
|
92
|
+
);
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const setRegistrationMutation = authClient.setRegistrationStatus.useMutation({
|
|
97
|
+
onSuccess: () => {
|
|
98
|
+
toast.success("Registration settings saved");
|
|
99
|
+
},
|
|
100
|
+
onError: (error) => {
|
|
101
|
+
toast.error(
|
|
102
|
+
error instanceof Error ? error.message : "Failed to save settings"
|
|
103
|
+
);
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const reloadAuthMutation = authClient.reloadAuth.useMutation({
|
|
108
|
+
onSuccess: () => {
|
|
109
|
+
toast.success("Authentication system reloaded");
|
|
110
|
+
void onDataChange();
|
|
111
|
+
setReloading(false);
|
|
112
|
+
},
|
|
113
|
+
onError: (error) => {
|
|
114
|
+
toast.error(
|
|
115
|
+
error instanceof Error ? error.message : "Failed to reload auth"
|
|
116
|
+
);
|
|
117
|
+
setReloading(false);
|
|
118
|
+
},
|
|
119
|
+
});
|
|
91
120
|
|
|
92
121
|
const handleToggleStrategy = async (strategyId: string, enabled: boolean) => {
|
|
93
|
-
|
|
94
|
-
await authClient.updateStrategy({ id: strategyId, enabled });
|
|
95
|
-
await onDataChange();
|
|
96
|
-
} catch (error: unknown) {
|
|
97
|
-
const message =
|
|
98
|
-
error instanceof Error ? error.message : "Failed to toggle strategy";
|
|
99
|
-
toast.error(message);
|
|
100
|
-
}
|
|
122
|
+
await updateStrategyMutation.mutateAsync({ id: strategyId, enabled });
|
|
101
123
|
};
|
|
102
124
|
|
|
103
125
|
const handleSaveStrategyConfig = async (
|
|
104
126
|
strategyId: string,
|
|
105
127
|
config: Record<string, unknown>
|
|
106
128
|
) => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
setStrategyConfigs({
|
|
114
|
-
...strategyConfigs,
|
|
115
|
-
[strategyId]: config,
|
|
116
|
-
});
|
|
117
|
-
await authClient.updateStrategy({
|
|
118
|
-
id: strategyId,
|
|
119
|
-
enabled: strategy.enabled,
|
|
120
|
-
config,
|
|
121
|
-
});
|
|
122
|
-
toast.success(
|
|
123
|
-
"Configuration saved successfully! Click 'Reload Authentication' to apply changes."
|
|
124
|
-
);
|
|
125
|
-
} catch (error: unknown) {
|
|
126
|
-
const message =
|
|
127
|
-
error instanceof Error
|
|
128
|
-
? error.message
|
|
129
|
-
: "Failed to save strategy configuration";
|
|
130
|
-
toast.error(message);
|
|
129
|
+
const strategy = strategies.find((s) => s.id === strategyId);
|
|
130
|
+
if (!strategy) {
|
|
131
|
+
toast.error("Strategy not found");
|
|
132
|
+
return;
|
|
131
133
|
}
|
|
134
|
+
setStrategyConfigs({
|
|
135
|
+
...strategyConfigs,
|
|
136
|
+
[strategyId]: config,
|
|
137
|
+
});
|
|
138
|
+
await updateStrategyMutation.mutateAsync({
|
|
139
|
+
id: strategyId,
|
|
140
|
+
enabled: strategy.enabled,
|
|
141
|
+
config,
|
|
142
|
+
});
|
|
143
|
+
toast.success(
|
|
144
|
+
"Configuration saved successfully! Click 'Reload Authentication' to apply changes."
|
|
145
|
+
);
|
|
132
146
|
};
|
|
133
147
|
|
|
134
|
-
const handleSaveRegistration =
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
await authClient.setRegistrationStatus(registrationSettings);
|
|
138
|
-
toast.success("Registration settings saved successfully");
|
|
139
|
-
} catch (error: unknown) {
|
|
140
|
-
toast.error(
|
|
141
|
-
error instanceof Error
|
|
142
|
-
? error.message
|
|
143
|
-
: "Failed to save registration settings"
|
|
144
|
-
);
|
|
145
|
-
} finally {
|
|
146
|
-
setSavingRegistration(false);
|
|
147
|
-
}
|
|
148
|
+
const handleSaveRegistration = () => {
|
|
149
|
+
setRegistrationMutation.mutate(registrationSettings);
|
|
148
150
|
};
|
|
149
151
|
|
|
150
|
-
const handleReloadAuth =
|
|
152
|
+
const handleReloadAuth = () => {
|
|
151
153
|
setReloading(true);
|
|
152
|
-
|
|
153
|
-
await authClient.reloadAuth();
|
|
154
|
-
toast.success("Authentication system reloaded successfully");
|
|
155
|
-
await onDataChange();
|
|
156
|
-
} catch (error: unknown) {
|
|
157
|
-
toast.error(
|
|
158
|
-
error instanceof Error
|
|
159
|
-
? error.message
|
|
160
|
-
: "Failed to reload authentication"
|
|
161
|
-
);
|
|
162
|
-
} finally {
|
|
163
|
-
setReloading(false);
|
|
164
|
-
}
|
|
154
|
+
reloadAuthMutation.mutate({});
|
|
165
155
|
};
|
|
166
156
|
|
|
167
157
|
const enabledStrategies = strategies.filter((s) => s.enabled);
|
|
@@ -183,7 +173,7 @@ export const StrategiesTab: React.FC<StrategiesTabProps> = ({
|
|
|
183
173
|
) : registrationSchema ? (
|
|
184
174
|
<div className="space-y-4">
|
|
185
175
|
<DynamicForm
|
|
186
|
-
schema={registrationSchema}
|
|
176
|
+
schema={registrationSchema as Record<string, unknown>}
|
|
187
177
|
value={registrationSettings}
|
|
188
178
|
onChange={(value) =>
|
|
189
179
|
setRegistrationSettings(
|
|
@@ -193,10 +183,14 @@ export const StrategiesTab: React.FC<StrategiesTabProps> = ({
|
|
|
193
183
|
onValidChange={setRegistrationValid}
|
|
194
184
|
/>
|
|
195
185
|
<Button
|
|
196
|
-
onClick={
|
|
197
|
-
disabled={
|
|
186
|
+
onClick={handleSaveRegistration}
|
|
187
|
+
disabled={
|
|
188
|
+
setRegistrationMutation.isPending || !registrationValid
|
|
189
|
+
}
|
|
198
190
|
>
|
|
199
|
-
{
|
|
191
|
+
{setRegistrationMutation.isPending
|
|
192
|
+
? "Saving..."
|
|
193
|
+
: "Save Settings"}
|
|
200
194
|
</Button>
|
|
201
195
|
</div>
|
|
202
196
|
) : (
|