zudoku 0.82.3 → 0.82.4
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/cli/cli.js +29 -25
- package/dist/cli/worker.js +19 -0
- package/dist/declarations/config/config.d.ts +3 -0
- package/dist/declarations/config/validators/ZudokuConfig.d.ts +38 -0
- package/dist/declarations/lib/authentication/providers/entra.d.ts +13 -0
- package/dist/declarations/lib/authentication/providers/openid.d.ts +4 -1
- package/dist/declarations/lib/authentication/providers/util.d.ts +4 -0
- package/dist/declarations/lib/plugins/openapi/McpClientLogos.d.ts +4 -0
- package/dist/declarations/lib/plugins/openapi/mcp-configs.d.ts +3 -0
- package/dist/flat-config.d.ts +17 -0
- package/docs/configuration/authentication-azure-ad.md +17 -15
- package/docs/configuration/authentication.md +22 -2
- package/docs/guides/mcp-servers.md +26 -0
- package/package.json +12 -9
- package/src/config/config.ts +4 -0
- package/src/config/validators/ZudokuConfig.ts +19 -0
- package/src/lib/auth/issuer.ts +7 -1
- package/src/lib/authentication/providers/entra.tsx +97 -0
- package/src/lib/authentication/providers/openid.tsx +29 -13
- package/src/lib/authentication/providers/util.ts +7 -0
- package/src/lib/plugins/openapi/MCPEndpoint.tsx +347 -228
- package/src/lib/plugins/openapi/McpClientLogos.tsx +72 -0
- package/src/lib/plugins/openapi/mcp-configs.ts +55 -0
- package/src/zuplo/enrich-with-zuplo-mcp.ts +14 -2
package/dist/cli/cli.js
CHANGED
|
@@ -134,7 +134,6 @@ var init_llms = __esm({
|
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
// src/cli/cli.ts
|
|
137
|
-
import * as Sentry2 from "@sentry/node";
|
|
138
137
|
import { hideBin } from "yargs/helpers";
|
|
139
138
|
import yargs from "yargs/yargs";
|
|
140
139
|
|
|
@@ -3384,6 +3383,25 @@ var AuthenticationSchema = z7.discriminatedUnion("type", [
|
|
|
3384
3383
|
authorizationParams: z7.record(z7.string(), z7.string()).optional(),
|
|
3385
3384
|
forwardAuthorizationParams: z7.array(z7.string()).optional()
|
|
3386
3385
|
}),
|
|
3386
|
+
z7.object({
|
|
3387
|
+
type: z7.literal("entra"),
|
|
3388
|
+
basePath: z7.string().optional(),
|
|
3389
|
+
clientId: z7.string(),
|
|
3390
|
+
// Tenant id or one of Entra's multi-tenant authorities
|
|
3391
|
+
// (common/organizations/consumers). Defaults to "common".
|
|
3392
|
+
tenantId: z7.string().optional(),
|
|
3393
|
+
// Full issuer override, e.g. for CIAM tenants on *.ciamlogin.com.
|
|
3394
|
+
issuer: z7.string().optional(),
|
|
3395
|
+
audience: z7.string().optional(),
|
|
3396
|
+
scopes: z7.array(z7.string()).optional(),
|
|
3397
|
+
redirectToAfterSignUp: z7.string().optional(),
|
|
3398
|
+
redirectToAfterSignIn: z7.string().optional(),
|
|
3399
|
+
redirectToAfterSignOut: z7.string().optional(),
|
|
3400
|
+
signUp: SignUpOpenIdSchema.optional(),
|
|
3401
|
+
disableSignUp: z7.boolean().optional(),
|
|
3402
|
+
authorizationParams: z7.record(z7.string(), z7.string()).optional(),
|
|
3403
|
+
forwardAuthorizationParams: z7.array(z7.string()).optional()
|
|
3404
|
+
}),
|
|
3387
3405
|
z7.object({
|
|
3388
3406
|
type: z7.literal("azureb2c"),
|
|
3389
3407
|
basePath: z7.string().optional(),
|
|
@@ -3840,6 +3858,7 @@ async function loadZudokuConfig(configEnv, rootDir) {
|
|
|
3840
3858
|
}
|
|
3841
3859
|
|
|
3842
3860
|
// src/lib/authentication/providers/util.ts
|
|
3861
|
+
var getEntraIssuer = (config) => config.issuer ?? `https://login.microsoftonline.com/${config.tenantId ?? "common"}/v2.0`;
|
|
3843
3862
|
var getClerkFrontendApi = (publishableKey) => {
|
|
3844
3863
|
const parts = publishableKey.split("_");
|
|
3845
3864
|
if (parts.length !== 3 || !parts[2]) {
|
|
@@ -3873,6 +3892,9 @@ var getIssuer = async (config) => {
|
|
|
3873
3892
|
case "openid": {
|
|
3874
3893
|
return config.authentication.issuer;
|
|
3875
3894
|
}
|
|
3895
|
+
case "entra": {
|
|
3896
|
+
return getEntraIssuer(config.authentication);
|
|
3897
|
+
}
|
|
3876
3898
|
case "supabase": {
|
|
3877
3899
|
return config.authentication.supabaseUrl;
|
|
3878
3900
|
}
|
|
@@ -8506,17 +8528,7 @@ var bundleSSREntry = async (options) => {
|
|
|
8506
8528
|
};
|
|
8507
8529
|
|
|
8508
8530
|
// src/cli/common/output.ts
|
|
8509
|
-
import * as Sentry from "@sentry/node";
|
|
8510
8531
|
import colors8 from "picocolors";
|
|
8511
|
-
|
|
8512
|
-
// src/cli/common/constants.ts
|
|
8513
|
-
var CLI_XDG_FOLDER_NAME = "zudoku";
|
|
8514
|
-
var VERSION_CHECK_FILE = "version.json";
|
|
8515
|
-
var SENTRY_DSN = void 0;
|
|
8516
|
-
var MAX_WAIT_PENDING_TIME_MS = 1e3;
|
|
8517
|
-
var POST_HOG_CAPTURE_KEY = void 0;
|
|
8518
|
-
|
|
8519
|
-
// src/cli/common/output.ts
|
|
8520
8532
|
function printDiagnosticsToConsole(message) {
|
|
8521
8533
|
console.error(colors8.bold(colors8.blue(message)));
|
|
8522
8534
|
}
|
|
@@ -8590,6 +8602,11 @@ async function build(argv) {
|
|
|
8590
8602
|
// src/cli/common/analytics/lib.ts
|
|
8591
8603
|
import { PostHog } from "posthog-node";
|
|
8592
8604
|
|
|
8605
|
+
// src/cli/common/constants.ts
|
|
8606
|
+
var CLI_XDG_FOLDER_NAME = "zudoku";
|
|
8607
|
+
var VERSION_CHECK_FILE = "version.json";
|
|
8608
|
+
var POST_HOG_CAPTURE_KEY = void 0;
|
|
8609
|
+
|
|
8593
8610
|
// src/cli/common/machine-id/lib.ts
|
|
8594
8611
|
import { execSync } from "node:child_process";
|
|
8595
8612
|
import { createHash } from "node:crypto";
|
|
@@ -9402,12 +9419,6 @@ To fix, run:
|
|
|
9402
9419
|
// src/cli/cli.ts
|
|
9403
9420
|
process.env.ZUDOKU_ENV = process.env.ZUDOKU_INTERNAL_DEV ? "internal" : "module";
|
|
9404
9421
|
var packageJson = getZudokuPackageJson();
|
|
9405
|
-
if (SENTRY_DSN) {
|
|
9406
|
-
Sentry2.init({
|
|
9407
|
-
dsn: SENTRY_DSN,
|
|
9408
|
-
release: packageJson?.version
|
|
9409
|
-
});
|
|
9410
|
-
}
|
|
9411
9422
|
var cli = yargs(hideBin(process.argv)).option("zuplo", {
|
|
9412
9423
|
type: "boolean",
|
|
9413
9424
|
description: "Enable Zuplo mode",
|
|
@@ -9421,14 +9432,7 @@ var cli = yargs(hideBin(process.argv)).option("zuplo", {
|
|
|
9421
9432
|
try {
|
|
9422
9433
|
void warnIfOutdatedVersion(packageJson?.version);
|
|
9423
9434
|
await cli.argv;
|
|
9424
|
-
void Sentry2.close(MAX_WAIT_PENDING_TIME_MS).then(() => {
|
|
9425
|
-
process.exit(0);
|
|
9426
|
-
});
|
|
9427
|
-
} catch (err) {
|
|
9428
|
-
if (err instanceof Error) {
|
|
9429
|
-
Sentry2.captureException(err);
|
|
9430
|
-
}
|
|
9431
|
-
throw err;
|
|
9432
9435
|
} finally {
|
|
9433
9436
|
await shutdownAnalytics();
|
|
9434
9437
|
}
|
|
9438
|
+
process.exit();
|
package/dist/cli/worker.js
CHANGED
|
@@ -2617,6 +2617,25 @@ var AuthenticationSchema = z4.discriminatedUnion("type", [
|
|
|
2617
2617
|
authorizationParams: z4.record(z4.string(), z4.string()).optional(),
|
|
2618
2618
|
forwardAuthorizationParams: z4.array(z4.string()).optional()
|
|
2619
2619
|
}),
|
|
2620
|
+
z4.object({
|
|
2621
|
+
type: z4.literal("entra"),
|
|
2622
|
+
basePath: z4.string().optional(),
|
|
2623
|
+
clientId: z4.string(),
|
|
2624
|
+
// Tenant id or one of Entra's multi-tenant authorities
|
|
2625
|
+
// (common/organizations/consumers). Defaults to "common".
|
|
2626
|
+
tenantId: z4.string().optional(),
|
|
2627
|
+
// Full issuer override, e.g. for CIAM tenants on *.ciamlogin.com.
|
|
2628
|
+
issuer: z4.string().optional(),
|
|
2629
|
+
audience: z4.string().optional(),
|
|
2630
|
+
scopes: z4.array(z4.string()).optional(),
|
|
2631
|
+
redirectToAfterSignUp: z4.string().optional(),
|
|
2632
|
+
redirectToAfterSignIn: z4.string().optional(),
|
|
2633
|
+
redirectToAfterSignOut: z4.string().optional(),
|
|
2634
|
+
signUp: SignUpOpenIdSchema.optional(),
|
|
2635
|
+
disableSignUp: z4.boolean().optional(),
|
|
2636
|
+
authorizationParams: z4.record(z4.string(), z4.string()).optional(),
|
|
2637
|
+
forwardAuthorizationParams: z4.array(z4.string()).optional()
|
|
2638
|
+
}),
|
|
2620
2639
|
z4.object({
|
|
2621
2640
|
type: z4.literal("azureb2c"),
|
|
2622
2641
|
basePath: z4.string().optional(),
|
|
@@ -22,3 +22,6 @@ export type FirebaseAuthenticationConfig = Extract<AuthenticationConfig, {
|
|
|
22
22
|
export type AzureB2CAuthenticationConfig = Extract<AuthenticationConfig, {
|
|
23
23
|
type: "azureb2c";
|
|
24
24
|
}>;
|
|
25
|
+
export type EntraAuthenticationConfig = Extract<AuthenticationConfig, {
|
|
26
|
+
type: "entra";
|
|
27
|
+
}>;
|
|
@@ -248,6 +248,25 @@ declare const AuthenticationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
248
248
|
disableSignUp: z.ZodOptional<z.ZodBoolean>;
|
|
249
249
|
authorizationParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
250
250
|
forwardAuthorizationParams: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
251
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
252
|
+
type: z.ZodLiteral<"entra">;
|
|
253
|
+
basePath: z.ZodOptional<z.ZodString>;
|
|
254
|
+
clientId: z.ZodString;
|
|
255
|
+
tenantId: z.ZodOptional<z.ZodString>;
|
|
256
|
+
issuer: z.ZodOptional<z.ZodString>;
|
|
257
|
+
audience: z.ZodOptional<z.ZodString>;
|
|
258
|
+
scopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
259
|
+
redirectToAfterSignUp: z.ZodOptional<z.ZodString>;
|
|
260
|
+
redirectToAfterSignIn: z.ZodOptional<z.ZodString>;
|
|
261
|
+
redirectToAfterSignOut: z.ZodOptional<z.ZodString>;
|
|
262
|
+
signUp: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|
|
263
|
+
url: z.ZodString;
|
|
264
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
265
|
+
authorizationParams: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
266
|
+
}, z.core.$strict>]>>;
|
|
267
|
+
disableSignUp: z.ZodOptional<z.ZodBoolean>;
|
|
268
|
+
authorizationParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
269
|
+
forwardAuthorizationParams: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
251
270
|
}, z.core.$strip>, z.ZodObject<{
|
|
252
271
|
type: z.ZodLiteral<"azureb2c">;
|
|
253
272
|
basePath: z.ZodOptional<z.ZodString>;
|
|
@@ -7040,6 +7059,25 @@ export declare const ZudokuConfig: z.ZodObject<{
|
|
|
7040
7059
|
disableSignUp: z.ZodOptional<z.ZodBoolean>;
|
|
7041
7060
|
authorizationParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
7042
7061
|
forwardAuthorizationParams: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
7062
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
7063
|
+
type: z.ZodLiteral<"entra">;
|
|
7064
|
+
basePath: z.ZodOptional<z.ZodString>;
|
|
7065
|
+
clientId: z.ZodString;
|
|
7066
|
+
tenantId: z.ZodOptional<z.ZodString>;
|
|
7067
|
+
issuer: z.ZodOptional<z.ZodString>;
|
|
7068
|
+
audience: z.ZodOptional<z.ZodString>;
|
|
7069
|
+
scopes: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
7070
|
+
redirectToAfterSignUp: z.ZodOptional<z.ZodString>;
|
|
7071
|
+
redirectToAfterSignIn: z.ZodOptional<z.ZodString>;
|
|
7072
|
+
redirectToAfterSignOut: z.ZodOptional<z.ZodString>;
|
|
7073
|
+
signUp: z.ZodOptional<z.ZodUnion<readonly [z.ZodObject<{
|
|
7074
|
+
url: z.ZodString;
|
|
7075
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
7076
|
+
authorizationParams: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
7077
|
+
}, z.core.$strict>]>>;
|
|
7078
|
+
disableSignUp: z.ZodOptional<z.ZodBoolean>;
|
|
7079
|
+
authorizationParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
7080
|
+
forwardAuthorizationParams: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
7043
7081
|
}, z.core.$strip>, z.ZodObject<{
|
|
7044
7082
|
type: z.ZodLiteral<"azureb2c">;
|
|
7045
7083
|
basePath: z.ZodOptional<z.ZodString>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type * as oauth from "oauth4webapi";
|
|
2
|
+
import type { EntraAuthenticationConfig } from "../../../config/config.js";
|
|
3
|
+
import type { AuthenticationPlugin, AuthenticationProviderInitializer } from "../authentication.js";
|
|
4
|
+
import type { UserProfile } from "../state.js";
|
|
5
|
+
import { OpenIDAuthenticationProvider } from "./openid.js";
|
|
6
|
+
export declare class EntraAuthenticationProvider extends OpenIDAuthenticationProvider implements AuthenticationPlugin {
|
|
7
|
+
constructor(config: EntraAuthenticationConfig);
|
|
8
|
+
protected getExpectedDiscoveryIssuer(issuerUrl: URL, response: Response): Promise<URL>;
|
|
9
|
+
protected resolveTokenIssuer(as: oauth.AuthorizationServer, response: Response): Promise<oauth.AuthorizationServer>;
|
|
10
|
+
protected buildUserProfile(userInfo: oauth.UserInfoResponse, claims: oauth.IDToken | undefined): UserProfile;
|
|
11
|
+
}
|
|
12
|
+
declare const entraAuth: AuthenticationProviderInitializer<EntraAuthenticationConfig>;
|
|
13
|
+
export default entraAuth;
|
|
@@ -3,6 +3,7 @@ import type { NavigateFunction } from "react-router";
|
|
|
3
3
|
import type { OpenIDAuthenticationConfig } from "../../../config/config.js";
|
|
4
4
|
import type { AuthActionContext, AuthActionOptions, AuthenticationPlugin, AuthenticationProviderInitializer, VerifyAccessTokenResult } from "../authentication.js";
|
|
5
5
|
import { CoreAuthenticationPlugin } from "../AuthenticationPlugin.js";
|
|
6
|
+
import { type UserProfile } from "../state.js";
|
|
6
7
|
export interface OpenIdProviderData {
|
|
7
8
|
type: "openid" | undefined;
|
|
8
9
|
accessToken: string;
|
|
@@ -48,6 +49,8 @@ export declare class OpenIDAuthenticationProvider extends CoreAuthenticationPlug
|
|
|
48
49
|
[oauth.allowInsecureRequests]?: undefined;
|
|
49
50
|
};
|
|
50
51
|
protected getAuthServer(): Promise<oauth.AuthorizationServer>;
|
|
52
|
+
protected getExpectedDiscoveryIssuer(issuerUrl: URL, _response: Response): Promise<URL>;
|
|
53
|
+
protected resolveTokenIssuer(as: oauth.AuthorizationServer, _response: Response): Promise<oauth.AuthorizationServer>;
|
|
51
54
|
protected setTokensFromResponse(response: oauth.TokenEndpointResponse): void;
|
|
52
55
|
signUp({ navigate }: {
|
|
53
56
|
navigate: NavigateFunction;
|
|
@@ -56,7 +59,7 @@ export declare class OpenIDAuthenticationProvider extends CoreAuthenticationPlug
|
|
|
56
59
|
replace?: boolean;
|
|
57
60
|
}): Promise<void>;
|
|
58
61
|
signIn(_: AuthActionContext, { redirectTo, replace }: AuthActionOptions): Promise<void>;
|
|
59
|
-
|
|
62
|
+
protected buildUserProfile(userInfo: oauth.UserInfoResponse, claims: oauth.IDToken | undefined): UserProfile;
|
|
60
63
|
verifyAccessToken(token: string): Promise<VerifyAccessTokenResult>;
|
|
61
64
|
refreshUserProfile(): Promise<boolean>;
|
|
62
65
|
private authorize;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
import type { NavigateFunction } from "react-router";
|
|
2
2
|
export declare const redirectToSignUpUrl: (url: string, navigate: NavigateFunction, replace?: boolean) => void;
|
|
3
|
+
export declare const getEntraIssuer: (config: {
|
|
4
|
+
issuer?: string;
|
|
5
|
+
tenantId?: string;
|
|
6
|
+
}) => string;
|
|
3
7
|
export declare const getClerkFrontendApi: (publishableKey: string) => string;
|
|
@@ -26,3 +26,6 @@ export declare const getCursorConfig: (name: string, mcpUrl: string, auth?: Auth
|
|
|
26
26
|
export declare const getVscodeConfig: (name: string, mcpUrl: string, auth?: AuthHeader) => string;
|
|
27
27
|
export declare const getCodexConfig: (name: string, mcpUrl: string, auth?: AuthHeader) => string;
|
|
28
28
|
export declare const getGenericConfig: (name: string, mcpUrl: string, auth?: AuthHeader) => string;
|
|
29
|
+
export declare const getCursorDeepLink: (name: string, mcpUrl: string, auth?: AuthHeader) => string;
|
|
30
|
+
export declare const getVscodeDeepLink: (name: string, mcpUrl: string, auth?: AuthHeader) => string;
|
|
31
|
+
export declare const CLAUDE_CONNECTORS_URL = "https://claude.ai/customize/connectors?modal=add-custom-connector";
|
package/dist/flat-config.d.ts
CHANGED
|
@@ -303,6 +303,23 @@ export interface FlatZudokuConfig {
|
|
|
303
303
|
[k: string]: string
|
|
304
304
|
}
|
|
305
305
|
forwardAuthorizationParams?: string[]
|
|
306
|
+
} | {
|
|
307
|
+
type: "entra"
|
|
308
|
+
basePath?: string
|
|
309
|
+
clientId: string
|
|
310
|
+
tenantId?: string
|
|
311
|
+
issuer?: string
|
|
312
|
+
audience?: string
|
|
313
|
+
scopes?: string[]
|
|
314
|
+
redirectToAfterSignUp?: string
|
|
315
|
+
redirectToAfterSignIn?: string
|
|
316
|
+
redirectToAfterSignOut?: string
|
|
317
|
+
signUp?: _Schema13
|
|
318
|
+
disableSignUp?: boolean
|
|
319
|
+
authorizationParams?: {
|
|
320
|
+
[k: string]: string
|
|
321
|
+
}
|
|
322
|
+
forwardAuthorizationParams?: string[]
|
|
306
323
|
} | {
|
|
307
324
|
type: "azureb2c"
|
|
308
325
|
basePath?: string
|
|
@@ -72,9 +72,9 @@ to integrate Azure AD with your Zudoku documentation site.
|
|
|
72
72
|
export default {
|
|
73
73
|
// ... other configuration
|
|
74
74
|
authentication: {
|
|
75
|
-
type: "
|
|
75
|
+
type: "entra",
|
|
76
76
|
clientId: "<your-application-client-id>",
|
|
77
|
-
|
|
77
|
+
tenantId: "<your-tenant-id>",
|
|
78
78
|
scopes: ["openid", "profile", "email"], // Optional: customize scopes
|
|
79
79
|
},
|
|
80
80
|
// ... other configuration
|
|
@@ -91,20 +91,21 @@ For single tenant (organization-only access):
|
|
|
91
91
|
|
|
92
92
|
```typescript
|
|
93
93
|
authentication: {
|
|
94
|
-
type: "
|
|
94
|
+
type: "entra",
|
|
95
95
|
clientId: "<your-application-client-id>",
|
|
96
|
-
|
|
96
|
+
tenantId: "<your-tenant-id>",
|
|
97
97
|
scopes: ["openid", "profile", "email"],
|
|
98
98
|
}
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
For multitenant (any Azure AD organization):
|
|
101
|
+
For multitenant (any Azure AD organization), use `tenantId: "common"` or `"organizations"` — this is
|
|
102
|
+
also the default if `tenantId` is omitted:
|
|
102
103
|
|
|
103
104
|
```typescript
|
|
104
105
|
authentication: {
|
|
105
|
-
type: "
|
|
106
|
+
type: "entra",
|
|
106
107
|
clientId: "<your-application-client-id>",
|
|
107
|
-
|
|
108
|
+
tenantId: "common",
|
|
108
109
|
scopes: ["openid", "profile", "email"],
|
|
109
110
|
}
|
|
110
111
|
```
|
|
@@ -115,9 +116,9 @@ Request additional Microsoft Graph API scopes:
|
|
|
115
116
|
|
|
116
117
|
```typescript
|
|
117
118
|
authentication: {
|
|
118
|
-
type: "
|
|
119
|
+
type: "entra",
|
|
119
120
|
clientId: "<your-application-client-id>",
|
|
120
|
-
|
|
121
|
+
tenantId: "<your-tenant-id>",
|
|
121
122
|
scopes: [
|
|
122
123
|
"openid",
|
|
123
124
|
"profile",
|
|
@@ -136,7 +137,7 @@ Protect specific documentation routes using the `protectedRoutes` configuration:
|
|
|
136
137
|
{
|
|
137
138
|
// ... other configuration
|
|
138
139
|
authentication: {
|
|
139
|
-
type: "
|
|
140
|
+
type: "entra",
|
|
140
141
|
// ... Azure AD config
|
|
141
142
|
},
|
|
142
143
|
protectedRoutes: [
|
|
@@ -217,16 +218,17 @@ Azure AD provides rich user profile data through OpenID Connect:
|
|
|
217
218
|
2. **Redirect URI Mismatch**: The redirect URI must exactly match one configured in Azure AD,
|
|
218
219
|
including protocol and path.
|
|
219
220
|
|
|
220
|
-
3. **Tenant Access Issues**: For single-tenant apps, ensure users are from the correct tenant
|
|
221
|
-
multi-tenant,
|
|
221
|
+
3. **Tenant Access Issues**: For single-tenant apps, ensure users are from the correct tenant and
|
|
222
|
+
`tenantId` is set to that tenant's GUID. For multi-tenant, use `tenantId: "common"` or
|
|
223
|
+
`"organizations"`.
|
|
222
224
|
|
|
223
225
|
4. **Missing User Information**: Check that required API permissions are granted and admin consent
|
|
224
226
|
is provided if needed.
|
|
225
227
|
|
|
226
|
-
5. **Token Validation Errors**: Ensure your
|
|
227
|
-
|
|
228
|
+
5. **Token Validation Errors**: Ensure your `tenantId` is correct. If you use a CIAM tenant
|
|
229
|
+
(`*.ciamlogin.com`), set the `issuer` option to override the default issuer instead.
|
|
228
230
|
|
|
229
|
-
6. **Authentication Not Working**: Verify your
|
|
231
|
+
6. **Authentication Not Working**: Verify your `tenantId` and client ID are correct, and that your
|
|
230
232
|
app registration is configured as a Single-page application (SPA) with the correct redirect URIs.
|
|
231
233
|
|
|
232
234
|
## Security Best Practices
|
|
@@ -15,8 +15,8 @@ authentication provider you use.
|
|
|
15
15
|
|
|
16
16
|
## Authentication Providers
|
|
17
17
|
|
|
18
|
-
Zudoku supports Clerk, Auth0, Supabase, Firebase, Azure B2C, and any OpenID
|
|
19
|
-
(including Okta, Keycloak, Authentik, and PingFederate).
|
|
18
|
+
Zudoku supports Clerk, Auth0, Supabase, Firebase, Microsoft Entra ID, Azure B2C, and any OpenID
|
|
19
|
+
Connect provider (including Okta, Keycloak, Authentik, and PingFederate).
|
|
20
20
|
|
|
21
21
|
Not seeing your authentication provider? [Let us know](https://github.com/zuplo/zudoku/issues)
|
|
22
22
|
|
|
@@ -99,6 +99,26 @@ providing your own array of scopes.
|
|
|
99
99
|
For provider-specific guides (Okta, Keycloak, etc.), see the
|
|
100
100
|
[OpenID Connect setup page](./authentication-openid.md).
|
|
101
101
|
|
|
102
|
+
### Microsoft Entra ID
|
|
103
|
+
|
|
104
|
+
For Microsoft Entra ID (formerly Azure AD), you will need the `clientId` from your app registration
|
|
105
|
+
and your `tenantId`.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
{
|
|
109
|
+
// ...
|
|
110
|
+
authentication: {
|
|
111
|
+
type: "entra",
|
|
112
|
+
clientId: "<your-application-client-id>",
|
|
113
|
+
tenantId: "<your-tenant-id>", // Or "common" for multitenant. Defaults to "common".
|
|
114
|
+
},
|
|
115
|
+
// ...
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
For full setup instructions, see the
|
|
120
|
+
[Azure AD / Entra ID setup guide](./authentication-azure-ad.md).
|
|
121
|
+
|
|
102
122
|
### Firebase
|
|
103
123
|
|
|
104
124
|
For Firebase authentication, you will need your Firebase project configuration. You can find this in
|
|
@@ -187,3 +187,29 @@ since they use a different interaction model.
|
|
|
187
187
|
If you are using [Zuplo](https://zuplo.com) to host your API, the `x-mcp-server` extension is
|
|
188
188
|
automatically added to POST operations that use the `mcpServerHandler`. No manual schema changes are
|
|
189
189
|
needed. See the [Zuplo MCP documentation](https://zuplo.com/docs/handlers/mcp-server) for details.
|
|
190
|
+
|
|
191
|
+
The server name shown in the install snippets (for example `claude mcp add … 'MCP Server' …`) is
|
|
192
|
+
taken from the handler's `name` option — the same name your MCP server advertises to clients. Set it
|
|
193
|
+
to override the default `"MCP Server"` title:
|
|
194
|
+
|
|
195
|
+
```json title="config/routes.oas.json (paths section)"
|
|
196
|
+
{
|
|
197
|
+
"/mcp": {
|
|
198
|
+
"post": {
|
|
199
|
+
"operationId": "mcpServerHandler",
|
|
200
|
+
"x-zuplo-route": {
|
|
201
|
+
"handler": {
|
|
202
|
+
"export": "mcpServerHandler",
|
|
203
|
+
"module": "$import(@zuplo/runtime)",
|
|
204
|
+
"options": {
|
|
205
|
+
"name": "Acme API",
|
|
206
|
+
"operations": [{ "file": "./config/routes.oas.json", "id": "get-users" }]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
With this configuration the snippets read `claude mcp add --transport http 'Acme API' …`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zudoku",
|
|
3
|
-
"version": "0.82.
|
|
3
|
+
"version": "0.82.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.19.0 <21.0.0 || >=22.12.0"
|
|
@@ -57,6 +57,10 @@
|
|
|
57
57
|
"types": "./dist/declarations/lib/authentication/providers/openid.d.ts",
|
|
58
58
|
"default": "./src/lib/authentication/providers/openid.tsx"
|
|
59
59
|
},
|
|
60
|
+
"./auth/entra": {
|
|
61
|
+
"types": "./dist/declarations/lib/authentication/providers/entra.d.ts",
|
|
62
|
+
"default": "./src/lib/authentication/providers/entra.tsx"
|
|
63
|
+
},
|
|
60
64
|
"./auth/supabase": {
|
|
61
65
|
"types": "./dist/declarations/lib/authentication/providers/supabase.d.ts",
|
|
62
66
|
"default": "./src/lib/authentication/providers/supabase.tsx"
|
|
@@ -173,13 +177,13 @@
|
|
|
173
177
|
},
|
|
174
178
|
"dependencies": {
|
|
175
179
|
"@apidevtools/json-schema-ref-parser": "15.3.5",
|
|
176
|
-
"@base-ui/react": "^1.
|
|
180
|
+
"@base-ui/react": "^1.6.0",
|
|
177
181
|
"@envelop/core": "5.5.1",
|
|
178
182
|
"@graphiql/toolkit": "0.12.1",
|
|
179
183
|
"@graphql-typed-document-node/core": "3.2.0",
|
|
180
184
|
"@hono/node-server": "2.0.8",
|
|
181
185
|
"@lekoarts/rehype-meta-as-attributes": "3.0.3",
|
|
182
|
-
"@mdx-js/mdx": "3.1.
|
|
186
|
+
"@mdx-js/mdx": "3.1.1",
|
|
183
187
|
"@mdx-js/react": "3.1.1",
|
|
184
188
|
"@mdx-js/rollup": "3.1.1",
|
|
185
189
|
"@pothos/core": "4.12.0",
|
|
@@ -209,7 +213,6 @@
|
|
|
209
213
|
"@radix-ui/react-visually-hidden": "1.2.4",
|
|
210
214
|
"@scalar/openapi-parser": "0.28.4",
|
|
211
215
|
"@scalar/snippetz": "0.9.11",
|
|
212
|
-
"@sentry/node": "10.62.0",
|
|
213
216
|
"@shikijs/langs": "4.2.0",
|
|
214
217
|
"@shikijs/rehype": "4.2.0",
|
|
215
218
|
"@shikijs/themes": "4.2.0",
|
|
@@ -220,8 +223,8 @@
|
|
|
220
223
|
"@tanstack/react-query": "5.101.0",
|
|
221
224
|
"@types/react": "19.2.17",
|
|
222
225
|
"@types/react-dom": "19.2.3",
|
|
223
|
-
"@vitejs/plugin-react": "6.0.
|
|
224
|
-
"@x0k/json-schema-merge": "1.0.
|
|
226
|
+
"@vitejs/plugin-react": "6.0.3",
|
|
227
|
+
"@x0k/json-schema-merge": "1.0.4",
|
|
225
228
|
"@unhead/react": "3.1.3",
|
|
226
229
|
"@zuplo/mcp": "0.0.32",
|
|
227
230
|
"bs58": "6.0.0",
|
|
@@ -250,7 +253,7 @@
|
|
|
250
253
|
"json-schema-to-typescript-lite": "15.0.0",
|
|
251
254
|
"loglevel": "1.9.2",
|
|
252
255
|
"lucide-react": "1.16.0",
|
|
253
|
-
"mdast-util-from-markdown": "2.0.
|
|
256
|
+
"mdast-util-from-markdown": "2.0.3",
|
|
254
257
|
"mdast-util-mdx": "3.0.0",
|
|
255
258
|
"mdast-util-mdx-jsx": "3.2.0",
|
|
256
259
|
"micromark-extension-mdxjs": "3.0.0",
|
|
@@ -262,7 +265,7 @@
|
|
|
262
265
|
"pagefind": "1.5.2",
|
|
263
266
|
"picocolors": "1.1.1",
|
|
264
267
|
"piscina": "5.2.0",
|
|
265
|
-
"posthog-node": "5.
|
|
268
|
+
"posthog-node": "5.40.0",
|
|
266
269
|
"quick-lru": "7.3.0",
|
|
267
270
|
"react-error-boundary": "6.1.1",
|
|
268
271
|
"react-hook-form": "7.76.1",
|
|
@@ -316,7 +319,7 @@
|
|
|
316
319
|
"@types/yargs": "17.0.35",
|
|
317
320
|
"@typescript/native-preview": "7.0.0-dev.20260624.1",
|
|
318
321
|
"@vitest/coverage-v8": "4.1.9",
|
|
319
|
-
"happy-dom": "20.10.
|
|
322
|
+
"happy-dom": "20.10.6",
|
|
320
323
|
"oxc-parser": "^0.135.0",
|
|
321
324
|
"react": "19.2.7",
|
|
322
325
|
"react-dom": "19.2.7",
|
package/src/config/config.ts
CHANGED
|
@@ -486,6 +486,25 @@ const AuthenticationSchema = z.discriminatedUnion("type", [
|
|
|
486
486
|
authorizationParams: z.record(z.string(), z.string()).optional(),
|
|
487
487
|
forwardAuthorizationParams: z.array(z.string()).optional(),
|
|
488
488
|
}),
|
|
489
|
+
z.object({
|
|
490
|
+
type: z.literal("entra"),
|
|
491
|
+
basePath: z.string().optional(),
|
|
492
|
+
clientId: z.string(),
|
|
493
|
+
// Tenant id or one of Entra's multi-tenant authorities
|
|
494
|
+
// (common/organizations/consumers). Defaults to "common".
|
|
495
|
+
tenantId: z.string().optional(),
|
|
496
|
+
// Full issuer override, e.g. for CIAM tenants on *.ciamlogin.com.
|
|
497
|
+
issuer: z.string().optional(),
|
|
498
|
+
audience: z.string().optional(),
|
|
499
|
+
scopes: z.array(z.string()).optional(),
|
|
500
|
+
redirectToAfterSignUp: z.string().optional(),
|
|
501
|
+
redirectToAfterSignIn: z.string().optional(),
|
|
502
|
+
redirectToAfterSignOut: z.string().optional(),
|
|
503
|
+
signUp: SignUpOpenIdSchema.optional(),
|
|
504
|
+
disableSignUp: z.boolean().optional(),
|
|
505
|
+
authorizationParams: z.record(z.string(), z.string()).optional(),
|
|
506
|
+
forwardAuthorizationParams: z.array(z.string()).optional(),
|
|
507
|
+
}),
|
|
489
508
|
z.object({
|
|
490
509
|
type: z.literal("azureb2c"),
|
|
491
510
|
basePath: z.string().optional(),
|
package/src/lib/auth/issuer.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { ZudokuConfig } from "../../config/validators/ZudokuConfig.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getClerkFrontendApi,
|
|
4
|
+
getEntraIssuer,
|
|
5
|
+
} from "../authentication/providers/util.js";
|
|
3
6
|
|
|
4
7
|
export const getIssuer = async (config: ZudokuConfig) => {
|
|
5
8
|
switch (config.authentication?.type) {
|
|
@@ -12,6 +15,9 @@ export const getIssuer = async (config: ZudokuConfig) => {
|
|
|
12
15
|
case "openid": {
|
|
13
16
|
return config.authentication.issuer;
|
|
14
17
|
}
|
|
18
|
+
case "entra": {
|
|
19
|
+
return getEntraIssuer(config.authentication);
|
|
20
|
+
}
|
|
15
21
|
case "supabase": {
|
|
16
22
|
return config.authentication.supabaseUrl;
|
|
17
23
|
}
|