@authhero/react-admin 0.12.0 → 0.14.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 +14 -0
- package/package.json +1 -1
- package/src/auth0DataProvider.ts +48 -31
- package/src/authProvider.ts +50 -7
- package/src/components/clients/edit.tsx +7 -3
- package/src/components/settings/edit.tsx +5 -0
- package/src/components/users/create.tsx +6 -0
- package/src/components/users/edit.tsx +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @authhero/react-admin
|
|
2
2
|
|
|
3
|
+
## 0.14.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 63f9c89: Remove requirement for password users to have verified emails
|
|
8
|
+
- 63f9c89: Fix the listing of logs for a user
|
|
9
|
+
|
|
10
|
+
## 0.13.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- 0f8e4e8: Change from main to control plane
|
|
15
|
+
- 3a180df: Fix organization names for main tenant
|
|
16
|
+
|
|
3
17
|
## 0.12.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/package.json
CHANGED
package/src/auth0DataProvider.ts
CHANGED
|
@@ -205,29 +205,7 @@ export default (
|
|
|
205
205
|
resourceKey: "organizations",
|
|
206
206
|
idKey: "id",
|
|
207
207
|
},
|
|
208
|
-
|
|
209
|
-
fetch: (client) => {
|
|
210
|
-
// Build the query string, combining search query and IP filter
|
|
211
|
-
let query = params.filter?.q || "";
|
|
212
|
-
if (params.filter?.ip) {
|
|
213
|
-
const ipQuery = `ip:${params.filter.ip}`;
|
|
214
|
-
query = query ? `${query} AND ${ipQuery}` : ipQuery;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return client.logs.list({
|
|
218
|
-
page: page - 1,
|
|
219
|
-
per_page: perPage,
|
|
220
|
-
q: query || undefined,
|
|
221
|
-
sort:
|
|
222
|
-
field && order
|
|
223
|
-
? `${field}:${order === "DESC" ? "-1" : "1"}`
|
|
224
|
-
: undefined,
|
|
225
|
-
include_totals: true,
|
|
226
|
-
});
|
|
227
|
-
},
|
|
228
|
-
resourceKey: "logs",
|
|
229
|
-
idKey: "log_id",
|
|
230
|
-
},
|
|
208
|
+
// Logs removed from SDK handlers - using HTTP directly for full control
|
|
231
209
|
rules: {
|
|
232
210
|
fetch: (client) => client.rules.list(),
|
|
233
211
|
resourceKey: "rules",
|
|
@@ -257,9 +235,9 @@ export default (
|
|
|
257
235
|
},
|
|
258
236
|
};
|
|
259
237
|
|
|
260
|
-
// Handle SDK resources
|
|
238
|
+
// Handle SDK resources (only for top-level resources, not nested paths like users/{id}/roles)
|
|
261
239
|
const handler = sdkHandlers[resource];
|
|
262
|
-
if (handler) {
|
|
240
|
+
if (handler && !resourcePath.includes("/")) {
|
|
263
241
|
const result = await handler.fetch(managementClient);
|
|
264
242
|
const { data, total } = normalizeSDKResponse(
|
|
265
243
|
result,
|
|
@@ -285,6 +263,35 @@ export default (
|
|
|
285
263
|
);
|
|
286
264
|
}
|
|
287
265
|
|
|
266
|
+
// Handle logs with direct HTTP for full control over query params
|
|
267
|
+
if (resource === "logs") {
|
|
268
|
+
const headers = createHeaders(tenantId);
|
|
269
|
+
const query: any = {
|
|
270
|
+
include_totals: true,
|
|
271
|
+
page: page - 1,
|
|
272
|
+
per_page: perPage,
|
|
273
|
+
sort:
|
|
274
|
+
field && order
|
|
275
|
+
? `${field}:${order === "DESC" ? "-1" : "1"}`
|
|
276
|
+
: undefined,
|
|
277
|
+
...params.filter, // Pass all filter params directly (q, from, etc.)
|
|
278
|
+
};
|
|
279
|
+
const url = `${apiUrl}/api/v2/logs?${stringify(query)}`;
|
|
280
|
+
|
|
281
|
+
const res = await httpClient(url, { headers });
|
|
282
|
+
const response = res.json;
|
|
283
|
+
const logsData = response.logs || response || [];
|
|
284
|
+
const logsArray = Array.isArray(logsData) ? logsData : [];
|
|
285
|
+
|
|
286
|
+
return {
|
|
287
|
+
data: logsArray.map((log: any) => ({
|
|
288
|
+
id: log.log_id || log.id,
|
|
289
|
+
...log,
|
|
290
|
+
})),
|
|
291
|
+
total: response.total || logsArray.length,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
288
295
|
// Handle stats/daily endpoint
|
|
289
296
|
if (resourcePath === "stats/daily") {
|
|
290
297
|
const headers = createHeaders(tenantId);
|
|
@@ -704,9 +711,10 @@ export default (
|
|
|
704
711
|
};
|
|
705
712
|
}
|
|
706
713
|
|
|
707
|
-
// Logs filtered by user_id
|
|
714
|
+
// Logs filtered by user_id - use direct HTTP for full control
|
|
708
715
|
if (resource === "logs" && params.target === "user_id") {
|
|
709
|
-
const
|
|
716
|
+
const headers = createHeaders(tenantId);
|
|
717
|
+
const query = {
|
|
710
718
|
page: page - 1,
|
|
711
719
|
per_page: perPage,
|
|
712
720
|
q: `user_id:${params.id}`,
|
|
@@ -715,12 +723,21 @@ export default (
|
|
|
715
723
|
? `${field}:${order === "DESC" ? "-1" : "1"}`
|
|
716
724
|
: undefined,
|
|
717
725
|
include_totals: true,
|
|
718
|
-
|
|
726
|
+
...params.filter, // Allow additional filters to be passed through
|
|
727
|
+
};
|
|
728
|
+
const url = `${apiUrl}/api/v2/logs?${stringify(query)}`;
|
|
729
|
+
|
|
730
|
+
const res = await httpClient(url, { headers });
|
|
731
|
+
const response = res.json;
|
|
732
|
+
const logsData = response.logs || response || [];
|
|
733
|
+
const logsArray = Array.isArray(logsData) ? logsData : [];
|
|
719
734
|
|
|
720
|
-
const normalized = normalizeSDKResponse(result, "logs");
|
|
721
735
|
return {
|
|
722
|
-
data:
|
|
723
|
-
|
|
736
|
+
data: logsArray.map((log: any) => ({
|
|
737
|
+
id: log.log_id || log.id,
|
|
738
|
+
...log,
|
|
739
|
+
})),
|
|
740
|
+
total: response.total || logsArray.length,
|
|
724
741
|
};
|
|
725
742
|
}
|
|
726
743
|
|
package/src/authProvider.ts
CHANGED
|
@@ -196,12 +196,35 @@ export const createManagementClient = async (
|
|
|
196
196
|
const orgAuth0Client = createAuth0ClientForOrg(domainForAuth, tenantId);
|
|
197
197
|
const audience =
|
|
198
198
|
import.meta.env.VITE_AUTH0_AUDIENCE || "urn:authhero:management";
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
199
|
+
try {
|
|
200
|
+
token = await orgAuth0Client.getTokenSilently({
|
|
201
|
+
authorizationParams: {
|
|
202
|
+
audience,
|
|
203
|
+
organization: tenantId,
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
} catch (error) {
|
|
207
|
+
// If silent token acquisition fails, redirect to login with org
|
|
208
|
+
// Get the base auth0 client to get the user's email for login hint
|
|
209
|
+
const baseClient = createAuth0Client(domainForAuth);
|
|
210
|
+
const user = await baseClient.getUser().catch(() => null);
|
|
211
|
+
|
|
212
|
+
// Redirect to login with organization
|
|
213
|
+
await orgAuth0Client.loginWithRedirect({
|
|
214
|
+
authorizationParams: {
|
|
215
|
+
organization: tenantId,
|
|
216
|
+
login_hint: user?.email,
|
|
217
|
+
},
|
|
218
|
+
appState: {
|
|
219
|
+
returnTo: window.location.pathname,
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// This won't be reached as loginWithRedirect redirects the page
|
|
224
|
+
throw new Error(
|
|
225
|
+
`Redirecting to login for organization ${tenantId}`,
|
|
226
|
+
);
|
|
227
|
+
}
|
|
205
228
|
} else {
|
|
206
229
|
// For token/client_credentials, use getOrganizationToken
|
|
207
230
|
token = await getOrganizationToken(domainConfig, tenantId);
|
|
@@ -751,6 +774,16 @@ export const createOrganizationHttpClient = (organizationId: string) => {
|
|
|
751
774
|
})
|
|
752
775
|
.then((token) => {
|
|
753
776
|
const headersObj = new Headers();
|
|
777
|
+
// Merge any headers passed in options
|
|
778
|
+
if (options.headers) {
|
|
779
|
+
const incomingHeaders =
|
|
780
|
+
options.headers instanceof Headers
|
|
781
|
+
? options.headers
|
|
782
|
+
: new Headers(options.headers as HeadersInit);
|
|
783
|
+
incomingHeaders.forEach((value, key) => {
|
|
784
|
+
headersObj.set(key, value);
|
|
785
|
+
});
|
|
786
|
+
}
|
|
754
787
|
headersObj.set("Authorization", `Bearer ${token}`);
|
|
755
788
|
const method = (options.method || "GET").toUpperCase();
|
|
756
789
|
if (method === "POST" || method === "PATCH") {
|
|
@@ -819,7 +852,7 @@ export const createOrganizationHttpClient = (organizationId: string) => {
|
|
|
819
852
|
organization: organizationId,
|
|
820
853
|
},
|
|
821
854
|
})
|
|
822
|
-
.catch(async (
|
|
855
|
+
.catch(async (_error) => {
|
|
823
856
|
// If silent token acquisition fails, we need to redirect to login with org
|
|
824
857
|
// Get the base auth0 client to get the user's email for login hint
|
|
825
858
|
const baseClient = createAuth0Client(selectedDomain);
|
|
@@ -843,6 +876,16 @@ export const createOrganizationHttpClient = (organizationId: string) => {
|
|
|
843
876
|
})
|
|
844
877
|
.then((token) => {
|
|
845
878
|
const headersObj = new Headers();
|
|
879
|
+
// Merge any headers passed in options
|
|
880
|
+
if (options.headers) {
|
|
881
|
+
const incomingHeaders =
|
|
882
|
+
options.headers instanceof Headers
|
|
883
|
+
? options.headers
|
|
884
|
+
: new Headers(options.headers as HeadersInit);
|
|
885
|
+
incomingHeaders.forEach((value, key) => {
|
|
886
|
+
headersObj.set(key, value);
|
|
887
|
+
});
|
|
888
|
+
}
|
|
846
889
|
headersObj.set("Authorization", `Bearer ${token}`);
|
|
847
890
|
const method = (options.method || "GET").toUpperCase();
|
|
848
891
|
if (method === "POST" || method === "PATCH") {
|
|
@@ -55,7 +55,7 @@ import EditIcon from "@mui/icons-material/Edit";
|
|
|
55
55
|
import DragIndicatorIcon from "@mui/icons-material/DragIndicator";
|
|
56
56
|
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
|
|
57
57
|
import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
|
|
58
|
-
import {
|
|
58
|
+
import { createOrganizationHttpClient } from "../../authProvider";
|
|
59
59
|
import {
|
|
60
60
|
getDomainFromStorage,
|
|
61
61
|
getSelectedDomainFromStorage,
|
|
@@ -864,8 +864,10 @@ const ConnectionsTab = () => {
|
|
|
864
864
|
setLoading(true);
|
|
865
865
|
try {
|
|
866
866
|
// Fetch enabled connections from the new API endpoint
|
|
867
|
+
// Use organization-scoped HTTP client to ensure proper org_id in token
|
|
867
868
|
const baseUrl = getApiBaseUrl();
|
|
868
|
-
const
|
|
869
|
+
const orgHttpClient = createOrganizationHttpClient(tenantId);
|
|
870
|
+
const response = await orgHttpClient(
|
|
869
871
|
`${baseUrl}/api/v2/clients/${clientId}/connections`,
|
|
870
872
|
{
|
|
871
873
|
method: "GET",
|
|
@@ -926,8 +928,10 @@ const ConnectionsTab = () => {
|
|
|
926
928
|
|
|
927
929
|
setSaving(true);
|
|
928
930
|
try {
|
|
931
|
+
// Use organization-scoped HTTP client to ensure proper org_id in token
|
|
929
932
|
const baseUrl = getApiBaseUrl();
|
|
930
|
-
|
|
933
|
+
const orgHttpClient = createOrganizationHttpClient(tenantId);
|
|
934
|
+
await orgHttpClient(
|
|
931
935
|
`${baseUrl}/api/v2/clients/${clientId}/connections`,
|
|
932
936
|
{
|
|
933
937
|
method: "PATCH",
|
|
@@ -126,6 +126,11 @@ export function SettingsEdit() {
|
|
|
126
126
|
|
|
127
127
|
<TabbedForm.Tab label="Feature Flags">
|
|
128
128
|
<Stack spacing={2}>
|
|
129
|
+
<BooleanInput
|
|
130
|
+
source="allow_organization_name_in_authentication_api"
|
|
131
|
+
label="Allow Organization Name in Authentication API"
|
|
132
|
+
helperText="Allow using organization names (instead of IDs) in the /authorize endpoint and include org_name claim in tokens"
|
|
133
|
+
/>
|
|
129
134
|
<BooleanInput
|
|
130
135
|
source="flags.allow_legacy_delegation_grant_types"
|
|
131
136
|
label="Allow Legacy Delegation Grant Types"
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
SelectInput,
|
|
7
7
|
useGetList,
|
|
8
8
|
FormDataConsumer,
|
|
9
|
+
BooleanInput,
|
|
9
10
|
} from "react-admin";
|
|
10
11
|
import { useState } from "react";
|
|
11
12
|
|
|
@@ -101,6 +102,11 @@ export function UserCreate() {
|
|
|
101
102
|
validate={[required()]}
|
|
102
103
|
helperText="Password for the user account"
|
|
103
104
|
/>
|
|
105
|
+
<BooleanInput
|
|
106
|
+
source="email_verified"
|
|
107
|
+
label="Email Verified"
|
|
108
|
+
defaultValue={false}
|
|
109
|
+
/>
|
|
104
110
|
</>
|
|
105
111
|
);
|
|
106
112
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
TextInput,
|
|
13
13
|
FunctionField,
|
|
14
14
|
BooleanField,
|
|
15
|
+
BooleanInput,
|
|
15
16
|
ArrayField,
|
|
16
17
|
SimpleShowLayout,
|
|
17
18
|
useNotify,
|
|
@@ -1444,6 +1445,7 @@ export function UserEdit() {
|
|
|
1444
1445
|
>
|
|
1445
1446
|
<TextField source="connection" />
|
|
1446
1447
|
</Labeled>
|
|
1448
|
+
<BooleanInput source="email_verified" label="Email Verified" />
|
|
1447
1449
|
</Stack>
|
|
1448
1450
|
<TextInput source="picture" />
|
|
1449
1451
|
<ArrayField source="identities">
|