zudoku 0.1.1-dev.50 → 0.1.1-dev.51
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/config/config.d.ts +19 -2
- package/dist/config/validators/auth.d.ts +2 -0
- package/dist/config/validators/auth.js +2 -0
- package/dist/config/validators/auth.js.map +1 -0
- package/dist/config/validators/validate.d.ts +2 -0
- package/dist/config/validators/validate.js +4 -0
- package/dist/config/validators/validate.js.map +1 -0
- package/dist/lib/authentication/Callback.d.ts +4 -0
- package/dist/lib/authentication/Callback.js +20 -0
- package/dist/lib/authentication/Callback.js.map +1 -0
- package/dist/lib/authentication/auth0.d.ts +5 -0
- package/dist/lib/authentication/auth0.js +9 -0
- package/dist/lib/authentication/auth0.js.map +1 -0
- package/dist/lib/authentication/authentication.d.ts +7 -10
- package/dist/lib/authentication/clerk.d.ts +3 -4
- package/dist/lib/authentication/clerk.js +13 -6
- package/dist/lib/authentication/clerk.js.map +1 -1
- package/dist/lib/authentication/openid.d.ts +5 -11
- package/dist/lib/authentication/openid.js +90 -73
- package/dist/lib/authentication/openid.js.map +1 -1
- package/dist/lib/components/DevPortal.d.ts +2 -2
- package/dist/lib/components/DevPortal.js +5 -1
- package/dist/lib/components/DevPortal.js.map +1 -1
- package/dist/lib/components/Layout.js +1 -4
- package/dist/lib/components/Layout.js.map +1 -1
- package/dist/lib/core/DevPortalContext.d.ts +4 -7
- package/dist/lib/core/DevPortalContext.js +6 -6
- package/dist/lib/core/DevPortalContext.js.map +1 -1
- package/dist/lib/core/plugins.d.ts +1 -6
- package/dist/lib/plugins/api-key/index.js +6 -3
- package/dist/lib/plugins/api-key/index.js.map +1 -1
- package/dist/vite/config.js +2 -0
- package/dist/vite/config.js.map +1 -1
- package/dist/vite/plugin-auth.js +1 -1
- package/dist/vite/plugin-auth.js.map +1 -1
- package/lib/DevPortalProvider-yBHPOS9_.js +4559 -0
- package/lib/Spinner-Daa7xsri.js +7329 -0
- package/lib/loglevel-D-4S8up4.js +152 -0
- package/lib/zudoku.auth-auth0.js +829 -0
- package/lib/zudoku.auth-clerk.js +21 -12
- package/lib/zudoku.components.js +180 -174
- package/lib/zudoku.plugins.js +5702 -5846
- package/package.json +4 -1
- package/src/lib/authentication/Callback.tsx +31 -0
- package/src/lib/authentication/auth0.tsx +18 -0
- package/src/lib/authentication/authentication.ts +7 -14
- package/src/lib/authentication/{clerk.ts → clerk.tsx} +17 -9
- package/src/lib/authentication/openid.tsx +206 -0
- package/src/lib/components/DevPortal.tsx +10 -3
- package/src/lib/components/Layout.tsx +1 -5
- package/src/lib/core/DevPortalContext.ts +10 -13
- package/src/lib/core/plugins.ts +4 -4
- package/src/lib/plugins/api-key/index.tsx +9 -3
- package/dist/lib/core/types/combine.d.ts +0 -4
- package/dist/lib/core/types/combine.js +0 -2
- package/dist/lib/core/types/combine.js.map +0 -1
- package/lib/Spinner-9_-7nYgL.js +0 -11855
- package/src/lib/authentication/openid.ts +0 -194
- package/src/lib/core/types/combine.ts +0 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zudoku",
|
|
3
|
-
"version": "0.1.1-dev.
|
|
3
|
+
"version": "0.1.1-dev.51",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
"./auth/clerk": {
|
|
22
22
|
"import": "./lib/zudoku.auth-clerk.js"
|
|
23
23
|
},
|
|
24
|
+
"./auth/auth0": {
|
|
25
|
+
"import": "./lib/zudoku.auth-auth0.js"
|
|
26
|
+
},
|
|
24
27
|
"./plugins": {
|
|
25
28
|
"import": "./lib/zudoku.plugins.js"
|
|
26
29
|
},
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { Navigate } from "react-router-dom";
|
|
3
|
+
import { useDevPortal } from "../components/context/DevPortalProvider.js";
|
|
4
|
+
import {
|
|
5
|
+
DevPortalContext,
|
|
6
|
+
SessionProfileState,
|
|
7
|
+
} from "../core/DevPortalContext.js";
|
|
8
|
+
|
|
9
|
+
export function Callback({
|
|
10
|
+
handleCallback,
|
|
11
|
+
}: {
|
|
12
|
+
handleCallback: (
|
|
13
|
+
url: URL,
|
|
14
|
+
context: DevPortalContext,
|
|
15
|
+
) => Promise<SessionProfileState>;
|
|
16
|
+
}) {
|
|
17
|
+
const context = useDevPortal();
|
|
18
|
+
const callback = useQuery({
|
|
19
|
+
queryFn: () => handleCallback(new URL(window.location.href), context),
|
|
20
|
+
retry: 0,
|
|
21
|
+
queryKey: ["auth-callback"],
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (callback.isPending) {
|
|
25
|
+
return <div>Loading...</div>;
|
|
26
|
+
} else if (callback.error) {
|
|
27
|
+
return <div>Error: {JSON.stringify(callback.error)}</div>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return <Navigate to="/" replace={true} />;
|
|
31
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Auth0AuthenticationConfig } from "../../config/config.js";
|
|
2
|
+
import { NavigationPlugin } from "../core/plugins.js";
|
|
3
|
+
import { AuthenticationProvider } from "./authentication.js";
|
|
4
|
+
import openIdAuth from "./openid.js";
|
|
5
|
+
|
|
6
|
+
const auth0Provider = ({
|
|
7
|
+
domain,
|
|
8
|
+
clientId,
|
|
9
|
+
audience,
|
|
10
|
+
}: Auth0AuthenticationConfig): AuthenticationProvider & NavigationPlugin =>
|
|
11
|
+
openIdAuth({
|
|
12
|
+
type: "openid",
|
|
13
|
+
issuer: `https://${domain}`,
|
|
14
|
+
clientId: clientId,
|
|
15
|
+
audience: audience,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export default auth0Provider;
|
|
@@ -1,18 +1,11 @@
|
|
|
1
|
-
import { DevPortalContext } from "
|
|
1
|
+
import { DevPortalContext } from "../../lib/core/DevPortalContext.js";
|
|
2
|
+
import { NavigationPlugin } from "../../lib/core/plugins.js";
|
|
2
3
|
|
|
3
|
-
export interface
|
|
4
|
-
initialize(context: DevPortalContext): Promise<unknown>;
|
|
4
|
+
export interface AuthenticationProvider extends NavigationPlugin {
|
|
5
|
+
initialize?(context: DevPortalContext): Promise<unknown>;
|
|
5
6
|
login(context: DevPortalContext): Promise<unknown>;
|
|
7
|
+
signRequest(request: Request, context: DevPortalContext): Promise<Request>;
|
|
8
|
+
/** @deprecated **/
|
|
6
9
|
getToken?: (context: DevPortalContext) => Promise<string | undefined>;
|
|
7
|
-
|
|
8
|
-
path: Path,
|
|
9
|
-
context: DevPortalContext,
|
|
10
|
-
) => Promise<unknown>;
|
|
11
|
-
signOut(context: DevPortalContext): void;
|
|
10
|
+
logout(context: DevPortalContext): void;
|
|
12
11
|
}
|
|
13
|
-
|
|
14
|
-
export type Path = {
|
|
15
|
-
pathname: string;
|
|
16
|
-
search: string;
|
|
17
|
-
hash: string;
|
|
18
|
-
};
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { Clerk } from "@clerk/clerk-js";
|
|
2
|
-
import {
|
|
2
|
+
import { ClerkAuthenticationConfig } from "../../config/config.js";
|
|
3
|
+
import { AuthenticationProvider } from "./authentication.js";
|
|
3
4
|
|
|
4
|
-
const clerkAuth = ({
|
|
5
|
+
const clerkAuth = ({
|
|
6
|
+
clerkPubKey,
|
|
7
|
+
}: ClerkAuthenticationConfig): AuthenticationProvider => {
|
|
5
8
|
let clerkApi: Clerk;
|
|
6
9
|
|
|
7
|
-
const
|
|
10
|
+
const ensureLoaded = (async () => {
|
|
8
11
|
const { Clerk } = await import("@clerk/clerk-js");
|
|
9
12
|
clerkApi = new Clerk(clerkPubKey);
|
|
10
13
|
|
|
@@ -13,7 +16,7 @@ const clerkAuth = ({ clerkPubKey }: { clerkPubKey: string }): AuthProvider => {
|
|
|
13
16
|
|
|
14
17
|
return {
|
|
15
18
|
initialize: async (context) => {
|
|
16
|
-
await
|
|
19
|
+
await ensureLoaded;
|
|
17
20
|
|
|
18
21
|
if (clerkApi.session) {
|
|
19
22
|
await context.setUserProfile({
|
|
@@ -29,19 +32,24 @@ const clerkAuth = ({ clerkPubKey }: { clerkPubKey: string }): AuthProvider => {
|
|
|
29
32
|
});
|
|
30
33
|
}
|
|
31
34
|
},
|
|
32
|
-
|
|
35
|
+
async signRequest(request: Request): Promise<Request> {
|
|
36
|
+
await ensureLoaded;
|
|
37
|
+
const token = await clerkApi.session?.getToken();
|
|
38
|
+
request.headers.set("Authorization", `Bearer ${token}`);
|
|
39
|
+
return request;
|
|
40
|
+
},
|
|
33
41
|
getToken: async () => {
|
|
34
|
-
await
|
|
42
|
+
await ensureLoaded;
|
|
35
43
|
const token = await clerkApi.session?.getToken();
|
|
36
44
|
return token ?? undefined;
|
|
37
45
|
},
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
clerkApi.signOut();
|
|
46
|
+
logout: async () => {
|
|
47
|
+
await clerkApi.signOut();
|
|
41
48
|
},
|
|
42
49
|
login: async () => {
|
|
43
50
|
await clerkApi.redirectToSignIn();
|
|
44
51
|
},
|
|
52
|
+
getRoutes: () => [],
|
|
45
53
|
};
|
|
46
54
|
};
|
|
47
55
|
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import logger from "loglevel";
|
|
2
|
+
import * as oauth from "oauth4webapi";
|
|
3
|
+
import { OpenIDAuthenticationConfig } from "../../config/config.js";
|
|
4
|
+
import { DevPortalContext } from "../core/DevPortalContext.js";
|
|
5
|
+
import { NavigationPlugin } from "../core/plugins.js";
|
|
6
|
+
import { AuthenticationProvider } from "./authentication.js";
|
|
7
|
+
import { Callback } from "./Callback.js";
|
|
8
|
+
|
|
9
|
+
const CALLBACK_URL_PATH = "/oauth/callback";
|
|
10
|
+
|
|
11
|
+
async function getAuthServerByIssuer(issuer: URL | string) {
|
|
12
|
+
const issuerUrl = typeof issuer === "string" ? new URL(issuer) : issuer;
|
|
13
|
+
const response = await oauth.discoveryRequest(issuerUrl);
|
|
14
|
+
const server = await oauth.processDiscoveryResponse(issuerUrl, response);
|
|
15
|
+
return server;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function getAuthServer({
|
|
19
|
+
issuer,
|
|
20
|
+
authorizationEndpoint,
|
|
21
|
+
tokenEndpoint,
|
|
22
|
+
}: Pick<
|
|
23
|
+
OpenIDAuthenticationConfig,
|
|
24
|
+
"issuer" | "authorizationEndpoint" | "tokenEndpoint"
|
|
25
|
+
>) {
|
|
26
|
+
return await getAuthServerByIssuer(issuer);
|
|
27
|
+
// : ({
|
|
28
|
+
// issuer: new URL(authorizationEndpoint!).origin,
|
|
29
|
+
// authorization_endpoint: authorizationEndpoint,
|
|
30
|
+
// token_endpoint: tokenEndpoint,
|
|
31
|
+
// code_challenge_methods_supported: [],
|
|
32
|
+
// } satisfies oauth.AuthorizationServer);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const openIdAuth = ({
|
|
36
|
+
issuer,
|
|
37
|
+
authorizationEndpoint,
|
|
38
|
+
tokenEndpoint,
|
|
39
|
+
clientId,
|
|
40
|
+
}: OpenIDAuthenticationConfig): AuthenticationProvider & NavigationPlugin => {
|
|
41
|
+
const client: oauth.Client = {
|
|
42
|
+
client_id: clientId,
|
|
43
|
+
token_endpoint_auth_method: "none",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
async function handleCallback(url: URL, context: DevPortalContext) {
|
|
47
|
+
const searchParams = url.searchParams;
|
|
48
|
+
const state = searchParams.get("state");
|
|
49
|
+
|
|
50
|
+
// one eternity later, the user lands back on the redirect_uri
|
|
51
|
+
// Authorization Code Grant Request & Response
|
|
52
|
+
const codeVerifier = await context.sessionStorage.get("codeVerifier");
|
|
53
|
+
|
|
54
|
+
if (!codeVerifier) {
|
|
55
|
+
return {
|
|
56
|
+
isLoggedIn: false,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const authServer = await getAuthServer({
|
|
61
|
+
issuer: issuer,
|
|
62
|
+
authorizationEndpoint: authorizationEndpoint,
|
|
63
|
+
tokenEndpoint: tokenEndpoint,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const params = oauth.validateAuthResponse(
|
|
67
|
+
authServer,
|
|
68
|
+
client,
|
|
69
|
+
searchParams,
|
|
70
|
+
state ?? undefined,
|
|
71
|
+
);
|
|
72
|
+
if (oauth.isOAuth2Error(params)) {
|
|
73
|
+
logger.error("Error Response", params);
|
|
74
|
+
throw new Error(); // Handle OAuth 2.0 redirect error
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const redirectUrl = new URL(url);
|
|
78
|
+
redirectUrl.pathname = CALLBACK_URL_PATH;
|
|
79
|
+
redirectUrl.search = "";
|
|
80
|
+
|
|
81
|
+
const response = await oauth.authorizationCodeGrantRequest(
|
|
82
|
+
authServer,
|
|
83
|
+
client,
|
|
84
|
+
params,
|
|
85
|
+
redirectUrl.toString(),
|
|
86
|
+
codeVerifier,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// @todo do we need to do these
|
|
90
|
+
// const challenges = oauth.parseWwwAuthenticateChallenges(response);
|
|
91
|
+
// if (challenges) {
|
|
92
|
+
// for (const challenge of challenges) {
|
|
93
|
+
// console.error("WWW-Authenticate Challenge", challenge);
|
|
94
|
+
// }
|
|
95
|
+
// throw new Error(); // Handle WWW-Authenticate Challenges as needed
|
|
96
|
+
// }
|
|
97
|
+
const oauthResult = await oauth.processAuthorizationCodeOAuth2Response(
|
|
98
|
+
authServer,
|
|
99
|
+
client,
|
|
100
|
+
response,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
if (oauth.isOAuth2Error(oauthResult)) {
|
|
104
|
+
logger.error("Error Response", oauthResult);
|
|
105
|
+
throw new Error(oauthResult.error);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const userInfoResponse = await oauth.userInfoRequest(
|
|
109
|
+
authServer,
|
|
110
|
+
client,
|
|
111
|
+
oauthResult.access_token,
|
|
112
|
+
);
|
|
113
|
+
const userInfo = await userInfoResponse.json();
|
|
114
|
+
|
|
115
|
+
const profile = {
|
|
116
|
+
sub: userInfo.sub,
|
|
117
|
+
email: userInfo.email,
|
|
118
|
+
name: userInfo.name,
|
|
119
|
+
email_verified: userInfo.email_verified ?? false,
|
|
120
|
+
picture: userInfo.picture,
|
|
121
|
+
isLoggedIn: true,
|
|
122
|
+
};
|
|
123
|
+
void context.setUserProfile(profile);
|
|
124
|
+
|
|
125
|
+
return profile;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
logout: async (context) => {
|
|
130
|
+
await context.setUserProfile({ isLoggedIn: false });
|
|
131
|
+
},
|
|
132
|
+
login: async (context) => {
|
|
133
|
+
const code_challenge_method = "S256";
|
|
134
|
+
const authorizationServer = await getAuthServer({
|
|
135
|
+
issuer: issuer,
|
|
136
|
+
authorizationEndpoint,
|
|
137
|
+
tokenEndpoint,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (!authorizationServer.authorization_endpoint) {
|
|
141
|
+
throw new Error("No authorization endpoint");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* The following MUST be generated for every redirect to the authorization_endpoint. You must store
|
|
146
|
+
* the codeVerifier and nonce in the end-user session such that it can be recovered as the user
|
|
147
|
+
* gets redirected from the authorization server back to your application.
|
|
148
|
+
*/
|
|
149
|
+
const codeVerifier = oauth.generateRandomCodeVerifier();
|
|
150
|
+
const codeChallenge =
|
|
151
|
+
await oauth.calculatePKCECodeChallenge(codeVerifier);
|
|
152
|
+
|
|
153
|
+
await context.sessionStorage.set("codeVerifier", codeVerifier);
|
|
154
|
+
|
|
155
|
+
// redirect user to as.authorization_endpoint
|
|
156
|
+
const authorizationUrl = new URL(
|
|
157
|
+
authorizationServer.authorization_endpoint,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const redirectUrl = new URL(context.url);
|
|
161
|
+
redirectUrl.pathname = CALLBACK_URL_PATH;
|
|
162
|
+
redirectUrl.search = "";
|
|
163
|
+
|
|
164
|
+
authorizationUrl.searchParams.set("client_id", client.client_id);
|
|
165
|
+
authorizationUrl.searchParams.set("redirect_uri", redirectUrl.toString());
|
|
166
|
+
authorizationUrl.searchParams.set("response_type", "code");
|
|
167
|
+
authorizationUrl.searchParams.set("scope", "openid+profile+email");
|
|
168
|
+
authorizationUrl.searchParams.set("code_challenge", codeChallenge);
|
|
169
|
+
authorizationUrl.searchParams.set(
|
|
170
|
+
"code_challenge_method",
|
|
171
|
+
code_challenge_method,
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* We cannot be sure the AS supports PKCE so we're going to use state too. Use of PKCE is
|
|
176
|
+
* backwards compatible even if the AS doesn't support it which is why we're using it regardless.
|
|
177
|
+
*/
|
|
178
|
+
if (
|
|
179
|
+
authorizationServer.code_challenge_methods_supported?.includes(
|
|
180
|
+
"S256",
|
|
181
|
+
) !== true
|
|
182
|
+
) {
|
|
183
|
+
const state = oauth.generateRandomState();
|
|
184
|
+
authorizationUrl.searchParams.set("state", state);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// now redirect the user to authorizationUrl.href
|
|
188
|
+
location.href = authorizationUrl.href;
|
|
189
|
+
},
|
|
190
|
+
signRequest(request: Request, context: DevPortalContext): Promise<Request> {
|
|
191
|
+
// check if token is expired
|
|
192
|
+
|
|
193
|
+
return Promise.resolve(request);
|
|
194
|
+
},
|
|
195
|
+
getRoutes: () => {
|
|
196
|
+
return [
|
|
197
|
+
{
|
|
198
|
+
path: CALLBACK_URL_PATH,
|
|
199
|
+
element: <Callback handleCallback={handleCallback} />,
|
|
200
|
+
},
|
|
201
|
+
];
|
|
202
|
+
},
|
|
203
|
+
} satisfies AuthenticationProvider & NavigationPlugin;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export default openIdAuth;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
/* eslint-disable react/destructuring-assignment */
|
|
1
2
|
import { MDXProvider } from "@mdx-js/react";
|
|
2
3
|
import { QueryClientProvider } from "@tanstack/react-query";
|
|
3
4
|
import { memo, Suspense, useEffect, useMemo } from "react";
|
|
4
|
-
import { type AuthProvider } from "../authentication/authentication.js";
|
|
5
5
|
import {
|
|
6
6
|
DevPortalContext,
|
|
7
7
|
queryClient,
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
} from "../core/DevPortalContext.js";
|
|
10
10
|
import { HelmetProvider } from "../core/helmet.js";
|
|
11
11
|
import { type DevPortalPlugin } from "../core/plugins.js";
|
|
12
|
+
|
|
13
|
+
import { AuthenticationProvider } from "../authentication/authentication.js";
|
|
12
14
|
import {
|
|
13
15
|
MdxComponents,
|
|
14
16
|
type MdxComponentsType,
|
|
@@ -39,7 +41,7 @@ export type DevPortalProps = {
|
|
|
39
41
|
logo: string;
|
|
40
42
|
favicon: string;
|
|
41
43
|
}>;
|
|
42
|
-
authentication?:
|
|
44
|
+
authentication?: AuthenticationProvider;
|
|
43
45
|
navigation: NavigationItem[];
|
|
44
46
|
plugins?: DevPortalPlugin[];
|
|
45
47
|
mdxComponents?: MdxComponentsType;
|
|
@@ -78,7 +80,12 @@ const DevPortalInner = (props: DevPortalProps) => {
|
|
|
78
80
|
</div>
|
|
79
81
|
}
|
|
80
82
|
>
|
|
81
|
-
<Router
|
|
83
|
+
<Router
|
|
84
|
+
plugins={[
|
|
85
|
+
...(props.plugins ?? []),
|
|
86
|
+
...(props.authentication ? [props.authentication] : []),
|
|
87
|
+
]}
|
|
88
|
+
/>
|
|
82
89
|
</Suspense>
|
|
83
90
|
</ViewportAnchorProvider>
|
|
84
91
|
</ComponentsProvider>
|
|
@@ -12,11 +12,7 @@ import { Spinner } from "./Spinner.js";
|
|
|
12
12
|
export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
13
13
|
const location = useLocation();
|
|
14
14
|
const { setActiveAnchor } = useViewportAnchor();
|
|
15
|
-
const { meta
|
|
16
|
-
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
void handleAuthenticationResponse(location);
|
|
19
|
-
}, [handleAuthenticationResponse, location]);
|
|
15
|
+
const { meta } = useDevPortal();
|
|
20
16
|
|
|
21
17
|
useScrollToAnchor();
|
|
22
18
|
useScrollToTop();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { QueryClient } from "@tanstack/react-query";
|
|
2
2
|
import { type ReactNode } from "react";
|
|
3
3
|
import { create } from "zustand";
|
|
4
|
-
import { type
|
|
4
|
+
import { type AuthenticationProvider } from "../authentication/authentication.js";
|
|
5
5
|
import {
|
|
6
6
|
type DevPortalPath,
|
|
7
7
|
type DevPortalProps,
|
|
@@ -62,6 +62,7 @@ export type SessionProfileState = {
|
|
|
62
62
|
name?: string;
|
|
63
63
|
email_verified?: string;
|
|
64
64
|
picture?: string;
|
|
65
|
+
[key: string]: string | boolean | undefined;
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
export type RoutingState = {
|
|
@@ -85,9 +86,13 @@ export class DevPortalContext {
|
|
|
85
86
|
|
|
86
87
|
public navigation: NavigationItem[];
|
|
87
88
|
public meta: DevPortalProps["meta"];
|
|
88
|
-
public authentication?:
|
|
89
|
+
public authentication?: AuthenticationProvider;
|
|
89
90
|
public state: typeof useDevPortalState;
|
|
90
91
|
|
|
92
|
+
public get url(): URL {
|
|
93
|
+
return new URL(window.location.href);
|
|
94
|
+
}
|
|
95
|
+
|
|
91
96
|
constructor(private config: DevPortalProps) {
|
|
92
97
|
this.plugins = config.plugins ?? [];
|
|
93
98
|
this.navigation = config.navigation;
|
|
@@ -101,7 +106,7 @@ export class DevPortalContext {
|
|
|
101
106
|
this.plugins
|
|
102
107
|
.filter(needsInitialization)
|
|
103
108
|
.forEach((plugin) => plugin.initialize(this));
|
|
104
|
-
this.authentication?.initialize(this);
|
|
109
|
+
await this.authentication?.initialize?.(this);
|
|
105
110
|
};
|
|
106
111
|
|
|
107
112
|
setUserProfile = async (profile: Partial<SessionProfileState>) => {
|
|
@@ -109,7 +114,7 @@ export class DevPortalContext {
|
|
|
109
114
|
};
|
|
110
115
|
|
|
111
116
|
invalidateCache = async (key: DevPortalCacheKey[]) => {
|
|
112
|
-
queryClient.invalidateQueries({ queryKey: key });
|
|
117
|
+
await queryClient.invalidateQueries({ queryKey: key });
|
|
113
118
|
};
|
|
114
119
|
|
|
115
120
|
getApiIdentities = async () => {
|
|
@@ -135,15 +140,7 @@ export class DevPortalContext {
|
|
|
135
140
|
throw new Error("No authentication configured");
|
|
136
141
|
}
|
|
137
142
|
|
|
138
|
-
return this.authentication.
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
handleAuthenticationResponse = async (path: {
|
|
142
|
-
pathname: string;
|
|
143
|
-
search: string;
|
|
144
|
-
hash: string;
|
|
145
|
-
}) => {
|
|
146
|
-
this.config.authentication?.handleAuthenticationResponse?.(path, this);
|
|
143
|
+
return this.authentication.logout(this);
|
|
147
144
|
};
|
|
148
145
|
|
|
149
146
|
getNavigation = async (path: string) => {
|
package/src/lib/core/plugins.ts
CHANGED
|
@@ -4,15 +4,15 @@ import {
|
|
|
4
4
|
type ApiIdentity,
|
|
5
5
|
type NavigationCategory,
|
|
6
6
|
} from "./DevPortalContext.js";
|
|
7
|
-
import { type Combine } from "./types/combine.js";
|
|
8
7
|
|
|
9
8
|
export type PluginNavigationCategory = {
|
|
10
9
|
path: string;
|
|
11
10
|
} & NavigationCategory;
|
|
12
11
|
|
|
13
|
-
export type DevPortalPlugin =
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
export type DevPortalPlugin =
|
|
13
|
+
| NavigationPlugin
|
|
14
|
+
| ApiIdentityPlugin
|
|
15
|
+
| InitializationPlugin;
|
|
16
16
|
|
|
17
17
|
export interface NavigationPlugin {
|
|
18
18
|
getRoutes: () => RouteObject[];
|
|
@@ -61,14 +61,16 @@ const createDefaultHandler = (endpoint: string): ApiKeyService => {
|
|
|
61
61
|
throw new Error("Not authenticated");
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
const request = new Request(endpoint + `/v1/developer/api-keys`, {
|
|
65
65
|
method: "POST",
|
|
66
66
|
headers: {
|
|
67
|
-
Authorization: `Bearer ${accessToken}`,
|
|
68
67
|
"Content-Type": "application/json",
|
|
69
68
|
},
|
|
70
69
|
body: JSON.stringify(apiKey),
|
|
71
70
|
});
|
|
71
|
+
|
|
72
|
+
await context.authentication?.signRequest(request, context);
|
|
73
|
+
await fetch(request);
|
|
72
74
|
},
|
|
73
75
|
getKeys: async (context) => {
|
|
74
76
|
const accessToken = await context.authentication?.getToken?.(context);
|
|
@@ -77,12 +79,16 @@ const createDefaultHandler = (endpoint: string): ApiKeyService => {
|
|
|
77
79
|
return [];
|
|
78
80
|
}
|
|
79
81
|
|
|
80
|
-
const
|
|
82
|
+
const request = new Request(endpoint + `/v1/developer/api-keys`, {
|
|
81
83
|
headers: {
|
|
82
84
|
Authorization: `Bearer ${accessToken}`,
|
|
83
85
|
},
|
|
84
86
|
});
|
|
85
87
|
|
|
88
|
+
await context.authentication?.signRequest(request, context);
|
|
89
|
+
|
|
90
|
+
const keys = await fetch(request);
|
|
91
|
+
|
|
86
92
|
return await keys.json();
|
|
87
93
|
},
|
|
88
94
|
};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
2
|
-
type AnyCombination<T, U = T> = T extends any ? U extends any ? T | (T & U) : never : never;
|
|
3
|
-
export type Combine<T extends any[]> = UnionToIntersection<T[number]> | AnyCombination<T[number]>;
|
|
4
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"combine.js","sourceRoot":"","sources":["../../../../src/lib/core/types/combine.ts"],"names":[],"mappings":""}
|