@nyuchi/mzizi-mcp 0.3.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/README.md ADDED
@@ -0,0 +1,186 @@
1
+ # @nyuchi/mzizi-mcp
2
+
3
+ Registry-driven Model Context Protocol server for the mzizi design system — the
4
+ drop-in replacement for the legacy `design.nyuchi.com` MCP.
5
+
6
+ The tool catalog is loaded at startup from the Supabase `mcp_tool_registry` table
7
+ (RLS public-read, anon role) and dispatched dynamically. Adding, renaming, or
8
+ retiring a tool is a registry edit, not a code change. The server is read-only on
9
+ the anonymous surface — write-kind tools are excluded.
10
+
11
+ **Live at** `https://mcp.mzizi.dev/mcp` (Cloudflare Worker `mzizi-mcp`, version
12
+ 0.3.0). A **free WorkOS-AuthKit signup gate** fronts the endpoint; sign up
13
+ once and read the whole registry at no cost. The worker advertises
14
+ `/.well-known/oauth-protected-resource` so MCP clients can run the OAuth discovery
15
+ dance automatically.
16
+
17
+ **MCP Registry name:** `io.github.nyuchi/mzizi-mcp`
18
+
19
+ ---
20
+
21
+ ## Using it — MCP client config
22
+
23
+ Point any MCP client at the live HTTP endpoint:
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "mzizi": {
29
+ "type": "http",
30
+ "url": "https://mcp.mzizi.dev/mcp"
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ The client will be redirected through the WorkOS-AuthKit OAuth flow on first
37
+ connect. After that, reads are free and unlimited.
38
+
39
+ ### stdio (local / offline)
40
+
41
+ Run the stdio bin via npx — useful for Claude Code, Cursor, or any assistant that
42
+ speaks stdio MCP:
43
+
44
+ ```json
45
+ {
46
+ "mcpServers": {
47
+ "mzizi": {
48
+ "type": "stdio",
49
+ "command": "npx",
50
+ "args": ["-y", "@nyuchi/mzizi-mcp"],
51
+ "env": {
52
+ "SUPABASE_URL": "https://grjsboqkaywpwatvrzmy.supabase.co",
53
+ "SUPABASE_PUBLISHABLE_KEY": "<anon-key>"
54
+ }
55
+ }
56
+ }
57
+ }
58
+ ```
59
+
60
+ `SUPABASE_URL` defaults to the canonical mzizi project if omitted. Only
61
+ `SUPABASE_PUBLISHABLE_KEY` is required for the stdio path.
62
+
63
+ ---
64
+
65
+ ## Registry-driven dispatch
66
+
67
+ | Registry `kind` | Dispatch |
68
+ | --------------- | ----------------------------------------- |
69
+ | `sql_function` | `supabase.rpc(fn, args)` |
70
+ | `edge_function` | `supabase.functions.invoke(fn, { body })` |
71
+ | `source_table` | `supabase.from(table).select()` |
72
+
73
+ The registry currently holds 62 rows (60 enabled). After filtering out write-kind
74
+ and first-party tools, ~55 tools are exposed on the anonymous public surface,
75
+ spread across 17 categories.
76
+
77
+ ---
78
+
79
+ ## Core tools (always present, defined in code)
80
+
81
+ | Tool | Backed by | Returns |
82
+ | --------------------- | ------------------------------- | -------------------------------------------------------- |
83
+ | `list_collections` | `component_documents` aggregate | Every collection + document counts + per-owner breakdown |
84
+ | `get_database_status` | `component_documents` count | Provider health + document-store row count + tool count |
85
+
86
+ ---
87
+
88
+ ## Tool categories (registry-driven)
89
+
90
+ | Category | Example tools |
91
+ | --------------- | --------------------------------------------------------------------------------- |
92
+ | `component` | `get_component`, `list_components`, `search_components`, `get_component_links` |
93
+ | `architecture` | `get_node_documents` |
94
+ | `brand` | `get_brand_tokens`, `list_ecosystem_brands` |
95
+ | `skills` | `get_skill`, `list_skills` |
96
+ | `doctrine` | doctrine-read tools |
97
+ | `documents` | `read_documents`, `read_versions` |
98
+ | `release` | `list_changelog`, `get_changelog_entry`, `compute_release_diff` |
99
+ | `resolver` | `resolve_primitive`, `list_framework_descriptors` |
100
+ | `a11y` | `calculate_contrast_ratio`, `simulate_color_blindness`, `run_accessibility_audit` |
101
+ | `observability` | `list_observability_events`, `is_domain_allowed` |
102
+ | `fundi` | `get_healing_log`, `list_recent_fundi_issues` |
103
+ | `chaos` | `list_chaos_events` |
104
+ | `system` | system-read tools |
105
+ | `ai` | `get_ai_instructions`, `list_ai_instructions` |
106
+ | `governance` | `get_bundu_convention` |
107
+ | `meta` | `mcp_describe` |
108
+
109
+ Call `mcp_describe` (with optional `p_category` filter) to enumerate the full live
110
+ catalog.
111
+
112
+ ---
113
+
114
+ ## Resources
115
+
116
+ | URI | Content |
117
+ | --------------------- | ---------------------------------------------------- |
118
+ | `mzizi://collections` | Every collection + document counts + owner breakdown |
119
+ | `mzizi://components` | Lean index of the `components` collection |
120
+
121
+ ---
122
+
123
+ ## Environment / secrets
124
+
125
+ ### HTTP Worker (Cloudflare — set via `wrangler secret put`)
126
+
127
+ | Secret | Required | Notes |
128
+ | -------------------------- | -------- | ---------------------------------------------- |
129
+ | `SUPABASE_URL` | yes | Public Supabase project URL |
130
+ | `SUPABASE_PUBLISHABLE_KEY` | yes | Anon (RLS public-read) key |
131
+ | `SUPABASE_SECRET_KEY` | no | Service-role key; placeholder is used if unset |
132
+ | `WORKOS_CLIENT_ID` | yes | mzizi-mcp WorkOS AuthKit app client ID |
133
+ | `WORKOS_CLIENT_SECRET` | yes | mzizi-mcp WorkOS AuthKit app secret |
134
+ | `COOKIE_ENCRYPTION_KEY` | yes | HMAC key for the consent cookie (random bytes) |
135
+
136
+ ### stdio / local dev (`.dev.vars` or environment)
137
+
138
+ | Variable | Required | Notes |
139
+ | -------------------------- | -------- | ----------------------------------- |
140
+ | `SUPABASE_URL` | no | Defaults to canonical mzizi project |
141
+ | `SUPABASE_PUBLISHABLE_KEY` | yes | Anon key |
142
+
143
+ ---
144
+
145
+ ## Entrypoints
146
+
147
+ | Surface | Entry | Use |
148
+ | ------------- | ------------------- | ----------------------------------------------- |
149
+ | stdio | `bin: mzizi-mcp` | Local AI assistants (Claude Code, Cursor, etc.) |
150
+ | HTTP / Worker | `mzizi-mcp/http` | Cloudflare Workers + any fetch runtime |
151
+ | Library | `@nyuchi/mzizi-mcp` | Embed the server factory in your own host |
152
+
153
+ Source files:
154
+
155
+ - `src/server.ts` — `createMziziMcpServer(supabase)` factory
156
+ - `src/http.ts` — `createMziziHttpHandler()` for any fetch runtime
157
+ - `src/worker.ts` — Cloudflare Worker bound to `env` (the gated deployment)
158
+ - `src/stdio.ts` — `bin: mzizi-mcp` for the MCP registry / local Claude Code
159
+
160
+ ---
161
+
162
+ ## Build and deploy
163
+
164
+ ```bash
165
+ # typecheck
166
+ pnpm --filter @nyuchi/mzizi-mcp typecheck
167
+
168
+ # build (tsc → dist/)
169
+ pnpm --filter @nyuchi/mzizi-mcp build
170
+
171
+ # local Worker dev
172
+ pnpm --filter @nyuchi/mzizi-mcp cf:dev
173
+
174
+ # deploy manually (CI handles pushes to main)
175
+ pnpm --filter @nyuchi/mzizi-mcp cf:deploy
176
+ ```
177
+
178
+ CI deploys automatically on push to `main` touching `mzizi-mcp/**` via
179
+ `.github/workflows/deploy-mzizi-mcp.yml`.
180
+
181
+ ---
182
+
183
+ ## License
184
+
185
+ Apache-2.0. Part of the mzizi tooling — an open-architecture project of the
186
+ [Bundu Foundation](https://mzizi.dev), operated by [nyuchi](https://nyuchi.com).
@@ -0,0 +1,28 @@
1
+ /**
2
+ * WorkOS AuthKit handler — the OAuth provider's `defaultHandler`.
3
+ *
4
+ * Ported from `nyuchi/mongodb-mcp`, with the gates removed: mongodb-mcp is
5
+ * internal and enforces an org allowlist + a required permission. mzizi-mcp is
6
+ * a FREE, PUBLIC tool — any WorkOS user who signs up is let through. There is
7
+ * no org gate and no required-permission gate here.
8
+ *
9
+ * Subscription gating for the future in-MCP fundi agent does NOT belong in this
10
+ * callback (auth = free signup). It belongs at the fundi *tool* level, which
11
+ * reads the entitlement off `ctx.props` (see `props.ts`). Keeping `permissions`
12
+ * on the token is what makes that later check possible without a second login.
13
+ */
14
+ import type { OAuthHelpers } from "@cloudflare/workers-oauth-provider";
15
+ import { WorkOS } from "@workos-inc/node";
16
+ import { Hono } from "hono";
17
+ import type { Env } from "../env.js";
18
+ type AuthEnv = {
19
+ Bindings: Env & {
20
+ OAUTH_PROVIDER: OAuthHelpers;
21
+ };
22
+ Variables: {
23
+ workOS: WorkOS;
24
+ };
25
+ };
26
+ export declare const AuthkitHandler: Hono<AuthEnv, import("hono/types").BlankSchema, "/">;
27
+ export {};
28
+ //# sourceMappingURL=authkit-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authkit-handler.d.ts","sourceRoot":"","sources":["../../src/auth/authkit-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAe,YAAY,EAAE,MAAM,oCAAoC,CAAA;AACnF,OAAO,EAAiD,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACxF,OAAO,EAAgB,IAAI,EAAE,MAAM,MAAM,CAAA;AAEzC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAcpC,KAAK,OAAO,GAAG;IAAE,QAAQ,EAAE,GAAG,GAAG;QAAE,cAAc,EAAE,YAAY,CAAA;KAAE,CAAC;IAAC,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAA;AAqLlG,eAAO,MAAM,cAAc,sDAAM,CAAA"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * WorkOS AuthKit handler — the OAuth provider's `defaultHandler`.
3
+ *
4
+ * Ported from `nyuchi/mongodb-mcp`, with the gates removed: mongodb-mcp is
5
+ * internal and enforces an org allowlist + a required permission. mzizi-mcp is
6
+ * a FREE, PUBLIC tool — any WorkOS user who signs up is let through. There is
7
+ * no org gate and no required-permission gate here.
8
+ *
9
+ * Subscription gating for the future in-MCP fundi agent does NOT belong in this
10
+ * callback (auth = free signup). It belongs at the fundi *tool* level, which
11
+ * reads the entitlement off `ctx.props` (see `props.ts`). Keeping `permissions`
12
+ * on the token is what makes that later check possible without a second login.
13
+ */
14
+ import { WorkOS } from "@workos-inc/node";
15
+ import { Hono } from "hono";
16
+ import * as jose from "jose";
17
+ import { addApprovedClient, bindStateToSession, createOAuthState, generateCSRFProtection, isClientApproved, OAuthError, renderApprovalDialog, validateCSRFToken, validateOAuthState, } from "./oauth-utils.js";
18
+ const SERVER_NAME = "Mzizi Design System Registry";
19
+ const SERVER_DESCRIPTION = "Free, public MCP over the Mzizi design-system registry — components, design tokens, brand, and the N1–N10 ecosystem architecture. Sign in or create a free account to connect.";
20
+ const ICON_REDIRECT = "https://www.nyuchi.com/icon-light.png";
21
+ const app = new Hono();
22
+ function requireWorkOS(c) {
23
+ const existing = c.get("workOS");
24
+ if (existing)
25
+ return existing;
26
+ if (!c.env.WORKOS_CLIENT_SECRET) {
27
+ throw new Error("WORKOS_CLIENT_SECRET is not configured. Add it as a Worker secret before using auth routes.");
28
+ }
29
+ const workOS = new WorkOS(c.env.WORKOS_CLIENT_SECRET);
30
+ c.set("workOS", workOS);
31
+ return workOS;
32
+ }
33
+ function redirectToIcon() {
34
+ return new Response(null, {
35
+ status: 301,
36
+ headers: { Location: ICON_REDIRECT, "Cache-Control": "public, max-age=86400" },
37
+ });
38
+ }
39
+ app.get("/", (c) => c.html(`<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>${SERVER_NAME}</title><style>body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#f9fafb;color:#111;margin:0}.wrap{max-width:640px;margin:4rem auto;padding:2rem}h1{font-size:1.6rem}code{background:#eef2f0;padding:.15rem .4rem;border-radius:4px}a{color:#1f6f4f}</style></head><body><div class="wrap"><h1>${SERVER_NAME}</h1><p>${SERVER_DESCRIPTION}</p><p>This is a remote <strong>Model Context Protocol</strong> endpoint. Add it as a connector in an MCP client (e.g. claude.ai) and connect to <code>/mcp</code>; you will be prompted to sign in or create a free account.</p><p>Learn more at <a href="https://mzizi.dev">mzizi.dev</a>.</p></div></body></html>`, 200, { "Cache-Control": "public, max-age=300" }));
40
+ app.get("/favicon.ico", () => redirectToIcon());
41
+ app.get("/icon.png", () => redirectToIcon());
42
+ app.get("/authorize", async (c) => {
43
+ const oauthReqInfo = await c.env.OAUTH_PROVIDER.parseAuthRequest(c.req.raw);
44
+ const { clientId } = oauthReqInfo;
45
+ if (!clientId)
46
+ return c.text("Invalid request", 400);
47
+ if (await isClientApproved(c.req.raw, clientId, c.env.COOKIE_ENCRYPTION_KEY)) {
48
+ const { stateToken } = await createOAuthState(oauthReqInfo, c.env.OAUTH_KV);
49
+ const { setCookie: sessionBindingCookie } = await bindStateToSession(stateToken);
50
+ return redirectToAuthKit(c, stateToken, { "Set-Cookie": sessionBindingCookie });
51
+ }
52
+ const { token: csrfToken, setCookie } = generateCSRFProtection();
53
+ return renderApprovalDialog(c.req.raw, {
54
+ client: await c.env.OAUTH_PROVIDER.lookupClient(clientId),
55
+ csrfToken,
56
+ server: { description: SERVER_DESCRIPTION, name: SERVER_NAME },
57
+ setCookie,
58
+ state: { oauthReqInfo },
59
+ });
60
+ });
61
+ app.post("/authorize", async (c) => {
62
+ try {
63
+ const formData = await c.req.raw.formData();
64
+ validateCSRFToken(formData, c.req.raw);
65
+ const encodedState = formData.get("state");
66
+ if (!encodedState || typeof encodedState !== "string") {
67
+ return c.text("Missing state in form data", 400);
68
+ }
69
+ let state;
70
+ try {
71
+ state = JSON.parse(atob(encodedState));
72
+ }
73
+ catch {
74
+ return c.text("Invalid state data", 400);
75
+ }
76
+ if (!state.oauthReqInfo || !state.oauthReqInfo.clientId) {
77
+ return c.text("Invalid request", 400);
78
+ }
79
+ const approvedClientCookie = await addApprovedClient(c.req.raw, state.oauthReqInfo.clientId, c.env.COOKIE_ENCRYPTION_KEY);
80
+ const { stateToken } = await createOAuthState(state.oauthReqInfo, c.env.OAUTH_KV);
81
+ const { setCookie: sessionBindingCookie } = await bindStateToSession(stateToken);
82
+ const headers = new Headers();
83
+ headers.append("Set-Cookie", approvedClientCookie);
84
+ headers.append("Set-Cookie", sessionBindingCookie);
85
+ return redirectToAuthKit(c, stateToken, Object.fromEntries(headers));
86
+ }
87
+ catch (error) {
88
+ console.error("POST /authorize error:", error);
89
+ if (error instanceof OAuthError)
90
+ return error.toResponse();
91
+ const message = error instanceof Error ? error.message : String(error);
92
+ return c.text(`Internal server error: ${message}`, 500);
93
+ }
94
+ });
95
+ function redirectToAuthKit(c, stateToken, headers = {}) {
96
+ const workOS = requireWorkOS(c);
97
+ return new Response(null, {
98
+ headers: {
99
+ ...headers,
100
+ location: workOS.userManagement.getAuthorizationUrl({
101
+ provider: "authkit",
102
+ clientId: c.env.WORKOS_CLIENT_ID,
103
+ redirectUri: new URL("/callback", c.req.url).href,
104
+ state: stateToken,
105
+ }),
106
+ },
107
+ status: 302,
108
+ });
109
+ }
110
+ app.get("/callback", async (c) => {
111
+ let oauthReqInfo;
112
+ let clearSessionCookie;
113
+ try {
114
+ const result = await validateOAuthState(c.req.raw, c.env.OAUTH_KV);
115
+ oauthReqInfo = result.oauthReqInfo;
116
+ clearSessionCookie = result.clearCookie;
117
+ }
118
+ catch (error) {
119
+ if (error instanceof OAuthError)
120
+ return error.toResponse();
121
+ return c.text("Internal server error", 500);
122
+ }
123
+ if (!oauthReqInfo.clientId)
124
+ return c.text("Invalid OAuth request data", 400);
125
+ const code = c.req.query("code");
126
+ if (!code)
127
+ return c.text("Missing code", 400);
128
+ const workOS = requireWorkOS(c);
129
+ let response;
130
+ try {
131
+ response = await workOS.userManagement.authenticateWithCode({
132
+ clientId: c.env.WORKOS_CLIENT_ID,
133
+ code,
134
+ });
135
+ }
136
+ catch (error) {
137
+ console.error("Authentication error:", error);
138
+ return c.text("Invalid authorization code", 400);
139
+ }
140
+ const { accessToken, organizationId, refreshToken, user } = response;
141
+ const { permissions = [] } = jose.decodeJwt(accessToken);
142
+ // No org allowlist, no required-permission gate: mzizi-mcp is free + public,
143
+ // so every authenticated WorkOS user is let through. `permissions` is carried
144
+ // on the token only so a future fundi subscription check can read it.
145
+ const { redirectTo } = await c.env.OAUTH_PROVIDER.completeAuthorization({
146
+ request: oauthReqInfo,
147
+ userId: user.id,
148
+ metadata: {},
149
+ scope: permissions,
150
+ props: {
151
+ accessToken,
152
+ organizationId,
153
+ permissions,
154
+ refreshToken,
155
+ user,
156
+ },
157
+ });
158
+ const headers = new Headers({ Location: redirectTo });
159
+ if (clearSessionCookie)
160
+ headers.set("Set-Cookie", clearSessionCookie);
161
+ return new Response(null, { status: 302, headers });
162
+ });
163
+ export const AuthkitHandler = app;
164
+ //# sourceMappingURL=authkit-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authkit-handler.js","sourceRoot":"","sources":["../../src/auth/authkit-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The MCP API handler the OAuth provider mounts at `apiRoute` (`/mcp`).
3
+ *
4
+ * It only runs AFTER the provider has validated the access token, so the
5
+ * authenticated WorkOS identity is available on `ctx.props`. Today the registry
6
+ * tools are free (auth only) and ignore props; when the fundi agent is attached
7
+ * inside the MCP, its tools read `ctx.props.permissions` for the subscription
8
+ * entitlement — the subscription gate, layered on top of the free auth gate.
9
+ */
10
+ import type { Env } from "../env.js";
11
+ import type { Props } from "./props.js";
12
+ type AuthedContext = ExecutionContext & {
13
+ props?: Props;
14
+ };
15
+ export declare const mcpApiHandler: {
16
+ fetch(request: Request, env: Env, _ctx: AuthedContext): Promise<Response>;
17
+ };
18
+ export {};
19
+ //# sourceMappingURL=mcp-api-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-api-handler.d.ts","sourceRoot":"","sources":["../../src/auth/mcp-api-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAEvC,KAAK,aAAa,GAAG,gBAAgB,GAAG;IAAE,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAA;AAEzD,eAAO,MAAM,aAAa;mBACH,OAAO,OAAO,GAAG,QAAQ,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC;CAiBhF,CAAA"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The MCP API handler the OAuth provider mounts at `apiRoute` (`/mcp`).
3
+ *
4
+ * It only runs AFTER the provider has validated the access token, so the
5
+ * authenticated WorkOS identity is available on `ctx.props`. Today the registry
6
+ * tools are free (auth only) and ignore props; when the fundi agent is attached
7
+ * inside the MCP, its tools read `ctx.props.permissions` for the subscription
8
+ * entitlement — the subscription gate, layered on top of the free auth gate.
9
+ */
10
+ import { createMziziHttpHandler } from "../http.js";
11
+ export const mcpApiHandler = {
12
+ async fetch(request, env, _ctx) {
13
+ const handler = createMziziHttpHandler({
14
+ supabaseConfig: {
15
+ auth: "none",
16
+ cors: false,
17
+ env: {
18
+ url: env.SUPABASE_URL,
19
+ publishableKeys: { default: env.SUPABASE_PUBLISHABLE_KEY },
20
+ // See worker history: `@supabase/server` eagerly builds `supabaseAdmin`
21
+ // even when `auth: "none"`, so pass the publishable key as a harmless
22
+ // placeholder unless a real service-role key is configured.
23
+ secretKeys: { default: env.SUPABASE_SECRET_KEY ?? env.SUPABASE_PUBLISHABLE_KEY },
24
+ },
25
+ },
26
+ });
27
+ return handler(request);
28
+ },
29
+ };
30
+ //# sourceMappingURL=mcp-api-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-api-handler.js","sourceRoot":"","sources":["../../src/auth/mcp-api-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAMnD,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,KAAK,CAAC,KAAK,CAAC,OAAgB,EAAE,GAAQ,EAAE,IAAmB;QACzD,MAAM,OAAO,GAAG,sBAAsB,CAAC;YACrC,cAAc,EAAE;gBACd,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,KAAK;gBACX,GAAG,EAAE;oBACH,GAAG,EAAE,GAAG,CAAC,YAAY;oBACrB,eAAe,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,wBAAwB,EAAE;oBAC1D,wEAAwE;oBACxE,sEAAsE;oBACtE,4DAA4D;oBAC5D,UAAU,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,mBAAmB,IAAI,GAAG,CAAC,wBAAwB,EAAE;iBACjF;aACF;SACF,CAAC,CAAA;QACF,OAAO,OAAO,CAAC,OAAO,CAAC,CAAA;IACzB,CAAC;CACF,CAAA"}
@@ -0,0 +1,47 @@
1
+ import type { AuthRequest, ClientInfo } from "@cloudflare/workers-oauth-provider";
2
+ export declare class OAuthError extends Error {
3
+ code: string;
4
+ description: string;
5
+ statusCode: number;
6
+ constructor(code: string, description: string, statusCode?: number);
7
+ toResponse(): Response;
8
+ }
9
+ export interface OAuthStateResult {
10
+ stateToken: string;
11
+ }
12
+ export interface ValidateStateResult {
13
+ oauthReqInfo: AuthRequest;
14
+ clearCookie: string;
15
+ }
16
+ export interface BindStateResult {
17
+ setCookie: string;
18
+ }
19
+ export interface CSRFProtectionResult {
20
+ token: string;
21
+ setCookie: string;
22
+ }
23
+ export interface ValidateCSRFResult {
24
+ clearCookie: string;
25
+ }
26
+ export declare function sanitizeText(text: string): string;
27
+ export declare function sanitizeUrl(url: string): string;
28
+ export declare function generateCSRFProtection(): CSRFProtectionResult;
29
+ export declare function validateCSRFToken(formData: FormData, request: Request): ValidateCSRFResult;
30
+ export declare function createOAuthState(oauthReqInfo: AuthRequest, kv: KVNamespace, stateTTL?: number): Promise<OAuthStateResult>;
31
+ export declare function bindStateToSession(stateToken: string): Promise<BindStateResult>;
32
+ export declare function validateOAuthState(request: Request, kv: KVNamespace): Promise<ValidateStateResult>;
33
+ export declare function isClientApproved(request: Request, clientId: string, cookieSecret: string): Promise<boolean>;
34
+ export declare function addApprovedClient(request: Request, clientId: string, cookieSecret: string): Promise<string>;
35
+ export interface ApprovalDialogOptions {
36
+ client: ClientInfo | null;
37
+ server: {
38
+ name: string;
39
+ logo?: string;
40
+ description?: string;
41
+ };
42
+ state: Record<string, unknown>;
43
+ csrfToken: string;
44
+ setCookie: string;
45
+ }
46
+ export declare function renderApprovalDialog(request: Request, options: ApprovalDialogOptions): Response;
47
+ //# sourceMappingURL=oauth-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-utils.d.ts","sourceRoot":"","sources":["../../src/auth/oauth-utils.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAA;AAEjF,qBAAa,UAAW,SAAQ,KAAK;IAE1B,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,MAAM;IACnB,UAAU;gBAFV,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,UAAU,SAAM;IAMzB,UAAU,IAAI,QAAQ;CAMvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,WAAW,CAAA;IACzB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOjD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAqB/C;AAED,wBAAgB,sBAAsB,IAAI,oBAAoB,CAK7D;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,kBAAkB,CAuB1F;AAED,wBAAsB,gBAAgB,CACpC,YAAY,EAAE,WAAW,EACzB,EAAE,EAAE,WAAW,EACf,QAAQ,SAAM,GACb,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AAED,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAUrF;AAED,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,WAAW,GACd,OAAO,CAAC,mBAAmB,CAAC,CAsD9B;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,OAAO,CAAC,CAGlB;AAED,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,CAYjB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAA;IACzB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,GAAG,QAAQ,CAkG/F"}