zudoku 0.46.3 → 0.47.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/dist/config/config.d.ts +11 -0
- package/dist/config/validators/InputSidebarSchema.d.ts +1 -1
- package/dist/config/validators/validate.d.ts +47 -47
- package/dist/config/validators/validate.js +11 -1
- package/dist/config/validators/validate.js.map +1 -1
- package/dist/config/validators/validate.test.d.ts +1 -0
- package/dist/config/validators/validate.test.js +80 -0
- package/dist/config/validators/validate.test.js.map +1 -0
- package/dist/lib/auth/issuer.d.ts +2 -0
- package/dist/lib/auth/issuer.js +34 -0
- package/dist/lib/auth/issuer.js.map +1 -0
- package/dist/lib/auth/issuer.test.d.ts +1 -0
- package/dist/lib/auth/issuer.test.js +81 -0
- package/dist/lib/auth/issuer.test.js.map +1 -0
- package/dist/lib/authentication/hook.d.ts +6 -0
- package/dist/lib/authentication/providers/auth0.js +1 -1
- package/dist/lib/authentication/providers/auth0.js.map +1 -1
- package/dist/lib/authentication/providers/azureb2c.d.ts +28 -0
- package/dist/lib/authentication/providers/azureb2c.js +145 -0
- package/dist/lib/authentication/providers/azureb2c.js.map +1 -0
- package/dist/lib/authentication/providers/clerk.js +3 -12
- package/dist/lib/authentication/providers/clerk.js.map +1 -1
- package/dist/lib/authentication/providers/openid.d.ts +1 -0
- package/dist/lib/authentication/providers/openid.js +38 -0
- package/dist/lib/authentication/providers/openid.js.map +1 -1
- package/dist/lib/authentication/providers/supabase.js +2 -9
- package/dist/lib/authentication/providers/supabase.js.map +1 -1
- package/dist/lib/authentication/state.d.ts +6 -5
- package/dist/lib/authentication/state.js +19 -6
- package/dist/lib/authentication/state.js.map +1 -1
- package/dist/lib/components/context/ZudokuProvider.d.ts +1 -1
- package/dist/lib/components/index.d.ts +6 -0
- package/dist/lib/hooks/index.d.ts +6 -0
- package/dist/lib/plugins/openapi/PlaygroundDialogWrapper.d.ts +1 -1
- package/dist/lib/plugins/openapi/playground/Headers.d.ts +2 -2
- package/dist/lib/plugins/openapi/playground/fileUtils.js +1 -1
- package/dist/lib/plugins/openapi/playground/fileUtils.js.map +1 -1
- package/dist/lib/util/MdxComponents.js +1 -1
- package/dist/lib/util/MdxComponents.js.map +1 -1
- package/dist/vite/build.js +1 -28
- package/dist/vite/build.js.map +1 -1
- package/lib/{Markdown-D81l28Ib.js → Markdown-BUE2ViaD.js} +37 -37
- package/lib/{Markdown-D81l28Ib.js.map → Markdown-BUE2ViaD.js.map} +1 -1
- package/lib/{MdxPage-S_CxlNmX.js → MdxPage-By4UkRkI.js} +4 -4
- package/lib/{MdxPage-S_CxlNmX.js.map → MdxPage-By4UkRkI.js.map} +1 -1
- package/lib/{OasProvider-D5rt6WMb.js → OasProvider-C6_Kx5O7.js} +2 -2
- package/lib/{OasProvider-D5rt6WMb.js.map → OasProvider-C6_Kx5O7.js.map} +1 -1
- package/lib/{OperationList-CNhg654C.js → OperationList-BISd29LY.js} +6 -6
- package/lib/OperationList-BISd29LY.js.map +1 -0
- package/lib/{RouteGuard-CZ_uLv3g.js → RouteGuard-CgmsSw7T.js} +2 -2
- package/lib/{RouteGuard-CZ_uLv3g.js.map → RouteGuard-CgmsSw7T.js.map} +1 -1
- package/lib/{SchemaList-BvzCrTYg.js → SchemaList-BqnRo5ov.js} +6 -6
- package/lib/{SchemaList-BvzCrTYg.js.map → SchemaList-BqnRo5ov.js.map} +1 -1
- package/lib/{SchemaView-Br1RupCp.js → SchemaView-CtYJpxQI.js} +3 -3
- package/lib/{SchemaView-Br1RupCp.js.map → SchemaView-CtYJpxQI.js.map} +1 -1
- package/lib/{SignUp-B-1Pvc-8.js → SignUp-CrjeBbqN.js} +2 -2
- package/lib/{SignUp-B-1Pvc-8.js.map → SignUp-CrjeBbqN.js.map} +1 -1
- package/lib/{Slot-T8NJUkm4.js → Slot-DANV2b7_.js} +4 -4
- package/lib/{Slot-T8NJUkm4.js.map → Slot-DANV2b7_.js.map} +1 -1
- package/lib/{SyntaxHighlight-Cz6Me7-F.js → SyntaxHighlight-DtvR7RLF.js} +1291 -1265
- package/lib/SyntaxHighlight-DtvR7RLF.js.map +1 -0
- package/lib/{Toc-PA-j0gEu.js → Toc-ClJBmdtI.js} +2 -2
- package/lib/{Toc-PA-j0gEu.js.map → Toc-ClJBmdtI.js.map} +1 -1
- package/lib/{circular-5FeDWJOn.js → circular-pOdgLzpG.js} +2 -2
- package/lib/{circular-5FeDWJOn.js.map → circular-pOdgLzpG.js.map} +1 -1
- package/lib/clerk-yAKDC3Qz.js +24812 -0
- package/lib/clerk-yAKDC3Qz.js.map +1 -0
- package/lib/{createServer-BC2RZgmW.js → createServer-qAtUf99r.js} +2625 -2615
- package/lib/createServer-qAtUf99r.js.map +1 -0
- package/lib/errors-Bpodza84.js +78 -0
- package/lib/errors-Bpodza84.js.map +1 -0
- package/lib/{hook-k7PfUIsj.js → hook-wIlTGE-2.js} +52 -31
- package/lib/{hook-k7PfUIsj.js.map → hook-wIlTGE-2.js.map} +1 -1
- package/lib/{index-CJZthJSj.js → index-RFzRn4fM.js} +10 -10
- package/lib/index-RFzRn4fM.js.map +1 -0
- package/lib/{mutation-BSeQ8pEK.js → mutation-C-kdA_1r.js} +2 -2
- package/lib/{mutation-BSeQ8pEK.js.map → mutation-C-kdA_1r.js.map} +1 -1
- package/lib/ui/SyntaxHighlight.js +2 -2
- package/lib/{useMutation-CZSmsIGW.js → useMutation-3Ph3x6En.js} +3 -3
- package/lib/{useMutation-CZSmsIGW.js.map → useMutation-3Ph3x6En.js.map} +1 -1
- package/lib/zudoku.auth-auth0.js +2 -2
- package/lib/zudoku.auth-auth0.js.map +1 -1
- package/lib/zudoku.auth-azureb2c.js +9974 -0
- package/lib/zudoku.auth-azureb2c.js.map +1 -0
- package/lib/zudoku.auth-clerk.js +39 -48
- package/lib/zudoku.auth-clerk.js.map +1 -1
- package/lib/zudoku.auth-openid.js +291 -316
- package/lib/zudoku.auth-openid.js.map +1 -1
- package/lib/zudoku.components.js +1222 -1594
- package/lib/zudoku.components.js.map +1 -1
- package/lib/zudoku.hooks.js +2 -2
- package/lib/zudoku.plugin-api-catalog.js +2 -2
- package/lib/zudoku.plugin-api-keys.js +3 -3
- package/lib/zudoku.plugin-api-keys.js.map +1 -1
- package/lib/zudoku.plugin-custom-pages.js +1 -1
- package/lib/zudoku.plugin-markdown.js +1 -1
- package/lib/zudoku.plugin-openapi.js +2 -2
- package/lib/zudoku.plugin-search-pagefind.js +2 -2
- package/package.json +37 -19
- package/src/lib/auth/issuer.test.ts +104 -0
- package/src/lib/auth/issuer.ts +38 -0
- package/src/lib/authentication/providers/auth0.tsx +1 -1
- package/src/lib/authentication/providers/azureb2c.tsx +196 -0
- package/src/lib/authentication/providers/clerk.tsx +3 -12
- package/src/lib/authentication/providers/openid.tsx +53 -0
- package/src/lib/authentication/providers/supabase.tsx +2 -9
- package/src/lib/authentication/state.ts +37 -7
- package/src/lib/components/context/ZudokuProvider.tsx +1 -1
- package/src/lib/plugins/api-keys/SettingsApiKeys.tsx +1 -1
- package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +1 -1
- package/src/lib/plugins/openapi/playground/Headers.tsx +2 -2
- package/src/lib/plugins/openapi/playground/fileUtils.ts +1 -1
- package/src/lib/util/MdxComponents.tsx +1 -1
- package/lib/OperationList-CNhg654C.js.map +0 -1
- package/lib/SyntaxHighlight-Cz6Me7-F.js.map +0 -1
- package/lib/createServer-BC2RZgmW.js.map +0 -1
- package/lib/index-CJZthJSj.js.map +0 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { ZudokuConfig } from "../../config/validators/validate.js";
|
|
3
|
+
import { getIssuer } from "./issuer.js";
|
|
4
|
+
|
|
5
|
+
describe("getIssuer", () => {
|
|
6
|
+
it("should return clerk frontend API for clerk authentication", async () => {
|
|
7
|
+
// Using a valid base64 encoded string: "example.example$test" -> "ZXhhbXBsZS5leGFtcGxlJHRlc3Q="
|
|
8
|
+
const config: ZudokuConfig = {
|
|
9
|
+
authentication: {
|
|
10
|
+
type: "clerk",
|
|
11
|
+
clerkPubKey:
|
|
12
|
+
"pk_test_dG9sZXJhbnQtaG9ybmV0LTQ2LmNsZXJrLmFjY291bnRzLmRldiQ",
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const result = await getIssuer(config);
|
|
17
|
+
expect(result).toBe("tolerant-hornet-46.clerk.accounts.dev");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should throw error for invalid clerk public key format", async () => {
|
|
21
|
+
const config: ZudokuConfig = {
|
|
22
|
+
authentication: {
|
|
23
|
+
type: "clerk",
|
|
24
|
+
clerkPubKey: "pk_test_invalid",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
await expect(getIssuer(config)).rejects.toThrow(
|
|
29
|
+
"Clerk public key is invalid",
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should throw error for clerk key with invalid base64", async () => {
|
|
34
|
+
const config: ZudokuConfig = {
|
|
35
|
+
authentication: {
|
|
36
|
+
type: "clerk",
|
|
37
|
+
clerkPubKey: "pk_test_invalid_base64",
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
await expect(getIssuer(config)).rejects.toThrow(
|
|
42
|
+
"Clerk public key is invalid",
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should return auth0 issuer URL for auth0 authentication", async () => {
|
|
47
|
+
const config: ZudokuConfig = {
|
|
48
|
+
authentication: {
|
|
49
|
+
type: "auth0",
|
|
50
|
+
domain: "example.auth0.com",
|
|
51
|
+
clientId: "test-client-id",
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const result = await getIssuer(config);
|
|
56
|
+
expect(result).toBe("https://example.auth0.com/");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should return openid issuer for openid authentication", async () => {
|
|
60
|
+
const config: ZudokuConfig = {
|
|
61
|
+
authentication: {
|
|
62
|
+
type: "openid",
|
|
63
|
+
issuer: "https://example.com/auth",
|
|
64
|
+
clientId: "test-client-id",
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const result = await getIssuer(config);
|
|
69
|
+
expect(result).toBe("https://example.com/auth");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should return supabase URL for supabase authentication", async () => {
|
|
73
|
+
const config: ZudokuConfig = {
|
|
74
|
+
authentication: {
|
|
75
|
+
type: "supabase",
|
|
76
|
+
supabaseUrl: "https://project.supabase.co",
|
|
77
|
+
supabaseKey: "test-anon-key",
|
|
78
|
+
provider: "github",
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const result = await getIssuer(config);
|
|
83
|
+
expect(result).toBe("https://project.supabase.co");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("should return undefined for no authentication", async () => {
|
|
87
|
+
const config: ZudokuConfig = {};
|
|
88
|
+
|
|
89
|
+
const result = await getIssuer(config);
|
|
90
|
+
expect(result).toBeUndefined();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should throw error for unsupported authentication type", async () => {
|
|
94
|
+
const config = {
|
|
95
|
+
authentication: {
|
|
96
|
+
type: "unsupported" as any,
|
|
97
|
+
},
|
|
98
|
+
} as ZudokuConfig;
|
|
99
|
+
|
|
100
|
+
await expect(getIssuer(config)).rejects.toThrow(
|
|
101
|
+
"Unsupported authentication type",
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ZudokuConfig } from "../../config/validators/validate.js";
|
|
2
|
+
import invariant from "../util/invariant.js";
|
|
3
|
+
|
|
4
|
+
export const getIssuer = async (config: ZudokuConfig) => {
|
|
5
|
+
switch (config.authentication?.type) {
|
|
6
|
+
case "clerk": {
|
|
7
|
+
const frontendApiEncoded = config.authentication.clerkPubKey
|
|
8
|
+
.split("_")
|
|
9
|
+
.at(-1);
|
|
10
|
+
invariant(frontendApiEncoded, "Clerk public key is invalid");
|
|
11
|
+
const frontendApiParts = atob(frontendApiEncoded).split("$");
|
|
12
|
+
|
|
13
|
+
if (frontendApiParts.length !== 2) {
|
|
14
|
+
throw new Error("Clerk public key is invalid");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const frontendApi = frontendApiParts.at(0);
|
|
18
|
+
invariant(frontendApi, "Clerk public key is invalid");
|
|
19
|
+
|
|
20
|
+
return frontendApi;
|
|
21
|
+
}
|
|
22
|
+
case "auth0": {
|
|
23
|
+
return `https://${config.authentication.domain}/`;
|
|
24
|
+
}
|
|
25
|
+
case "openid": {
|
|
26
|
+
return config.authentication.issuer;
|
|
27
|
+
}
|
|
28
|
+
case "supabase": {
|
|
29
|
+
return config.authentication.supabaseUrl;
|
|
30
|
+
}
|
|
31
|
+
case undefined: {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
default: {
|
|
35
|
+
throw new Error(`Unsupported authentication type`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import type { AuthenticationResult, EventMessage } from "@azure/msal-browser";
|
|
2
|
+
import { EventType, PublicClientApplication } from "@azure/msal-browser";
|
|
3
|
+
import { type AzureB2CAuthenticationConfig } from "../../../config/config.js";
|
|
4
|
+
import { ClientOnly } from "../../components/ClientOnly.js";
|
|
5
|
+
import { joinUrl } from "../../util/joinUrl.js";
|
|
6
|
+
import {
|
|
7
|
+
type AuthenticationPlugin,
|
|
8
|
+
type AuthenticationProviderInitializer,
|
|
9
|
+
} from "../authentication.js";
|
|
10
|
+
import { CallbackHandler } from "../components/CallbackHandler.js";
|
|
11
|
+
import { AuthorizationError } from "../errors.js";
|
|
12
|
+
import { useAuthState } from "../state.js";
|
|
13
|
+
|
|
14
|
+
import { CoreAuthenticationPlugin } from "../AuthenticationPlugin.js";
|
|
15
|
+
|
|
16
|
+
const AZUREB2C_CALLBACK_PATH = "/oauth/callback";
|
|
17
|
+
|
|
18
|
+
export class AzureB2CAuthPlugin
|
|
19
|
+
extends CoreAuthenticationPlugin
|
|
20
|
+
implements AuthenticationPlugin
|
|
21
|
+
{
|
|
22
|
+
private msalInstance: PublicClientApplication;
|
|
23
|
+
private readonly scopes: string[];
|
|
24
|
+
private readonly redirectToAfterSignUp?: string;
|
|
25
|
+
private readonly redirectToAfterSignIn?: string;
|
|
26
|
+
private readonly redirectToAfterSignOut: string;
|
|
27
|
+
|
|
28
|
+
constructor({
|
|
29
|
+
clientId,
|
|
30
|
+
tenantName,
|
|
31
|
+
policyName,
|
|
32
|
+
scopes,
|
|
33
|
+
redirectToAfterSignUp,
|
|
34
|
+
redirectToAfterSignIn,
|
|
35
|
+
redirectToAfterSignOut = "/",
|
|
36
|
+
basePath = "",
|
|
37
|
+
}: AzureB2CAuthenticationConfig) {
|
|
38
|
+
super();
|
|
39
|
+
this.scopes = scopes ?? ["openid", "profile", "email"];
|
|
40
|
+
this.redirectToAfterSignUp = redirectToAfterSignUp;
|
|
41
|
+
this.redirectToAfterSignIn = redirectToAfterSignIn;
|
|
42
|
+
this.redirectToAfterSignOut = redirectToAfterSignOut;
|
|
43
|
+
|
|
44
|
+
const authority = `https://${tenantName}.b2clogin.com/${tenantName}.onmicrosoft.com/${policyName}`;
|
|
45
|
+
const redirectUri = joinUrl(basePath, AZUREB2C_CALLBACK_PATH);
|
|
46
|
+
|
|
47
|
+
this.msalInstance = new PublicClientApplication({
|
|
48
|
+
auth: {
|
|
49
|
+
clientId,
|
|
50
|
+
authority,
|
|
51
|
+
redirectUri,
|
|
52
|
+
knownAuthorities: [`${tenantName}.b2clogin.com`],
|
|
53
|
+
},
|
|
54
|
+
cache: {
|
|
55
|
+
cacheLocation: "sessionStorage",
|
|
56
|
+
storeAuthStateInCookie: false,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
void this.msalInstance.initialize().then(async () => {
|
|
61
|
+
void this.msalInstance
|
|
62
|
+
.handleRedirectPromise()
|
|
63
|
+
.then((response: AuthenticationResult | null) => {
|
|
64
|
+
if (response) {
|
|
65
|
+
this.handleAuthResponse(response);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Add event callback
|
|
70
|
+
void this.msalInstance.addEventCallback((event: EventMessage) => {
|
|
71
|
+
if (event.eventType === EventType.LOGIN_SUCCESS) {
|
|
72
|
+
this.handleAuthResponse(event.payload as AuthenticationResult);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private handleAuthResponse(response: AuthenticationResult) {
|
|
79
|
+
const { accessToken, idToken, scopes, account } = response;
|
|
80
|
+
|
|
81
|
+
if (!account) {
|
|
82
|
+
throw new AuthorizationError("No account information in response");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Get the user's name from Azure B2C claims
|
|
86
|
+
const name =
|
|
87
|
+
[account.idTokenClaims?.given_name, account.idTokenClaims?.family_name]
|
|
88
|
+
.filter(Boolean)
|
|
89
|
+
.join(" ") || account.username;
|
|
90
|
+
|
|
91
|
+
useAuthState.getState().setLoggedIn({
|
|
92
|
+
providerData: {
|
|
93
|
+
accessToken,
|
|
94
|
+
idToken,
|
|
95
|
+
scopes,
|
|
96
|
+
account,
|
|
97
|
+
},
|
|
98
|
+
profile: {
|
|
99
|
+
sub: account.localAccountId,
|
|
100
|
+
email: account.username,
|
|
101
|
+
name,
|
|
102
|
+
emailVerified: true, // Azure B2C emails are verified
|
|
103
|
+
pictureUrl: undefined, // Azure B2C doesn't provide profile pictures by default
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async signUp({ redirectTo }: { redirectTo?: string } = {}) {
|
|
109
|
+
const redirectUri = this.redirectToAfterSignUp ?? redirectTo ?? "/";
|
|
110
|
+
sessionStorage.setItem("redirect-to", redirectUri);
|
|
111
|
+
|
|
112
|
+
await this.msalInstance.loginRedirect({
|
|
113
|
+
scopes: this.scopes,
|
|
114
|
+
prompt: "select_account",
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async signIn({ redirectTo }: { redirectTo?: string } = {}) {
|
|
119
|
+
const redirectUri = this.redirectToAfterSignIn ?? redirectTo ?? "/";
|
|
120
|
+
sessionStorage.setItem("redirect-to", redirectUri);
|
|
121
|
+
|
|
122
|
+
await this.msalInstance.loginRedirect({
|
|
123
|
+
scopes: this.scopes,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async getAccessToken(): Promise<string> {
|
|
128
|
+
const account = this.msalInstance.getAllAccounts()[0];
|
|
129
|
+
if (!account) {
|
|
130
|
+
throw new AuthorizationError("No active account");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const response = await this.msalInstance.acquireTokenSilent({
|
|
135
|
+
scopes: this.scopes,
|
|
136
|
+
account,
|
|
137
|
+
});
|
|
138
|
+
return response.accessToken;
|
|
139
|
+
} catch {
|
|
140
|
+
// If silent token acquisition fails, try interactive
|
|
141
|
+
await this.msalInstance.acquireTokenRedirect({
|
|
142
|
+
scopes: this.scopes,
|
|
143
|
+
account,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
throw new AuthorizationError(
|
|
147
|
+
"Token acquisition failed after interactive attempt",
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
signRequest = async (request: Request): Promise<Request> => {
|
|
153
|
+
const accessToken = await this.getAccessToken();
|
|
154
|
+
request.headers.set("Authorization", `Bearer ${accessToken}`);
|
|
155
|
+
return request;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
signOut = async () => {
|
|
159
|
+
const account = this.msalInstance.getAllAccounts()[0];
|
|
160
|
+
if (account) {
|
|
161
|
+
await this.msalInstance.logoutRedirect({
|
|
162
|
+
account,
|
|
163
|
+
postLogoutRedirectUri:
|
|
164
|
+
window.location.origin + this.redirectToAfterSignOut,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
useAuthState.getState().setLoggedOut();
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
handleCallback = async () => {
|
|
172
|
+
const redirectTo = sessionStorage.getItem("redirect-to") ?? "/";
|
|
173
|
+
sessionStorage.removeItem("redirect-to");
|
|
174
|
+
return redirectTo;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
getRoutes() {
|
|
178
|
+
return [
|
|
179
|
+
...super.getRoutes(),
|
|
180
|
+
{
|
|
181
|
+
path: AZUREB2C_CALLBACK_PATH,
|
|
182
|
+
element: (
|
|
183
|
+
<ClientOnly>
|
|
184
|
+
<CallbackHandler handleCallback={this.handleCallback} />
|
|
185
|
+
</ClientOnly>
|
|
186
|
+
),
|
|
187
|
+
},
|
|
188
|
+
];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const azureB2CAuth: AuthenticationProviderInitializer<
|
|
193
|
+
AzureB2CAuthenticationConfig
|
|
194
|
+
> = (options) => new AzureB2CAuthPlugin(options);
|
|
195
|
+
|
|
196
|
+
export default azureB2CAuth;
|
|
@@ -31,9 +31,7 @@ const clerkAuth: AuthenticationProviderInitializer<
|
|
|
31
31
|
const verifiedEmail = clerkApi.user.emailAddresses.find(
|
|
32
32
|
(email) => email.verification.status === "verified",
|
|
33
33
|
);
|
|
34
|
-
useAuthState.
|
|
35
|
-
isAuthenticated: true,
|
|
36
|
-
isPending: false,
|
|
34
|
+
useAuthState.getState().setLoggedIn({
|
|
37
35
|
profile: {
|
|
38
36
|
sub: clerkApi.user.id,
|
|
39
37
|
name: clerkApi.user.fullName ?? undefined,
|
|
@@ -115,9 +113,7 @@ const clerkAuth: AuthenticationProviderInitializer<
|
|
|
115
113
|
const verifiedEmail = clerk.session.user.emailAddresses.find(
|
|
116
114
|
(email) => email.verification.status === "verified",
|
|
117
115
|
);
|
|
118
|
-
useAuthState.
|
|
119
|
-
isAuthenticated: true,
|
|
120
|
-
isPending: false,
|
|
116
|
+
useAuthState.getState().setLoggedIn({
|
|
121
117
|
profile: {
|
|
122
118
|
sub: clerk.session.user.id,
|
|
123
119
|
name: clerk.session.user.fullName ?? undefined,
|
|
@@ -146,12 +142,7 @@ const clerkAuth: AuthenticationProviderInitializer<
|
|
|
146
142
|
await clerkApi?.signOut({
|
|
147
143
|
redirectUrl: window.location.origin + redirectToAfterSignOut,
|
|
148
144
|
});
|
|
149
|
-
useAuthState.
|
|
150
|
-
isAuthenticated: false,
|
|
151
|
-
isPending: false,
|
|
152
|
-
profile: null,
|
|
153
|
-
providerData: null,
|
|
154
|
-
});
|
|
145
|
+
useAuthState.getState().setLoggedOut();
|
|
155
146
|
},
|
|
156
147
|
signIn: async ({ redirectTo }: { redirectTo?: string } = {}) => {
|
|
157
148
|
await ensureLoaded;
|
|
@@ -273,6 +273,59 @@ export class OpenIDAuthenticationProvider
|
|
|
273
273
|
}
|
|
274
274
|
};
|
|
275
275
|
|
|
276
|
+
onPageLoad = async () => {
|
|
277
|
+
const { providerData } = useAuthState.getState();
|
|
278
|
+
|
|
279
|
+
if (!providerData) {
|
|
280
|
+
useAuthState.setState({ isPending: false });
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const tokenState = providerData as OpenIdProviderData;
|
|
285
|
+
|
|
286
|
+
if (new Date(tokenState.expiresOn) < new Date()) {
|
|
287
|
+
if (!tokenState.refreshToken) {
|
|
288
|
+
useAuthState.setState({
|
|
289
|
+
isAuthenticated: false,
|
|
290
|
+
isPending: false,
|
|
291
|
+
profile: null,
|
|
292
|
+
providerData: null,
|
|
293
|
+
});
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
const as = await this.getAuthServer();
|
|
299
|
+
const request = await oauth.refreshTokenGrantRequest(
|
|
300
|
+
as,
|
|
301
|
+
this.client,
|
|
302
|
+
tokenState.refreshToken,
|
|
303
|
+
);
|
|
304
|
+
const response = await oauth.processRefreshTokenResponse(
|
|
305
|
+
as,
|
|
306
|
+
this.client,
|
|
307
|
+
request,
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
if (!response.access_token) {
|
|
311
|
+
throw new AuthorizationError("No access token in response");
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
this.setTokensFromResponse(response);
|
|
315
|
+
} catch {
|
|
316
|
+
useAuthState.setState({
|
|
317
|
+
isAuthenticated: false,
|
|
318
|
+
isPending: false,
|
|
319
|
+
profile: null,
|
|
320
|
+
providerData: null,
|
|
321
|
+
});
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
useAuthState.setState({ isPending: false });
|
|
327
|
+
};
|
|
328
|
+
|
|
276
329
|
handleCallback = async () => {
|
|
277
330
|
const url = new URL(window.location.href);
|
|
278
331
|
const state = url.searchParams.get("state");
|
|
@@ -51,12 +51,7 @@ class SupabaseAuthenticationProvider
|
|
|
51
51
|
if (session && (event === "SIGNED_IN" || event === "TOKEN_REFRESHED")) {
|
|
52
52
|
await this.updateUserState(session);
|
|
53
53
|
} else if (event === "SIGNED_OUT") {
|
|
54
|
-
useAuthState.
|
|
55
|
-
isAuthenticated: false,
|
|
56
|
-
isPending: false,
|
|
57
|
-
profile: undefined,
|
|
58
|
-
providerData: undefined,
|
|
59
|
-
});
|
|
54
|
+
useAuthState.getState().setLoggedOut();
|
|
60
55
|
}
|
|
61
56
|
});
|
|
62
57
|
}
|
|
@@ -72,9 +67,7 @@ class SupabaseAuthenticationProvider
|
|
|
72
67
|
pictureUrl: user.user_metadata.avatar_url,
|
|
73
68
|
};
|
|
74
69
|
|
|
75
|
-
useAuthState.
|
|
76
|
-
isAuthenticated: true,
|
|
77
|
-
isPending: false,
|
|
70
|
+
useAuthState.getState().setLoggedIn({
|
|
78
71
|
profile,
|
|
79
72
|
providerData: { session },
|
|
80
73
|
});
|
|
@@ -6,12 +6,15 @@ export interface AuthState<ProviderData = unknown> {
|
|
|
6
6
|
isPending: boolean;
|
|
7
7
|
profile: UserProfile | null;
|
|
8
8
|
providerData: ProviderData | null;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
setAuthenticationPending: () => void;
|
|
10
|
+
setLoggedOut: () => void;
|
|
11
|
+
setLoggedIn: ({
|
|
12
|
+
profile,
|
|
13
|
+
providerData,
|
|
14
|
+
}: {
|
|
15
|
+
profile: UserProfile;
|
|
16
|
+
providerData: unknown;
|
|
17
|
+
}) => void;
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
export type StoreWithPersist<T> = Mutate<
|
|
@@ -35,11 +38,38 @@ const withStorageDOMEvents = <T>(store: StoreWithPersist<T>) => {
|
|
|
35
38
|
|
|
36
39
|
export const useAuthState = create<AuthState>()(
|
|
37
40
|
persist(
|
|
38
|
-
(
|
|
41
|
+
(set) => ({
|
|
39
42
|
isAuthenticated: false,
|
|
40
43
|
isPending: true,
|
|
41
44
|
profile: null,
|
|
42
45
|
providerData: null,
|
|
46
|
+
setAuthenticationPending: () =>
|
|
47
|
+
set(() => ({
|
|
48
|
+
isAuthenticated: false,
|
|
49
|
+
isPending: false,
|
|
50
|
+
profile: null,
|
|
51
|
+
providerData: null,
|
|
52
|
+
})),
|
|
53
|
+
setLoggedOut: () =>
|
|
54
|
+
set(() => ({
|
|
55
|
+
isAuthenticated: false,
|
|
56
|
+
isPending: false,
|
|
57
|
+
profile: null,
|
|
58
|
+
providerData: null,
|
|
59
|
+
})),
|
|
60
|
+
setLoggedIn: ({
|
|
61
|
+
profile,
|
|
62
|
+
providerData,
|
|
63
|
+
}: {
|
|
64
|
+
profile: UserProfile;
|
|
65
|
+
providerData: unknown;
|
|
66
|
+
}) =>
|
|
67
|
+
set(() => ({
|
|
68
|
+
isAuthenticated: true,
|
|
69
|
+
isPending: false,
|
|
70
|
+
profile,
|
|
71
|
+
providerData,
|
|
72
|
+
})),
|
|
43
73
|
}),
|
|
44
74
|
{
|
|
45
75
|
merge: (persistedState, currentState) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
2
2
|
import type { PropsWithChildren } from "react";
|
|
3
|
-
import { ZudokuContext } from "../../core/ZudokuContext.js";
|
|
3
|
+
import { type ZudokuContext } from "../../core/ZudokuContext.js";
|
|
4
4
|
import { NO_DEHYDRATE } from "../cache.js";
|
|
5
5
|
import { ZudokuReactContext } from "./ZudokuContext.js";
|
|
6
6
|
|
|
@@ -32,7 +32,7 @@ import { Slot } from "../../components/Slot.js";
|
|
|
32
32
|
import { Button } from "../../ui/Button.js";
|
|
33
33
|
import { Input } from "../../ui/Input.js";
|
|
34
34
|
import { cn } from "../../util/cn.js";
|
|
35
|
-
import { ApiConsumer, type ApiKey, type ApiKeyService } from "./index.js";
|
|
35
|
+
import { type ApiConsumer, type ApiKey, type ApiKeyService } from "./index.js";
|
|
36
36
|
|
|
37
37
|
export const SettingsApiKeys = ({ service }: { service: ApiKeyService }) => {
|
|
38
38
|
const context = useZudoku();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useAuth } from "zudoku/components";
|
|
2
2
|
import type { OperationListItemResult } from "./OperationList.js";
|
|
3
3
|
import { PlaygroundDialog } from "./playground/PlaygroundDialog.js";
|
|
4
|
-
import { Content } from "./SidecarExamples.js";
|
|
4
|
+
import { type Content } from "./SidecarExamples.js";
|
|
5
5
|
|
|
6
6
|
export const PlaygroundDialogWrapper = ({
|
|
7
7
|
server,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { XIcon } from "lucide-react";
|
|
2
2
|
import { useCallback, useEffect, useRef } from "react";
|
|
3
3
|
import {
|
|
4
|
-
Control,
|
|
4
|
+
type Control,
|
|
5
5
|
Controller,
|
|
6
6
|
useFieldArray,
|
|
7
7
|
useFormContext,
|
|
@@ -12,7 +12,7 @@ import { Autocomplete } from "../../../components/Autocomplete.js";
|
|
|
12
12
|
import { Button } from "../../../ui/Button.js";
|
|
13
13
|
import { Input } from "../../../ui/Input.js";
|
|
14
14
|
import ParamsGrid, { ParamsGridItem } from "./ParamsGrid.js";
|
|
15
|
-
import { Header, type PlaygroundForm } from "./Playground.js";
|
|
15
|
+
import { type Header, type PlaygroundForm } from "./Playground.js";
|
|
16
16
|
|
|
17
17
|
const headerOptions = Object.freeze([
|
|
18
18
|
"Accept",
|
|
@@ -16,7 +16,7 @@ export const extractFileName = (
|
|
|
16
16
|
const filenameMatch = contentDisposition.match(
|
|
17
17
|
/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/,
|
|
18
18
|
);
|
|
19
|
-
if (filenameMatch
|
|
19
|
+
if (filenameMatch?.[1]) {
|
|
20
20
|
return filenameMatch[1].replace(/['"]/g, "");
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -16,7 +16,7 @@ export const MdxComponents = {
|
|
|
16
16
|
if (/\.(mp4|webm|mov|avi)$/.test(props.src ?? "")) {
|
|
17
17
|
return <video src={props.src} controls playsInline autoPlay loop />;
|
|
18
18
|
}
|
|
19
|
-
return <img {...props} className="rounded-md" />;
|
|
19
|
+
return <img {...props} className={cn("rounded-md", props.className)} />;
|
|
20
20
|
},
|
|
21
21
|
h1: ({ children, id }) => (
|
|
22
22
|
<Heading level={1} id={id}>
|