@authhero/react-admin 0.11.0 → 0.13.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 +13 -0
- package/package.json +1 -1
- package/src/authProvider.ts +89 -27
- package/src/components/clients/edit.tsx +3 -1
- package/src/components/settings/edit.tsx +5 -0
- package/src/dataProvider.ts +5 -3
- package/src/utils/tokenUtils.ts +72 -18
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @authhero/react-admin
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0f8e4e8: Change from main to control plane
|
|
8
|
+
- 3a180df: Fix organization names for main tenant
|
|
9
|
+
|
|
10
|
+
## 0.12.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- aba8ef9: Handle org tokens for the main tenant
|
|
15
|
+
|
|
3
16
|
## 0.11.0
|
|
4
17
|
|
|
5
18
|
### Minor Changes
|
package/package.json
CHANGED
package/src/authProvider.ts
CHANGED
|
@@ -6,9 +6,11 @@ import {
|
|
|
6
6
|
getClientIdFromStorage,
|
|
7
7
|
getDomainFromStorage,
|
|
8
8
|
buildUrlWithProtocol,
|
|
9
|
+
formatDomain,
|
|
9
10
|
} from "./utils/domainUtils";
|
|
10
11
|
import getToken, {
|
|
11
12
|
clearOrganizationTokenCache,
|
|
13
|
+
getOrganizationToken,
|
|
12
14
|
OrgCache,
|
|
13
15
|
} from "./utils/tokenUtils";
|
|
14
16
|
|
|
@@ -43,15 +45,16 @@ export const createAuth0Client = (domain: string) => {
|
|
|
43
45
|
const currentUrl = new URL(window.location.href);
|
|
44
46
|
const redirectUri = `${currentUrl.protocol}//${currentUrl.host}/auth-callback`;
|
|
45
47
|
|
|
46
|
-
// Use
|
|
48
|
+
// Use the management API audience for cross-tenant operations
|
|
49
|
+
// This allows the admin UI to manage tenants and their resources
|
|
47
50
|
const audience =
|
|
48
|
-
import.meta.env.VITE_AUTH0_AUDIENCE ||
|
|
51
|
+
import.meta.env.VITE_AUTH0_AUDIENCE || "urn:authhero:management";
|
|
49
52
|
|
|
50
53
|
const auth0Client = new Auth0Client({
|
|
51
54
|
domain: fullDomain,
|
|
52
55
|
clientId: getClientIdFromStorage(domain),
|
|
53
56
|
cacheLocation: "localstorage",
|
|
54
|
-
useRefreshTokens:
|
|
57
|
+
useRefreshTokens: false,
|
|
55
58
|
authorizationParams: {
|
|
56
59
|
audience,
|
|
57
60
|
redirect_uri: redirectUri,
|
|
@@ -133,21 +136,22 @@ export const createAuth0ClientForOrg = (
|
|
|
133
136
|
const currentUrl = new URL(window.location.href);
|
|
134
137
|
const redirectUri = `${currentUrl.protocol}//${currentUrl.host}/auth-callback`;
|
|
135
138
|
|
|
136
|
-
// Use
|
|
139
|
+
// Use the management API audience for cross-tenant operations
|
|
140
|
+
// The org_id claim in the token determines which tenant's resources are accessed
|
|
137
141
|
const audience =
|
|
138
|
-
import.meta.env.VITE_AUTH0_AUDIENCE ||
|
|
142
|
+
import.meta.env.VITE_AUTH0_AUDIENCE || "urn:authhero:management";
|
|
139
143
|
|
|
140
144
|
const auth0Client = new Auth0Client({
|
|
141
145
|
domain: fullDomain,
|
|
142
146
|
clientId: getClientIdFromStorage(domain),
|
|
143
|
-
useRefreshTokens:
|
|
147
|
+
useRefreshTokens: false,
|
|
144
148
|
// Use organization-specific cache to isolate tokens
|
|
145
149
|
// Note: Don't use cacheLocation when providing a custom cache
|
|
146
150
|
cache: new OrgCache(organizationId),
|
|
147
151
|
authorizationParams: {
|
|
148
152
|
audience,
|
|
149
153
|
redirect_uri: redirectUri,
|
|
150
|
-
scope: "openid profile email auth:read auth:write
|
|
154
|
+
scope: "openid profile email auth:read auth:write",
|
|
151
155
|
organization: organizationId,
|
|
152
156
|
},
|
|
153
157
|
});
|
|
@@ -171,9 +175,11 @@ export const createManagementClient = async (
|
|
|
171
175
|
}
|
|
172
176
|
|
|
173
177
|
// Use oauthDomain for finding credentials, fallback to apiDomain
|
|
174
|
-
const domainForAuth = oauthDomain || apiDomain;
|
|
178
|
+
const domainForAuth = formatDomain(oauthDomain || apiDomain);
|
|
175
179
|
const domains = getDomainFromStorage();
|
|
176
|
-
const domainConfig = domains.find(
|
|
180
|
+
const domainConfig = domains.find(
|
|
181
|
+
(d) => formatDomain(d.url) === domainForAuth,
|
|
182
|
+
);
|
|
177
183
|
|
|
178
184
|
if (!domainConfig) {
|
|
179
185
|
throw new Error(
|
|
@@ -181,13 +187,33 @@ export const createManagementClient = async (
|
|
|
181
187
|
);
|
|
182
188
|
}
|
|
183
189
|
|
|
184
|
-
|
|
185
|
-
let auth0Client: Auth0Client | undefined;
|
|
186
|
-
if (domainConfig.connectionMethod === "login") {
|
|
187
|
-
auth0Client = createAuth0Client(domainForAuth);
|
|
188
|
-
}
|
|
190
|
+
let token: string;
|
|
189
191
|
|
|
190
|
-
|
|
192
|
+
if (tenantId) {
|
|
193
|
+
// When accessing tenant-specific resources, use org-scoped token
|
|
194
|
+
if (domainConfig.connectionMethod === "login") {
|
|
195
|
+
// For OAuth login, use organization-scoped client
|
|
196
|
+
const orgAuth0Client = createAuth0ClientForOrg(domainForAuth, tenantId);
|
|
197
|
+
const audience =
|
|
198
|
+
import.meta.env.VITE_AUTH0_AUDIENCE || "urn:authhero:management";
|
|
199
|
+
token = await orgAuth0Client.getTokenSilently({
|
|
200
|
+
authorizationParams: {
|
|
201
|
+
audience,
|
|
202
|
+
organization: tenantId,
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
} else {
|
|
206
|
+
// For token/client_credentials, use getOrganizationToken
|
|
207
|
+
token = await getOrganizationToken(domainConfig, tenantId);
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
// No tenantId - get non-org-scoped token for tenant management endpoints
|
|
211
|
+
let auth0Client: Auth0Client | undefined;
|
|
212
|
+
if (domainConfig.connectionMethod === "login") {
|
|
213
|
+
auth0Client = createAuth0Client(domainForAuth);
|
|
214
|
+
}
|
|
215
|
+
token = await getToken(domainConfig, auth0Client);
|
|
216
|
+
}
|
|
191
217
|
|
|
192
218
|
// ManagementClient expects domain WITHOUT protocol (it adds https:// internally)
|
|
193
219
|
const managementClient = new ManagementClient({
|
|
@@ -215,7 +241,10 @@ export const getAuthProvider = (
|
|
|
215
241
|
) => {
|
|
216
242
|
// Get domain config to check connection method
|
|
217
243
|
const domains = getDomainFromStorage();
|
|
218
|
-
const
|
|
244
|
+
const formattedDomain = formatDomain(domain);
|
|
245
|
+
const domainConfig = domains.find(
|
|
246
|
+
(d) => formatDomain(d.url) === formattedDomain,
|
|
247
|
+
);
|
|
219
248
|
|
|
220
249
|
// If using token auth or client credentials, create a simple auth provider that uses the token
|
|
221
250
|
if (
|
|
@@ -389,17 +418,23 @@ const authorizedHttpClient = (url: string, options: HttpOptions = {}) => {
|
|
|
389
418
|
// Check if we're using token-based auth or client credentials
|
|
390
419
|
const domains = getDomainFromStorage();
|
|
391
420
|
const selectedDomain = getSelectedDomainFromStorage();
|
|
392
|
-
const
|
|
421
|
+
const formattedSelectedDomain = formatDomain(selectedDomain);
|
|
422
|
+
const domainConfig = domains.find(
|
|
423
|
+
(d) => formatDomain(d.url) === formattedSelectedDomain,
|
|
424
|
+
);
|
|
393
425
|
|
|
394
426
|
// If using login method and auth request is in progress, delay the request
|
|
395
427
|
if (
|
|
396
428
|
domainConfig?.connectionMethod === "login" &&
|
|
397
|
-
(authRequestInProgress || activeSessions.has(
|
|
429
|
+
(authRequestInProgress || activeSessions.has(formattedSelectedDomain))
|
|
398
430
|
) {
|
|
399
431
|
// Return a promise that waits for auth to complete
|
|
400
432
|
const delayedRequest = new Promise((resolve, reject) => {
|
|
401
433
|
const checkInterval = setInterval(() => {
|
|
402
|
-
if (
|
|
434
|
+
if (
|
|
435
|
+
!authRequestInProgress &&
|
|
436
|
+
!activeSessions.has(formattedSelectedDomain)
|
|
437
|
+
) {
|
|
403
438
|
clearInterval(checkInterval);
|
|
404
439
|
// Retry the request now that auth is complete
|
|
405
440
|
authorizedHttpClient(url, options).then(resolve).catch(reject);
|
|
@@ -651,16 +686,22 @@ export const createOrganizationHttpClient = (organizationId: string) => {
|
|
|
651
686
|
// Check if we're using token-based auth or client credentials
|
|
652
687
|
const domains = getDomainFromStorage();
|
|
653
688
|
const selectedDomain = getSelectedDomainFromStorage();
|
|
654
|
-
const
|
|
689
|
+
const formattedSelectedDomain = formatDomain(selectedDomain);
|
|
690
|
+
const domainConfig = domains.find(
|
|
691
|
+
(d) => formatDomain(d.url) === formattedSelectedDomain,
|
|
692
|
+
);
|
|
655
693
|
|
|
656
694
|
// If using login method and auth request is in progress, delay the request
|
|
657
695
|
if (
|
|
658
696
|
domainConfig?.connectionMethod === "login" &&
|
|
659
|
-
(authRequestInProgress || activeSessions.has(
|
|
697
|
+
(authRequestInProgress || activeSessions.has(formattedSelectedDomain))
|
|
660
698
|
) {
|
|
661
699
|
const delayedRequest = new Promise((resolve, reject) => {
|
|
662
700
|
const checkInterval = setInterval(() => {
|
|
663
|
-
if (
|
|
701
|
+
if (
|
|
702
|
+
!authRequestInProgress &&
|
|
703
|
+
!activeSessions.has(formattedSelectedDomain)
|
|
704
|
+
) {
|
|
664
705
|
clearInterval(checkInterval);
|
|
665
706
|
createOrganizationHttpClient(organizationId)(url, options)
|
|
666
707
|
.then(resolve)
|
|
@@ -700,8 +741,9 @@ export const createOrganizationHttpClient = (organizationId: string) => {
|
|
|
700
741
|
domainConfig.connectionMethod || "",
|
|
701
742
|
)
|
|
702
743
|
) {
|
|
703
|
-
// For token/client_credentials, use
|
|
704
|
-
|
|
744
|
+
// For token/client_credentials, use organization-scoped token
|
|
745
|
+
// This includes the org_id claim for accessing tenant-specific resources
|
|
746
|
+
request = getOrganizationToken(domainConfig, organizationId)
|
|
705
747
|
.catch((error) => {
|
|
706
748
|
throw new Error(
|
|
707
749
|
`Authentication failed: ${error.message}. Please configure your credentials in the domain selector.`,
|
|
@@ -709,6 +751,16 @@ export const createOrganizationHttpClient = (organizationId: string) => {
|
|
|
709
751
|
})
|
|
710
752
|
.then((token) => {
|
|
711
753
|
const headersObj = new Headers();
|
|
754
|
+
// Merge any headers passed in options
|
|
755
|
+
if (options.headers) {
|
|
756
|
+
const incomingHeaders =
|
|
757
|
+
options.headers instanceof Headers
|
|
758
|
+
? options.headers
|
|
759
|
+
: new Headers(options.headers as HeadersInit);
|
|
760
|
+
incomingHeaders.forEach((value, key) => {
|
|
761
|
+
headersObj.set(key, value);
|
|
762
|
+
});
|
|
763
|
+
}
|
|
712
764
|
headersObj.set("Authorization", `Bearer ${token}`);
|
|
713
765
|
const method = (options.method || "GET").toUpperCase();
|
|
714
766
|
if (method === "POST" || method === "PATCH") {
|
|
@@ -764,10 +816,10 @@ export const createOrganizationHttpClient = (organizationId: string) => {
|
|
|
764
816
|
organizationId,
|
|
765
817
|
);
|
|
766
818
|
|
|
767
|
-
//
|
|
819
|
+
// Use the management API audience for cross-tenant operations
|
|
820
|
+
// The org_id in the token will determine which tenant's resources are being accessed
|
|
768
821
|
const audience =
|
|
769
|
-
import.meta.env.VITE_AUTH0_AUDIENCE ||
|
|
770
|
-
`https://${selectedDomain}/api/v2/`;
|
|
822
|
+
import.meta.env.VITE_AUTH0_AUDIENCE || "urn:authhero:management";
|
|
771
823
|
|
|
772
824
|
// First, check if we have a valid session for this organization
|
|
773
825
|
request = orgAuth0Client
|
|
@@ -801,6 +853,16 @@ export const createOrganizationHttpClient = (organizationId: string) => {
|
|
|
801
853
|
})
|
|
802
854
|
.then((token) => {
|
|
803
855
|
const headersObj = new Headers();
|
|
856
|
+
// Merge any headers passed in options
|
|
857
|
+
if (options.headers) {
|
|
858
|
+
const incomingHeaders =
|
|
859
|
+
options.headers instanceof Headers
|
|
860
|
+
? options.headers
|
|
861
|
+
: new Headers(options.headers as HeadersInit);
|
|
862
|
+
incomingHeaders.forEach((value, key) => {
|
|
863
|
+
headersObj.set(key, value);
|
|
864
|
+
});
|
|
865
|
+
}
|
|
804
866
|
headersObj.set("Authorization", `Bearer ${token}`);
|
|
805
867
|
const method = (options.method || "GET").toUpperCase();
|
|
806
868
|
if (method === "POST" || method === "PATCH") {
|
|
@@ -60,6 +60,7 @@ import {
|
|
|
60
60
|
getDomainFromStorage,
|
|
61
61
|
getSelectedDomainFromStorage,
|
|
62
62
|
buildUrlWithProtocol,
|
|
63
|
+
formatDomain,
|
|
63
64
|
} from "../../utils/domainUtils";
|
|
64
65
|
|
|
65
66
|
const AddClientGrantButton = () => {
|
|
@@ -821,7 +822,8 @@ interface Connection {
|
|
|
821
822
|
const getApiBaseUrl = (): string => {
|
|
822
823
|
const selectedDomain = getSelectedDomainFromStorage();
|
|
823
824
|
const domains = getDomainFromStorage();
|
|
824
|
-
const
|
|
825
|
+
const formattedDomain = formatDomain(selectedDomain);
|
|
826
|
+
const domainConfig = domains.find((d) => formatDomain(d.url) === formattedDomain);
|
|
825
827
|
|
|
826
828
|
if (domainConfig?.restApiUrl) {
|
|
827
829
|
return domainConfig.restApiUrl.replace(/\/$/, "");
|
|
@@ -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"
|
package/src/dataProvider.ts
CHANGED
|
@@ -7,6 +7,7 @@ import auth0DataProvider from "./auth0DataProvider";
|
|
|
7
7
|
import {
|
|
8
8
|
getDomainFromStorage,
|
|
9
9
|
buildUrlWithProtocol,
|
|
10
|
+
formatDomain,
|
|
10
11
|
} from "./utils/domainUtils";
|
|
11
12
|
|
|
12
13
|
async function removeExtraFields(params: UpdateParams) {
|
|
@@ -32,7 +33,8 @@ export function getDataprovider(auth0Domain?: string) {
|
|
|
32
33
|
if (auth0Domain) {
|
|
33
34
|
// Check if there's a custom REST API URL configured for this domain
|
|
34
35
|
const domains = getDomainFromStorage();
|
|
35
|
-
const
|
|
36
|
+
const formattedAuth0Domain = formatDomain(auth0Domain);
|
|
37
|
+
const domainConfig = domains.find((d) => formatDomain(d.url) === formattedAuth0Domain);
|
|
36
38
|
|
|
37
39
|
if (domainConfig?.restApiUrl) {
|
|
38
40
|
// Use the custom REST API URL if configured
|
|
@@ -70,8 +72,8 @@ export function getDataproviderForTenant(
|
|
|
70
72
|
if (auth0Domain) {
|
|
71
73
|
// Check if there's a custom REST API URL configured for this domain
|
|
72
74
|
const domains = getDomainFromStorage();
|
|
73
|
-
|
|
74
|
-
const domainConfig = domains.find((d) => d.url ===
|
|
75
|
+
const formattedAuth0Domain = formatDomain(auth0Domain);
|
|
76
|
+
const domainConfig = domains.find((d) => formatDomain(d.url) === formattedAuth0Domain);
|
|
75
77
|
|
|
76
78
|
if (domainConfig?.restApiUrl) {
|
|
77
79
|
// Use the custom REST API URL if configured
|
package/src/utils/tokenUtils.ts
CHANGED
|
@@ -57,28 +57,37 @@ async function fetchTokenWithClientCredentials(
|
|
|
57
57
|
domain: string,
|
|
58
58
|
clientId: string,
|
|
59
59
|
clientSecret: string,
|
|
60
|
+
organizationId?: string,
|
|
60
61
|
): Promise<string> {
|
|
62
|
+
const body: Record<string, string> = {
|
|
63
|
+
grant_type: "client_credentials",
|
|
64
|
+
client_id: clientId,
|
|
65
|
+
client_secret: clientSecret,
|
|
66
|
+
// Use the management API audience for cross-tenant operations
|
|
67
|
+
audience: "urn:authhero:management",
|
|
68
|
+
scope:
|
|
69
|
+
"read:users update:users create:users delete:users read:user_idp_tokens " +
|
|
70
|
+
"read:clients update:clients create:clients delete:clients " +
|
|
71
|
+
"read:connections update:connections create:connections delete:connections " +
|
|
72
|
+
"read:resource_servers update:resource_servers create:resource_servers delete:resource_servers " +
|
|
73
|
+
"read:rules update:rules create:rules delete:rules " +
|
|
74
|
+
"read:email_templates update:email_templates " +
|
|
75
|
+
"read:tenant_settings update:tenant_settings " +
|
|
76
|
+
"read:logs read:stats read:branding update:branding read:forms",
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Add organization if specified - this will include org_id in the token
|
|
80
|
+
if (organizationId) {
|
|
81
|
+
body.organization = organizationId;
|
|
82
|
+
}
|
|
83
|
+
|
|
61
84
|
const response = await fetch(`https://proxy.authhe.ro/oauth/token`, {
|
|
62
85
|
method: "POST",
|
|
63
86
|
headers: {
|
|
64
87
|
"Content-Type": "application/json",
|
|
65
88
|
"X-Auth0-Domain": domain,
|
|
66
89
|
},
|
|
67
|
-
body: JSON.stringify(
|
|
68
|
-
grant_type: "client_credentials",
|
|
69
|
-
client_id: clientId,
|
|
70
|
-
client_secret: clientSecret,
|
|
71
|
-
audience: `https://${domain}/api/v2/`,
|
|
72
|
-
scope:
|
|
73
|
-
"read:users update:users create:users delete:users read:user_idp_tokens " +
|
|
74
|
-
"read:clients update:clients create:clients delete:clients " +
|
|
75
|
-
"read:connections update:connections create:connections delete:connections " +
|
|
76
|
-
"read:resource_servers update:resource_servers create:resource_servers delete:resource_servers " +
|
|
77
|
-
"read:rules update:rules create:rules delete:rules " +
|
|
78
|
-
"read:email_templates update:email_templates " +
|
|
79
|
-
"read:tenant_settings update:tenant_settings " +
|
|
80
|
-
"read:logs read:stats read:branding update:branding read:forms",
|
|
81
|
-
}),
|
|
90
|
+
body: JSON.stringify(body),
|
|
82
91
|
});
|
|
83
92
|
|
|
84
93
|
if (!response.ok) {
|
|
@@ -96,12 +105,52 @@ async function fetchTokenWithClientCredentials(
|
|
|
96
105
|
export function clearOrganizationTokenCache(): void {
|
|
97
106
|
// Clear all org-cached tokens from localStorage
|
|
98
107
|
const keysToRemove = Object.keys(window.localStorage).filter(
|
|
99
|
-
(key) =>
|
|
100
|
-
key.startsWith(CACHE_KEY_PREFIX) && key.match(/:[^:]+$/),
|
|
108
|
+
(key) => key.startsWith(CACHE_KEY_PREFIX) && key.match(/:[^:]+$/),
|
|
101
109
|
);
|
|
102
110
|
keysToRemove.forEach((key) => localStorage.removeItem(key));
|
|
103
111
|
}
|
|
104
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Get an organization-scoped token for the given domain configuration.
|
|
115
|
+
* This token will have the org_id claim set to the specified organization.
|
|
116
|
+
* Used for accessing tenant-specific resources.
|
|
117
|
+
*/
|
|
118
|
+
export async function getOrganizationToken(
|
|
119
|
+
domainConfig: DomainConfig,
|
|
120
|
+
organizationId: string,
|
|
121
|
+
): Promise<string> {
|
|
122
|
+
if (
|
|
123
|
+
domainConfig.connectionMethod === "client_credentials" &&
|
|
124
|
+
domainConfig.clientId &&
|
|
125
|
+
domainConfig.clientSecret
|
|
126
|
+
) {
|
|
127
|
+
// For client credentials, fetch a token with organization parameter
|
|
128
|
+
const token = await fetchTokenWithClientCredentials(
|
|
129
|
+
domainConfig.url,
|
|
130
|
+
domainConfig.clientId,
|
|
131
|
+
domainConfig.clientSecret,
|
|
132
|
+
organizationId,
|
|
133
|
+
);
|
|
134
|
+
return token;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// For token-based auth, we can't add org_id dynamically
|
|
138
|
+
// The token must already have the correct org_id claim
|
|
139
|
+
if (domainConfig.connectionMethod === "token") {
|
|
140
|
+
// Static tokens cannot have dynamic org_id - this is a limitation
|
|
141
|
+
throw new Error(
|
|
142
|
+
"Token-based auth cannot provide organization-scoped tokens. " +
|
|
143
|
+
"Use client_credentials or login authentication method for multi-tenant access.",
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// For login method, organization-scoped tokens are handled separately
|
|
148
|
+
// via OAuth with organization parameter in createOrganizationHttpClient
|
|
149
|
+
throw new Error(
|
|
150
|
+
"Organization-scoped tokens require client_credentials or login authentication method.",
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
105
154
|
/**
|
|
106
155
|
* Get a token for the given domain configuration.
|
|
107
156
|
* For OAuth login, this gets a token WITHOUT organization scope.
|
|
@@ -128,8 +177,13 @@ export default async function getToken(
|
|
|
128
177
|
return token;
|
|
129
178
|
} else if (domainConfig.connectionMethod === "login" && auth0Client) {
|
|
130
179
|
// Get a regular token WITHOUT organization scope
|
|
180
|
+
// This is used for tenant management endpoints which require non-org-scoped tokens
|
|
131
181
|
try {
|
|
132
|
-
const token = await auth0Client.getTokenSilently(
|
|
182
|
+
const token = await auth0Client.getTokenSilently({
|
|
183
|
+
authorizationParams: {
|
|
184
|
+
organization: undefined,
|
|
185
|
+
},
|
|
186
|
+
});
|
|
133
187
|
return token;
|
|
134
188
|
} catch (error) {
|
|
135
189
|
throw new Error(
|