authflow-cli 0.2.0 → 0.4.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.
Files changed (42) hide show
  1. package/README.md +55 -5
  2. package/dist/cli.js +4 -35
  3. package/dist/cli.js.map +1 -1
  4. package/dist/client-conformance.d.ts +34 -0
  5. package/dist/client-conformance.js +379 -0
  6. package/dist/client-conformance.js.map +1 -0
  7. package/dist/client-fixtures.d.ts +67 -0
  8. package/dist/client-fixtures.js +130 -0
  9. package/dist/client-fixtures.js.map +1 -0
  10. package/dist/commands/connect.js +11 -1
  11. package/dist/commands/connect.js.map +1 -1
  12. package/dist/commands/doctor.js +13 -3
  13. package/dist/commands/doctor.js.map +1 -1
  14. package/dist/commands/init.js +32 -5
  15. package/dist/commands/init.js.map +1 -1
  16. package/dist/commands/plan.d.ts +6 -0
  17. package/dist/commands/plan.js +26 -0
  18. package/dist/commands/plan.js.map +1 -0
  19. package/dist/commands/probe.js +1 -0
  20. package/dist/commands/probe.js.map +1 -1
  21. package/dist/commands/set-connected-account.d.ts +7 -0
  22. package/dist/commands/set-connected-account.js +38 -0
  23. package/dist/commands/set-connected-account.js.map +1 -0
  24. package/dist/doctor.d.ts +26 -2
  25. package/dist/doctor.js +127 -16
  26. package/dist/doctor.js.map +1 -1
  27. package/dist/google-redirect.d.ts +31 -0
  28. package/dist/google-redirect.js +479 -0
  29. package/dist/google-redirect.js.map +1 -0
  30. package/dist/mcp/server.js +32 -11
  31. package/dist/mcp/server.js.map +1 -1
  32. package/dist/program.d.ts +6 -0
  33. package/dist/program.js +46 -0
  34. package/dist/program.js.map +1 -0
  35. package/dist/rail.js +2 -1
  36. package/dist/rail.js.map +1 -1
  37. package/dist/status.js +18 -6
  38. package/dist/status.js.map +1 -1
  39. package/dist/surface.d.ts +22 -0
  40. package/dist/surface.js +22 -0
  41. package/dist/surface.js.map +1 -0
  42. package/package.json +1 -1
package/README.md CHANGED
@@ -43,11 +43,11 @@ npm run authflow -- --help
43
43
  | Env | Flag | Used by |
44
44
  | --- | --- | --- |
45
45
  | `AUTHFLOW_ISSUER` | `--issuer` | all HTTP commands |
46
- | `AUTHFLOW_ADMIN_API_KEY` | `--admin-key` | `init`, `register`, `connect`, `set-origin`, `status`, `get`, `list`, `rotate-origin-key` (mint with `authflow signup`) |
46
+ | `AUTHFLOW_ADMIN_API_KEY` | `--admin-key` | `init`, `register`, `connect`, `set-connected-account`, `set-origin`, `status`, `get`, `list`, `plan`, `rotate-origin-key` (mint with `authflow signup`) |
47
47
  | `AUTHFLOW_ORIGIN_API_KEY` | `--origin-key` | `mint-header`, `probe`, `set-origin` (for its probe) |
48
48
  | `AUTHFLOW_RESOURCE` / `AUTHFLOW_GATEWAY_URL` | `--gateway-url` / `--receipt` | curl hint on `mint-header` |
49
49
 
