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.
- package/README.md +55 -5
- package/dist/cli.js +4 -35
- package/dist/cli.js.map +1 -1
- package/dist/client-conformance.d.ts +34 -0
- package/dist/client-conformance.js +379 -0
- package/dist/client-conformance.js.map +1 -0
- package/dist/client-fixtures.d.ts +67 -0
- package/dist/client-fixtures.js +130 -0
- package/dist/client-fixtures.js.map +1 -0
- package/dist/commands/connect.js +11 -1
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/doctor.js +13 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.js +32 -5
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/plan.d.ts +6 -0
- package/dist/commands/plan.js +26 -0
- package/dist/commands/plan.js.map +1 -0
- package/dist/commands/probe.js +1 -0
- package/dist/commands/probe.js.map +1 -1
- package/dist/commands/set-connected-account.d.ts +7 -0
- package/dist/commands/set-connected-account.js +38 -0
- package/dist/commands/set-connected-account.js.map +1 -0
- package/dist/doctor.d.ts +26 -2
- package/dist/doctor.js +127 -16
- package/dist/doctor.js.map +1 -1
- package/dist/google-redirect.d.ts +31 -0
- package/dist/google-redirect.js +479 -0
- package/dist/google-redirect.js.map +1 -0
- package/dist/mcp/server.js +32 -11
- package/dist/mcp/server.js.map +1 -1
- package/dist/program.d.ts +6 -0
- package/dist/program.js +46 -0
- package/dist/program.js.map +1 -0
- package/dist/rail.js +2 -1
- package/dist/rail.js.map +1 -1
- package/dist/status.js +18 -6
- package/dist/status.js.map +1 -1
- package/dist/surface.d.ts +22 -0
- package/dist/surface.js +22 -0
- package/dist/surface.js.map +1 -0
- 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:
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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,
|
|
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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"}
|