50
- Issuer is the Origin Protocol issuer origin, with no trailing slash: staging is `https://staging.rails.authflow.ai`, local is typically `https://localhost:7175`. For an already-registered resource it is the `issuer` field of the receipt. See [tenant quickstart §1](../../docs/onboarding/tenant-quickstart.md#environments-and-hostnames).
50
+ Issuer is the Origin Protocol issuer origin, with no trailing slash: staging is `https://staging.rails.authflow.ai`, local is typically `https://localhost:7062`. For an already-registered resource it is the `issuer` field of the receipt. See [tenant quickstart §1](../../docs/onboarding/tenant-quickstart.md#environments-and-hostnames).
51
51
 
52
52
  ## Commands
53
53
 
@@ -84,9 +84,13 @@ The receipt's `pending_requirements` lists what is still outstanding:
84
84
  | Requirement | Clear it with |
85
85
  | --- | --- |
86
86
  | `origin_base_uri` | `authflow set-origin --slug video --origin-url https://…` |
87
- | `connected_account_id` | `authflow connect --slug video` |
87
+ | `connected_account_id` | `authflow connect --slug video` (hosted KYC) or `authflow set-connected-account --slug video --account acct_…` (you already have the id) |
88
88
  | `stripe_onboarding_complete` | finish Stripe's form, then `authflow connect --slug video` again |
89
89
 
90
+ Re-running `init` against a slug this admin key already owns resumes it: `409 slug_owned_by_caller`
91
+ is not a dead end. `--connected-account` and `--origin-url` then attach instead of failing. A slug
92
+ owned by someone else still returns `slug_taken`.
93
+
90
94
  The resource goes live once the list is empty. Add `--connect` to `init` to get the Stripe
91
95
  onboarding link from the first command instead of running `connect` separately.
92
96
 
@@ -110,6 +114,7 @@ complete KYC.
110
114
  ```bash
111
115
  authflow connect --slug video
112
116
  authflow connect --slug video --wait # poll until Stripe reports the account can take charges
117
+ authflow connect --slug video --connected-account acct_… # attach an account you already have
113
118
  ```
114
119
 
115
120
  Account links are single-use and expire in minutes. Re-running is the expected way to get a fresh
@@ -117,6 +122,18 @@ one: it resumes the same Stripe account rather than creating another, so retryin
117
122
  abandoned accounts behind. Run it again after the form is submitted to confirm `charges_enabled` and
118
123
  promote the resource.
119
124
 
125
+ `--connected-account` skips hosted KYC and is the same PUT as `authflow set-connected-account`.
126
+
127
+ ### `authflow set-connected-account`
128
+
129
+ Attach a Stripe Standard account id the platform already has. This is the CLI twin of
130
+ `authflow_set_connected_account` and of `PUT /admin/resources/{slug}/connected-account`. Use it when
131
+ `connect` would otherwise start a new hosted onboarding you do not need.
132
+
133
+ ```bash
134
+ authflow set-connected-account --slug video --account acct_…
135
+ ```
136
+
120
137
  ### `authflow set-origin`
121
138
 
122
139
  Points a draft at its origin, promoting it to live if nothing else is pending, then runs the
@@ -155,15 +172,32 @@ gateway: https://staging.rails.authflow.ai/video/mcp
155
172
  no origin — the gateway has nowhere to forward to
156
173
  next: authflow set-origin --slug video --origin-url https://your-origin.example
157
174
  [ ] Stripe connected
158
- next: authflow connect --slug video
175
+ no connected account — nothing can be charged
176
+ next: authflow connect --slug video (or authflow set-connected-account --slug video --account acct_…)
177
+ [ ] Stripe onboarding complete
178
+ no connected account — attach one before Stripe can confirm charges
179
+ next: authflow connect --slug video (or authflow set-connected-account --slug video --account acct_…)
159
180
  ```
160
181
 
182
+ "Stripe onboarding complete" is ticked only when this resource has an account attached **and** Stripe
183
+ has not listed `stripe_onboarding_complete` as pending. No account is not "charges enabled."
184
+
161
185
  Use it to resume an onboarding whose earlier steps may or may not have finished — a deploy that got
162
186
  backgrounded, a terminal that was closed. `doctor` answers "is the rail up", which is a different
163
187
  question: the rail can be perfectly healthy while a resource sits half-configured and rejects every
164
188
  request. Exit code is non-zero until the resource can take paid traffic, so it works as a deploy
165
189
  gate. `--json` emits the report for scripting.
166
190
 
191
+ ### `authflow plan`
192
+
193
+ Prints the same framework-specific origin integration plan as `authflow_integration_plan`.
194
+
195
+ ```bash
196
+ authflow plan --slug video --framework dotnet
197
+ ```
198
+
199
+ `--framework` is one of `dotnet`, `typescript`, `python`, or `proxy`.
200
+
167
201
  ### `authflow get` / `authflow list`
168
202
 
169
203
  ```bash
@@ -203,6 +237,19 @@ Run this first when anything looks broken. It separates the two failures that ar
203
237
  from the outside: a hostname that is not bound to the rail returns `404` on every path including
204
238
  `/healthz`, which reads exactly like a broken endpoint. Exit code is non-zero on any failure.
205
239
 
240
+ `--client cursor|claude|chatgpt|all` additionally replays that MCP client's real OAuth registration
241
+ against the authorization server (the failure that presents as "this server does not work in
242
+ Cursor"). `--dry-run` is discovery only and does not POST DCR. Live DCR registrations are stamped
243
+ `authflow-cli doctor-connect` so a cleanup sweep can reclaim them.
244
+
245
+ A `400` from the rail's DCR endpoint is the interesting result: paste `error=` and
246
+ `error_description=` verbatim into a rail bug. Do not work around it client-side.
247
+
248
+ ```bash
249
+ authflow doctor --issuer https://staging.rails.authflow.ai --slug video --client cursor
250
+ authflow doctor --client all --dry-run
251
+ ```
252
+
206
253
  ### `authflow register`
207
254
 
208
255
  ```bash
@@ -264,9 +311,12 @@ Asks the rail to probe the registered origin with valid and deliberately broken
264
311
 
265
312
  ```bash
266
313
  authflow probe
314
+ authflow probe --slug video
267
315
  ```
268
316
 
269
- Renders a pass/fail table. Exit code is non-zero when any check fails or the origin is unreachable.
317
+ The origin API key is bound to one resource, so `--slug` is optional and ignored. It exists so a
318
+ documented `authflow probe --slug …` does not fail. Renders a pass/fail table. Exit code is
319
+ non-zero when any check fails or the origin is unreachable.
270
320
 
271
321
  ### `authflow scaffold`
272
322
 
package/dist/cli.js CHANGED
@@ -1,39 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { Command } from "commander";
3
- import { connectCommand } from "./commands/connect.js";
4
- import { doctorCommand } from "./commands/doctor.js";
5
- import { getCommand } from "./commands/get.js";
6
- import { initCommand } from "./commands/init.js";
7
- import { listCommand } from "./commands/list.js";
8
- import { mcpCommand } from "./commands/mcp.js";
9
- import { signupCommand } from "./commands/signup.js";
10
- import { registerCommand } from "./commands/register.js";
11
- import { rotateOriginKeyCommand } from "./commands/rotate-origin-key.js";
12
- import { setOriginCommand } from "./commands/set-origin.js";
13
- import { mintHeaderCommand } from "./commands/mint-header.js";
14
- import { probeCommand } from "./commands/probe.js";
15
- import { scaffoldCommand } from "./commands/scaffold.js";
16
- import { statusCommand } from "./commands/status.js";
17
- const program = new Command();
18
- program
19
- .name("authflow")
20
- .description("Authflow Rail onboarding CLI")
21
- .version("0.1.0");
22
- signupCommand(program);
23
- initCommand(program);
24
- registerCommand(program);
25
- connectCommand(program);
26
- setOriginCommand(program);
27
- statusCommand(program);
28
- getCommand(program);
29
- listCommand(program);
30
- rotateOriginKeyCommand(program);
31
- mintHeaderCommand(program);
32
- probeCommand(program);
33
- doctorCommand(program);
34
- scaffoldCommand(program);
35
- mcpCommand(program);
36
- program.parseAsync(process.argv).catch((err) => {
2
+ import { createProgram } from "./program.js";
3
+ createProgram()
4
+ .parseAsync(process.argv)
5
+ .catch((err) => {
37
6
  const message = err instanceof Error ? err.message : String(err);
38
7
  console.error(message);
39
8
  process.exit(1);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,8BAA8B,CAAC;KAC3C,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,aAAa,CAAC,OAAO,CAAC,CAAC;AACvB,WAAW,CAAC,OAAO,CAAC,CAAC;AACrB,eAAe,CAAC,OAAO,CAAC,CAAC;AACzB,cAAc,CAAC,OAAO,CAAC,CAAC;AACxB,gBAAgB,CAAC,OAAO,CAAC,CAAC;AAC1B,aAAa,CAAC,OAAO,CAAC,CAAC;AACvB,UAAU,CAAC,OAAO,CAAC,CAAC;AACpB,WAAW,CAAC,OAAO,CAAC,CAAC;AACrB,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,iBAAiB,CAAC,OAAO,CAAC,CAAC;AAC3B,YAAY,CAAC,OAAO,CAAC,CAAC;AACtB,aAAa,CAAC,OAAO,CAAC,CAAC;AACvB,eAAe,CAAC,OAAO,CAAC,CAAC;AACzB,UAAU,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IACtD,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,aAAa,EAAE;KACZ,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC;KACxB,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Replay what real MCP clients send at the authorization server, from a tenant's point of view.
3
+ *
4
+ * This exists because of a real connect failure: tenant #1 could not connect from Cursor because
5
+ * our DCR endpoint rejected Cursor's actual redirect_uris (private-use `cursor://` scheme,
6
+ * omitted application_type, three callbacks at once). We only found out because they filed a bug.
7
+ * Nothing on the rail tested against what clients actually send. These checks do.
8
+ *
9
+ * DCR replay creates a real OAuth client row. Registrations are stamped with
10
+ * `client_name` prefix `authflow-cli doctor-connect` so a cleanup sweep can reclaim them; this
11
+ * harness does not delete them. Use `--dry-run` to skip POSTs.
12
+ */
13
+ import { type ClientId } from "./client-fixtures.js";
14
+ import { type DoctorReport } from "./doctor.js";
15
+ export type ClientConformanceOptions = {
16
+ issuer: string;
17
+ clients: ClientId[];
18
+ dryRun?: boolean;
19
+ slug?: string;
20
+ };
21
+ export type AuthorizationServerMetadata = {
22
+ issuer?: string;
23
+ registration_endpoint?: string;
24
+ client_id_metadata_document_supported?: boolean;
25
+ token_endpoint_auth_methods_supported?: unknown;
26
+ code_challenge_methods_supported?: unknown;
27
+ authorization_response_iss_parameter_supported?: unknown;
28
+ };
29
+ export declare function runDoctorAndConnect(issuer: string, options?: {
30
+ slug?: string;
31
+ client?: string;
32
+ dryRun?: boolean;
33
+ }): Promise<DoctorReport>;
34
+ export declare function runClientConformance(options: ClientConformanceOptions): Promise<DoctorReport>;
@@ -0,0 +1,379 @@
1
+ /**
2
+ * Replay what real MCP clients send at the authorization server, from a tenant's point of view.
3
+ *
4
+ * This exists because of a real connect failure: tenant #1 could not connect from Cursor because
5
+ * our DCR endpoint rejected Cursor's actual redirect_uris (private-use `cursor://` scheme,
6
+ * omitted application_type, three callbacks at once). We only found out because they filed a bug.
7
+ * Nothing on the rail tested against what clients actually send. These checks do.
8
+ *
9
+ * DCR replay creates a real OAuth client row. Registrations are stamped with
10
+ * `client_name` prefix `authflow-cli doctor-connect` so a cleanup sweep can reclaim them; this
11
+ * harness does not delete them. Use `--dry-run` to skip POSTs.
12
+ */
13
+ import { chatgptCallbackCimdDocument, chatgptDcrVariants, chatgptStableCimdDocument, cursorDcrVariants, parseClientOption, probeClientName, } from "./client-fixtures.js";
14
+ import { checkGatewayChallenge, fetchOrNull, runDoctor, } from "./doctor.js";
15
+ import { checkGoogleRedirectUri } from "./google-redirect.js";
16
+ const metadataPath = "/.well-known/oauth-authorization-server";
17
+ const cimdNoneRemedy = 'Claude uses CIMD only when the AS advertises both client_id_metadata_document_supported: true ' +
18
+ 'AND "none" in token_endpoint_auth_methods_supported. Missing "none" means Claude silently ' +
19
+ "ignores CIMD, with no error pointing at the cause.";
20
+ const cimdFetchAdvisoryDetail = "Advisory — not proven. This CLI cannot host a publicly-reachable Client ID Metadata Document " +
21
+ "(HTTPS URL with a path), which is what Claude sends as client_id. AS advertisement of CIMD " +
22
+ "support is checked separately. A pass here does not mean the rail fetches, caches, or " +
23
+ "validates a document.";
24
+ const cimdFetchAdvisoryRemedy = "To fully prove CIMD, host a document at an HTTPS URL whose path is non-empty, use that URL as " +
25
+ "client_id at /authorize, and confirm the AS fetches it and matches redirect_uris. Until then " +
26
+ "this check is advisory and does not fail the run.";
27
+ const chatgptCimdFetchAdvisoryDetail = `Advisory — not proven. ChatGPT's CIMD client_id is ${chatgptStableCimdDocument} when this AS ` +
28
+ `advertises RFC 9207 issuer identification, or ${chatgptCallbackCimdDocument} otherwise. This ` +
29
+ "CLI does not fetch those documents on the AS's behalf. A pass here does not mean the rail " +
30
+ "fetches, caches, or validates ChatGPT's document.";
31
+ const dcr400Footer = "A DCR 400 is the interesting result. Paste error= and error_description= verbatim into a rail " +
32
+ "bug; do not work around it client-side.";
33
+ export async function runDoctorAndConnect(issuer, options = {}) {
34
+ const report = await runDoctor(issuer, options.slug);
35
+ if (!options.client) {
36
+ return report;
37
+ }
38
+ const clients = parseClientOption(options.client);
39
+ const reachable = report.checks.find((c) => c.name === "issuer reachable");
40
+ if (reachable && !reachable.pass) {
41
+ return { ...report, clients, dryRun: options.dryRun };
42
+ }
43
+ const extra = await runClientConformance({
44
+ issuer: report.issuer,
45
+ clients,
46
+ dryRun: options.dryRun,
47
+ });
48
+ const google = await checkGoogleRedirectUri(report.issuer, {
49
+ slug: options.slug,
50
+ dryRun: options.dryRun,
51
+ });
52
+ const checks = [...report.checks, ...extra.checks, ...google];
53
+ return {
54
+ ...report,
55
+ clients,
56
+ dryRun: extra.dryRun,
57
+ checks,
58
+ pass: checks.every((c) => c.pass),
59
+ };
60
+ }
61
+ export async function runClientConformance(options) {
62
+ const base = options.issuer.trim().replace(/\/+$/, "");
63
+ const dryRun = !!options.dryRun;
64
+ const checks = [];
65
+ const metadata = await fetchMetadata(base);
66
+ checks.push(metadata.check);
67
+ if (!metadata.document) {
68
+ return {
69
+ issuer: base,
70
+ slug: options.slug,
71
+ clients: options.clients,
72
+ dryRun,
73
+ pass: false,
74
+ checks,
75
+ };
76
+ }
77
+ const document = metadata.document;
78
+ const selected = new Set(options.clients);
79
+ checks.push(checkPkceS256(document));
80
+ checks.push(checkRfc9207Iss(document));
81
+ if (selected.has("cursor")) {
82
+ checks.push(checkRegistrationEndpoint(document, "cursor"));
83
+ }
84
+ if (selected.has("chatgpt")) {
85
+ checks.push(checkRegistrationEndpoint(document, "chatgpt"));
86
+ checks.push(...checkChatgptCimd(document));
87
+ }
88
+ if (selected.has("claude")) {
89
+ checks.push(...checkClaudeCimd(document));
90
+ }
91
+ if (!dryRun) {
92
+ const endpoint = resolveRegistrationEndpoint(base, document.registration_endpoint);
93
+ if (selected.has("cursor") && endpoint) {
94
+ for (const variant of cursorDcrVariants) {
95
+ checks.push(await replayDcr(endpoint, "cursor", variant));
96
+ }
97
+ }
98
+ if (selected.has("chatgpt") && endpoint) {
99
+ for (const variant of chatgptDcrVariants) {
100
+ checks.push(await replayDcr(endpoint, "chatgpt", variant));
101
+ }
102
+ }
103
+ }
104
+ if (options.slug) {
105
+ checks.push(await checkGatewayChallenge(base, options.slug));
106
+ }
107
+ return {
108
+ issuer: base,
109
+ slug: options.slug,
110
+ clients: options.clients,
111
+ dryRun,
112
+ pass: checks.every((c) => c.pass),
113
+ checks,
114
+ };
115
+ }
116
+ async function fetchMetadata(base) {
117
+ const url = `${base}${metadataPath}`;
118
+ const response = await fetchOrNull(url);
119
+ if (!response.ok && response.error) {
120
+ return {
121
+ check: {
122
+ name: "authorization server metadata",
123
+ pass: false,
124
+ detail: `${url} could not be reached: ${response.error}`,
125
+ remedy: "Check the hostname and DNS. MCP clients cannot start OAuth without this document.",
126
+ },
127
+ };
128
+ }
129
+ if (response.status !== 200) {
130
+ return {
131
+ check: {
132
+ name: "authorization server metadata",
133
+ pass: false,
134
+ detail: `${url} returned ${response.status ?? response.error}, expected 200.`,
135
+ remedy: "MCP clients discover DCR and CIMD from this document. File a rail bug.",
136
+ },
137
+ };
138
+ }
139
+ let document;
140
+ try {
141
+ document = JSON.parse(response.body ?? "");
142
+ }
143
+ catch {
144
+ return {
145
+ check: {
146
+ name: "authorization server metadata",
147
+ pass: false,
148
+ detail: `${url} did not return JSON.`,
149
+ remedy: "File this against the rail.",
150
+ },
151
+ };
152
+ }
153
+ return {
154
+ check: {
155
+ name: "authorization server metadata",
156
+ pass: true,
157
+ detail: `${url} returned 200.`,
158
+ },
159
+ document,
160
+ };
161
+ }
162
+ function checkPkceS256(document) {
163
+ const methods = asStringArray(document.code_challenge_methods_supported);
164
+ if (methods.includes("S256")) {
165
+ return {
166
+ name: "PKCE S256",
167
+ pass: true,
168
+ detail: `code_challenge_methods_supported includes S256 (${methods.join(", ")}).`,
169
+ };
170
+ }
171
+ return {
172
+ name: "PKCE S256",
173
+ pass: false,
174
+ detail: `code_challenge_methods_supported is [${methods.join(", ")}], expected to include S256.`,
175
+ remedy: "MCP clients require S256 PKCE. Advertise it in AS metadata.",
176
+ };
177
+ }
178
+ function checkRfc9207Iss(document) {
179
+ if (document.authorization_response_iss_parameter_supported === true) {
180
+ return {
181
+ name: "RFC 9207 iss parameter",
182
+ pass: true,
183
+ detail: "authorization_response_iss_parameter_supported is true.",
184
+ };
185
+ }
186
+ return {
187
+ name: "RFC 9207 iss parameter",
188
+ pass: false,
189
+ detail: `authorization_response_iss_parameter_supported is ${JSON.stringify(document.authorization_response_iss_parameter_supported)}, expected true.`,
190
+ remedy: "Advertise authorization_response_iss_parameter_supported: true and return iss on every " +
191
+ "authorization response. ChatGPT uses this flag to choose connector_platform_oauth_redirect " +
192
+ "versus connector/oauth/{callback_id}; a regression silently changes the redirect URI.",
193
+ };
194
+ }
195
+ function checkRegistrationEndpoint(document, client) {
196
+ const endpoint = (document.registration_endpoint ?? "").trim();
197
+ const name = `${client}: registration_endpoint`;
198
+ if (endpoint) {
199
+ return {
200
+ name,
201
+ pass: true,
202
+ detail: `registration_endpoint is ${endpoint}.`,
203
+ };
204
+ }
205
+ const remedy = client === "cursor"
206
+ ? "Cursor is DCR-only and has no CIMD timeline. Without registration_endpoint in AS metadata, Cursor cannot connect at all."
207
+ : "ChatGPT prefers CIMD when advertised, but still uses DCR when the plugin builder chooses it or CIMD is unavailable. Without registration_endpoint that fallback cannot register.";
208
+ return {
209
+ name,
210
+ pass: false,
211
+ detail: "AS metadata has no registration_endpoint.",
212
+ remedy,
213
+ };
214
+ }
215
+ function checkClaudeCimd(document) {
216
+ const advertised = document.client_id_metadata_document_supported === true;
217
+ const methods = asStringArray(document.token_endpoint_auth_methods_supported);
218
+ const hasNone = methods.includes("none");
219
+ const advertisement = advertised
220
+ ? {
221
+ name: "claude: CIMD advertisement",
222
+ pass: true,
223
+ detail: "client_id_metadata_document_supported is true.",
224
+ }
225
+ : {
226
+ name: "claude: CIMD advertisement",
227
+ pass: false,
228
+ detail: `client_id_metadata_document_supported is ${JSON.stringify(document.client_id_metadata_document_supported)}, expected true.`,
229
+ remedy: cimdNoneRemedy,
230
+ };
231
+ const noneMethod = hasNone
232
+ ? {
233
+ name: "claude: token endpoint auth none",
234
+ pass: true,
235
+ detail: `token_endpoint_auth_methods_supported includes "none" (${methods.join(", ")}).`,
236
+ }
237
+ : {
238
+ name: "claude: token endpoint auth none",
239
+ pass: false,
240
+ detail: `token_endpoint_auth_methods_supported is [${methods.join(", ")}], expected to include "none".`,
241
+ remedy: cimdNoneRemedy,
242
+ };
243
+ const fetchValidate = {
244
+ name: "claude: CIMD fetch-and-validate",
245
+ pass: true,
246
+ detail: cimdFetchAdvisoryDetail,
247
+ remedy: cimdFetchAdvisoryRemedy,
248
+ };
249
+ return [advertisement, noneMethod, fetchValidate];
250
+ }
251
+ function checkChatgptCimd(document) {
252
+ const advertised = document.client_id_metadata_document_supported === true;
253
+ const advertisement = advertised
254
+ ? {
255
+ name: "chatgpt: CIMD advertisement",
256
+ pass: true,
257
+ detail: "client_id_metadata_document_supported is true. ChatGPT prefers CIMD when advertised " +
258
+ `(stable document ${chatgptStableCimdDocument} if RFC 9207 iss is also true).`,
259
+ }
260
+ : {
261
+ name: "chatgpt: CIMD advertisement",
262
+ pass: false,
263
+ detail: `client_id_metadata_document_supported is ${JSON.stringify(document.client_id_metadata_document_supported)}, expected true.`,
264
+ remedy: "ChatGPT prefers CIMD when the AS advertises client_id_metadata_document_supported: true. " +
265
+ "Without it ChatGPT falls back to DCR (or cannot register if registration_endpoint is also missing).",
266
+ };
267
+ const fetchValidate = {
268
+ name: "chatgpt: CIMD fetch-and-validate",
269
+ pass: true,
270
+ detail: chatgptCimdFetchAdvisoryDetail,
271
+ remedy: cimdFetchAdvisoryRemedy,
272
+ };
273
+ return [advertisement, fetchValidate];
274
+ }
275
+ async function replayDcr(endpoint, client, variant) {
276
+ const sent = variant.payload.redirect_uris;
277
+ const body = {
278
+ ...variant.payload,
279
+ client_name: probeClientName(client, variant.id),
280
+ };
281
+ const response = await fetchOrNull(endpoint, {
282
+ method: "POST",
283
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
284
+ body: JSON.stringify(body),
285
+ });
286
+ if (!response.ok && response.error) {
287
+ return {
288
+ name: variant.checkName,
289
+ pass: false,
290
+ detail: `POST ${endpoint} could not be reached: ${response.error}`,
291
+ remedy: "The registration_endpoint was advertised but did not respond. File a rail bug.",
292
+ };
293
+ }
294
+ if (response.status === 400) {
295
+ return {
296
+ name: variant.checkName,
297
+ pass: false,
298
+ detail: `DCR 400: ${formatDcrError(response.body)}`,
299
+ remedy: dcr400Footer,
300
+ };
301
+ }
302
+ if (response.status !== 201) {
303
+ return {
304
+ name: variant.checkName,
305
+ pass: false,
306
+ detail: `POST ${endpoint} returned ${response.status ?? response.error}, expected 201. ${formatDcrError(response.body)}`,
307
+ remedy: "DCR must succeed with 201 for this client's real payload. File a rail bug.",
308
+ };
309
+ }
310
+ let echoed;
311
+ try {
312
+ echoed = JSON.parse(response.body ?? "");
313
+ }
314
+ catch {
315
+ return {
316
+ name: variant.checkName,
317
+ pass: false,
318
+ detail: "DCR returned 201 but the body was not JSON.",
319
+ remedy: "File this against the rail.",
320
+ };
321
+ }
322
+ const dropped = missingRedirectUris(sent, echoed.redirect_uris);
323
+ if (dropped.length > 0) {
324
+ return {
325
+ name: variant.checkName,
326
+ pass: false,
327
+ detail: `DCR returned 201 but dropped redirect_uris: ${dropped.join(", ")}. ` +
328
+ `Sent [${sent.join(", ")}]; echoed ${JSON.stringify(echoed.redirect_uris)}.`,
329
+ remedy: "A silently dropped callback fails later at /authorize with a confusing redirect_uri " +
330
+ "mismatch. Echo every registered URI.",
331
+ };
332
+ }
333
+ const clientId = typeof echoed.client_id === "string" ? echoed.client_id : "(none)";
334
+ return {
335
+ name: variant.checkName,
336
+ pass: true,
337
+ detail: `DCR 201; client_id=${clientId}; redirect_uris echoed in full. Stamped client_name for cleanup sweep.`,
338
+ };
339
+ }
340
+ function missingRedirectUris(sent, echoed) {
341
+ if (!Array.isArray(echoed)) {
342
+ return [...sent];
343
+ }
344
+ const got = new Set(echoed.map((value) => String(value)));
345
+ return sent.filter((uri) => !got.has(uri));
346
+ }
347
+ function formatDcrError(raw) {
348
+ if (!raw) {
349
+ return "error=(empty) error_description=(empty)";
350
+ }
351
+ try {
352
+ const body = JSON.parse(raw);
353
+ const error = body.error !== undefined ? String(body.error) : "(none)";
354
+ const description = body.error_description !== undefined ? String(body.error_description) : "(none)";
355
+ return `error=${error} error_description=${description}`;
356
+ }
357
+ catch {
358
+ return raw.slice(0, 300);
359
+ }
360
+ }
361
+ function resolveRegistrationEndpoint(base, value) {
362
+ const endpoint = (value ?? "").trim();
363
+ if (!endpoint) {
364
+ return undefined;
365
+ }
366
+ try {
367
+ return new URL(endpoint, `${base}/`).href;
368
+ }
369
+ catch {
370
+ return undefined;
371
+ }
372
+ }
373
+ function asStringArray(value) {
374
+ if (!Array.isArray(value)) {
375
+ return [];
376
+ }
377
+ return value.map((entry) => String(entry));
378
+ }
379
+ //# sourceMappingURL=client-conformance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-conformance.js","sourceRoot":"","sources":["../src/client-conformance.ts"],"names":[],"mappings":"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"}