authflow-cli 0.1.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/LICENSE +201 -0
- package/README.md +260 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +33 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/connect.d.ts +7 -0
- package/dist/commands/connect.js +84 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +16 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +12 -0
- package/dist/commands/init.js +182 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/mcp.d.ts +2 -0
- package/dist/commands/mcp.js +24 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/mint-header.d.ts +2 -0
- package/dist/commands/mint-header.js +69 -0
- package/dist/commands/mint-header.js.map +1 -0
- package/dist/commands/probe.d.ts +2 -0
- package/dist/commands/probe.js +49 -0
- package/dist/commands/probe.js.map +1 -0
- package/dist/commands/register.d.ts +2 -0
- package/dist/commands/register.js +78 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/commands/scaffold.d.ts +2 -0
- package/dist/commands/scaffold.js +72 -0
- package/dist/commands/scaffold.js.map +1 -0
- package/dist/commands/set-origin.d.ts +6 -0
- package/dist/commands/set-origin.js +38 -0
- package/dist/commands/set-origin.js.map +1 -0
- package/dist/commands/signup.d.ts +2 -0
- package/dist/commands/signup.js +53 -0
- package/dist/commands/signup.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.js +56 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +12 -0
- package/dist/constants.js +13 -0
- package/dist/constants.js.map +1 -0
- package/dist/doctor.d.ts +24 -0
- package/dist/doctor.js +230 -0
- package/dist/doctor.js.map +1 -0
- package/dist/integration.d.ts +44 -0
- package/dist/integration.js +247 -0
- package/dist/integration.js.map +1 -0
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.js +362 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/rail.d.ts +80 -0
- package/dist/rail.js +115 -0
- package/dist/rail.js.map +1 -0
- package/dist/secrets.d.ts +28 -0
- package/dist/secrets.js +117 -0
- package/dist/secrets.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { apiJson, bearer, requireAdminKey, requireIssuer } from "../config.js";
|
|
3
|
+
import { describePending, startConnect } from "../rail.js";
|
|
4
|
+
import { originApiKeyConfigKey, secretTargets, writeOriginApiKey } from "../secrets.js";
|
|
5
|
+
/**
|
|
6
|
+
* One command from nothing to a registered, configured origin.
|
|
7
|
+
*
|
|
8
|
+
* The individual steps already existed as `register`, `scaffold`, and `probe`. They are wrapped here
|
|
9
|
+
* because the gaps between them were where onboarding actually stalled: neither the origin URL nor a
|
|
10
|
+
* Stripe account is usually available at registration time, and requiring either one forced a stop
|
|
11
|
+
* at the very first command. Both are now deferrable, so a creator can register, price, take the
|
|
12
|
+
* receipt, and build against it before they have a company bank account — and the receipt's
|
|
13
|
+
* `pending_requirements` is what tells them what is still outstanding.
|
|
14
|
+
*/
|
|
15
|
+
export function initCommand(program) {
|
|
16
|
+
program
|
|
17
|
+
.command("init")
|
|
18
|
+
.description("Register a resource, store its secret, and print ready-to-paste origin config")
|
|
19
|
+
.requiredOption("--slug <slug>", "URL segment for the resource, e.g. video")
|
|
20
|
+
.option("--connected-account <acct>", "Your Stripe Standard account id (acct_...). Omit to connect Stripe later")
|
|
21
|
+
.option("--connect", "Immediately start Stripe onboarding and print the hosted link")
|
|
22
|
+
.option("--display-name <name>", "Human-readable name shown to subscribers")
|
|
23
|
+
.option("--currency <code>", "ISO currency code", "usd")
|
|
24
|
+
.option("--price <minor>", "Subscription price in minor units, e.g. 900 for $9.00", "900")
|
|
25
|
+
.option("--credits <n>", "Credits granted per billing period", "12")
|
|
26
|
+
.option("--origin-url <url>", "Origin base URI, if you already know it. Omit to register a draft")
|
|
27
|
+
.option("--integration <kind>", "Config to print: dotnet | proxy", "dotnet")
|
|
28
|
+
.option("--issuer <url>", "Authflow issuer (default: AUTHFLOW_ISSUER)")
|
|
29
|
+
.option("--admin-key <key>", "Admin API key (default: AUTHFLOW_ADMIN_API_KEY)")
|
|
30
|
+
.option("-o, --out <path>", "Receipt output path", "receipt.json")
|
|
31
|
+
.option("--write-secrets <target>", `Secret store (${secretTargets.join(" | ")})`, "user-secrets")
|
|
32
|
+
.option("--secret-destination <value>", "Project dir, env-file path, or Key Vault name")
|
|
33
|
+
.option("--no-probe", "Skip the conformance probe even when the origin URL is known")
|
|
34
|
+
.action(async (opts) => {
|
|
35
|
+
const issuer = requireIssuer(opts.issuer);
|
|
36
|
+
const adminKey = requireAdminKey(opts.adminKey);
|
|
37
|
+
const target = parseTarget(opts.writeSecrets);
|
|
38
|
+
const draft = {
|
|
39
|
+
slug: opts.slug,
|
|
40
|
+
display_name: opts.displayName ?? opts.slug,
|
|
41
|
+
origin_base_uri: opts.originUrl ?? null,
|
|
42
|
+
connected_account_id: opts.connectedAccount ?? null,
|
|
43
|
+
currency: opts.currency,
|
|
44
|
+
price_amount_minor: Number(opts.price),
|
|
45
|
+
credits_per_period: Number(opts.credits),
|
|
46
|
+
};
|
|
47
|
+
const { body: receipt } = await apiJson(`${issuer}/admin/resources`, {
|
|
48
|
+
method: "POST",
|
|
49
|
+
headers: {
|
|
50
|
+
...bearer(adminKey),
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
Accept: "application/json",
|
|
53
|
+
},
|
|
54
|
+
body: JSON.stringify(draft),
|
|
55
|
+
expectedStatuses: [201],
|
|
56
|
+
});
|
|
57
|
+
const originApiKey = receipt.origin_api_key;
|
|
58
|
+
let storedAt;
|
|
59
|
+
if (originApiKey) {
|
|
60
|
+
storedAt = await writeOriginApiKey({
|
|
61
|
+
target,
|
|
62
|
+
value: originApiKey,
|
|
63
|
+
destination: opts.secretDestination,
|
|
64
|
+
});
|
|
65
|
+
receipt.origin_api_key = `<stored in ${storedAt}>`;
|
|
66
|
+
}
|
|
67
|
+
await writeFile(opts.out, `${JSON.stringify(receipt, null, 2)}\n`, "utf8");
|
|
68
|
+
// Readiness is the rail's call, not something to re-derive from which flags were passed:
|
|
69
|
+
// a resource with both an origin and a Stripe account is still a draft until Stripe reports
|
|
70
|
+
// charges_enabled, and only the receipt knows that.
|
|
71
|
+
const pending = receipt.pending_requirements ?? [];
|
|
72
|
+
const status = receipt.status ?? (pending.length === 0 ? "live" : "draft");
|
|
73
|
+
console.log(`Registered ${receipt.slug} (${status}).`);
|
|
74
|
+
console.log(` gateway URL: ${receipt.gateway_url ?? "-"}`);
|
|
75
|
+
console.log(` receipt: ${opts.out}`);
|
|
76
|
+
if (storedAt) {
|
|
77
|
+
console.log(` secret: ${storedAt}`);
|
|
78
|
+
}
|
|
79
|
+
console.log("");
|
|
80
|
+
printOriginConfig(opts.integration, receipt, storedAt !== undefined);
|
|
81
|
+
if (opts.connect && !opts.connectedAccount) {
|
|
82
|
+
console.log("");
|
|
83
|
+
await printConnectLink(issuer, adminKey, opts.slug);
|
|
84
|
+
}
|
|
85
|
+
if (pending.length > 0) {
|
|
86
|
+
console.log("");
|
|
87
|
+
console.log("The resource is a draft: registered and priced, but not yet routable.");
|
|
88
|
+
console.log("Outstanding before go-live:");
|
|
89
|
+
console.log("");
|
|
90
|
+
for (const requirement of pending) {
|
|
91
|
+
console.log(` ${requirement}`);
|
|
92
|
+
console.log(` ${describePending(requirement, opts.slug)}`);
|
|
93
|
+
}
|
|
94
|
+
console.log("");
|
|
95
|
+
console.log("Order does not matter. The resource goes live once nothing is left.");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (opts.probe === false || !originApiKey) {
|
|
99
|
+
console.log("");
|
|
100
|
+
console.log("Verify fail-closed behaviour once the origin is up: authflow probe");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
console.log("");
|
|
104
|
+
console.log("Running the conformance probe against the origin...");
|
|
105
|
+
await runProbe(issuer, originApiKey);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Kicks off Connect during init so a creator who has no Stripe account yet still leaves the first
|
|
110
|
+
* command holding the one link they need, rather than a note to run another command later.
|
|
111
|
+
*/
|
|
112
|
+
async function printConnectLink(issuer, adminKey, slug) {
|
|
113
|
+
const result = await startConnect({ issuer, adminKey }, slug);
|
|
114
|
+
if (result.charges_enabled) {
|
|
115
|
+
console.log(`Stripe is already connected (${result.connected_account_id}) and can take charges.`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
console.log(`Stripe onboarding (${result.connected_account_id}) — open this and complete the form:`);
|
|
119
|
+
console.log("");
|
|
120
|
+
console.log(` ${result.onboarding_url}`);
|
|
121
|
+
console.log("");
|
|
122
|
+
console.log(`The link is single-use. For a fresh one: authflow connect --slug ${slug}`);
|
|
123
|
+
}
|
|
124
|
+
function printOriginConfig(integration, receipt, secretStored) {
|
|
125
|
+
const issuer = receipt.issuer ?? "";
|
|
126
|
+
const resource = receipt.resource ?? receipt.gateway_url ?? "";
|
|
127
|
+
if (integration === "proxy") {
|
|
128
|
+
console.log("Origin proxy sidecar config:");
|
|
129
|
+
console.log("");
|
|
130
|
+
console.log(` Authflow__Issuer=${issuer}`);
|
|
131
|
+
console.log(` Authflow__Resource=${resource}`);
|
|
132
|
+
console.log(" Authflow__UpstreamBaseUri=<your MCP server>");
|
|
133
|
+
console.log("");
|
|
134
|
+
console.log(" # Required before running more than one replica:");
|
|
135
|
+
console.log(" # Authflow__NonceCacheRedis=<redis connection string>");
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
console.log("Add the package and configuration to your MCP server:");
|
|
139
|
+
console.log("");
|
|
140
|
+
console.log(" dotnet add package Authflow.Origin");
|
|
141
|
+
console.log("");
|
|
142
|
+
console.log(" builder.Services.AddAuthflowOrigin(builder.Configuration);");
|
|
143
|
+
console.log(" await app.WarmAuthflowOriginKeysAsync();");
|
|
144
|
+
console.log(" app.UseAuthflowOrigin();");
|
|
145
|
+
console.log("");
|
|
146
|
+
console.log(" // appsettings.json");
|
|
147
|
+
console.log(` "Authflow": { "Issuer": "${issuer}", "Resource": "${resource}" }`);
|
|
148
|
+
if (secretStored) {
|
|
149
|
+
console.log("");
|
|
150
|
+
console.log(` ${originApiKeyConfigKey} is already in your secret store.`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// The probe authenticates with the origin API key, which is bound to exactly one resource — so it
|
|
154
|
+
// needs no slug, and it cannot be pointed at someone else's origin.
|
|
155
|
+
async function runProbe(issuer, originApiKey) {
|
|
156
|
+
const { body } = await apiJson(`${issuer}/origin/v1/conformance`, {
|
|
157
|
+
method: "POST",
|
|
158
|
+
headers: { ...bearer(originApiKey), Accept: "application/json" },
|
|
159
|
+
expectedStatuses: [200],
|
|
160
|
+
});
|
|
161
|
+
for (const check of body.checks) {
|
|
162
|
+
console.log(` ${check.pass ? "PASS" : "FAIL"} ${check.name}`);
|
|
163
|
+
if (!check.pass) {
|
|
164
|
+
console.log(` expected ${check.expected}, got ${check.observed}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
console.log("");
|
|
168
|
+
if (body.pass) {
|
|
169
|
+
console.log("Conformance: PASS. The origin is ready for paid traffic.");
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
console.log("Conformance: FAIL. The origin is registered but should not take paid traffic yet.");
|
|
173
|
+
process.exitCode = 1;
|
|
174
|
+
}
|
|
175
|
+
function parseTarget(raw) {
|
|
176
|
+
const value = raw.trim();
|
|
177
|
+
if (!secretTargets.includes(value)) {
|
|
178
|
+
throw new Error(`--write-secrets must be one of: ${secretTargets.join(", ")}`);
|
|
179
|
+
}
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { runStdioServer } from "../mcp/server.js";
|
|
2
|
+
export function mcpCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("mcp")
|
|
5
|
+
.description("Run the Authflow onboarding MCP server on stdio")
|
|
6
|
+
.addHelpText("after", [
|
|
7
|
+
"",
|
|
8
|
+
"Add to an MCP client (Cursor, Claude Desktop) so a coding agent can onboard a server",
|
|
9
|
+
"against the codebase it already has open:",
|
|
10
|
+
"",
|
|
11
|
+
' { "mcpServers": { "authflow": {',
|
|
12
|
+
' "command": "npx", "args": ["-y", "authflow-cli", "mcp"],',
|
|
13
|
+
' "env": { "AUTHFLOW_ISSUER": "https://…", "AUTHFLOW_ADMIN_API_KEY": "…" } } } }',
|
|
14
|
+
"",
|
|
15
|
+
"Mint AUTHFLOW_ADMIN_API_KEY with `authflow signup --email you@example.com` if you do not",
|
|
16
|
+
"already have one. The onboarding server can also mint it via authflow_signup.",
|
|
17
|
+
"",
|
|
18
|
+
"stdout is the MCP transport, so this command writes nothing else to it.",
|
|
19
|
+
].join("\n"))
|
|
20
|
+
.action(async () => {
|
|
21
|
+
await runStdioServer();
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,MAAM,UAAU,UAAU,CAAC,OAAgB;IACzC,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,iDAAiD,CAAC;SAC9D,WAAW,CACV,OAAO,EACP;QACE,EAAE;QACF,sFAAsF;QACtF,2CAA2C;QAC3C,EAAE;QACF,mCAAmC;QACnC,gEAAgE;QAChE,sFAAsF;QACtF,EAAE;QACF,0FAA0F;QAC1F,+EAA+E;QAC/E,EAAE;QACF,yEAAyE;KAC1E,CAAC,IAAI,CAAC,IAAI,CAAC,CACb;SACA,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,cAAc,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { apiJson, bearer, readReceipt, requireIssuer, requireOriginKey, resolveGatewayUrl, } from "../config.js";
|
|
2
|
+
export function mintHeaderCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("mint-header")
|
|
5
|
+
.description("POST /origin/v1/dev/mint-header (dev/staging only)")
|
|
6
|
+
.option("--issuer <url>", "Authflow issuer (default: AUTHFLOW_ISSUER)")
|
|
7
|
+
.option("--origin-key <key>", "Origin API key (default: AUTHFLOW_ORIGIN_API_KEY)")
|
|
8
|
+
.option("--subject <userId>", "Rail user id; omit to let the rail mint one")
|
|
9
|
+
.option("--tier <tier>", "Entitlement tier claim", "standard")
|
|
10
|
+
.option("--ttl <seconds>", "Header TTL in seconds (1–300)", (v) => Number.parseInt(v, 10))
|
|
11
|
+
.option("--grant-credits <n>", "Optional credit grant for the minted subject", (v) => Number.parseInt(v, 10))
|
|
12
|
+
.option("--receipt <path>", "Receipt JSON to resolve gateway_url for the curl hint")
|
|
13
|
+
.option("--gateway-url <url>", "Override gateway URL for the curl hint")
|
|
14
|
+
.action(async (opts) => {
|
|
15
|
+
const issuer = requireIssuer(opts.issuer);
|
|
16
|
+
const originKey = requireOriginKey(opts.originKey);
|
|
17
|
+
const body = {};
|
|
18
|
+
if (opts.subject)
|
|
19
|
+
body.subject = opts.subject;
|
|
20
|
+
if (opts.tier)
|
|
21
|
+
body.tier = opts.tier;
|
|
22
|
+
if (opts.ttl !== undefined) {
|
|
23
|
+
if (!Number.isFinite(opts.ttl))
|
|
24
|
+
throw new Error("--ttl must be an integer.");
|
|
25
|
+
body.ttl_seconds = opts.ttl;
|
|
26
|
+
}
|
|
27
|
+
if (opts.grantCredits !== undefined) {
|
|
28
|
+
if (!Number.isFinite(opts.grantCredits)) {
|
|
29
|
+
throw new Error("--grant-credits must be an integer.");
|
|
30
|
+
}
|
|
31
|
+
body.grant_credits = opts.grantCredits;
|
|
32
|
+
}
|
|
33
|
+
const { body: mint } = await apiJson(`${issuer}/origin/v1/dev/mint-header`, {
|
|
34
|
+
method: "POST",
|
|
35
|
+
headers: {
|
|
36
|
+
...bearer(originKey),
|
|
37
|
+
"Content-Type": "application/json",
|
|
38
|
+
Accept: "application/json",
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify(body),
|
|
41
|
+
expectedStatuses: [200],
|
|
42
|
+
});
|
|
43
|
+
console.log(`${mint.header_name}: ${mint.header}`);
|
|
44
|
+
console.log(`subject: ${mint.subject}`);
|
|
45
|
+
console.log(`nonce: ${mint.nonce}`);
|
|
46
|
+
console.log(`expires_at: ${mint.expires_at}`);
|
|
47
|
+
const receipt = opts.receipt ? await readReceipt(opts.receipt) : undefined;
|
|
48
|
+
const gatewayUrl = resolveGatewayUrl({ gatewayUrl: opts.gatewayUrl, receipt });
|
|
49
|
+
if (gatewayUrl) {
|
|
50
|
+
console.log("");
|
|
51
|
+
console.log("# Ready-to-paste curl (identity hop only — not a substitute for OAuth):");
|
|
52
|
+
console.log(`curl -sS -X POST ${shellQuote(gatewayUrl)} \\\n` +
|
|
53
|
+
` -H ${shellQuote(`${mint.header_name}: ${mint.header}`)} \\\n` +
|
|
54
|
+
` -H 'Content-Type: application/json' \\\n` +
|
|
55
|
+
` -d '{"jsonrpc":"2.0","id":1,"method":"ping"}'`);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
console.log("");
|
|
59
|
+
console.log("(No gateway URL known — set AUTHFLOW_RESOURCE / --gateway-url / --receipt for a curl hint.)");
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function shellQuote(value) {
|
|
64
|
+
if (/^[A-Za-z0-9_/:.-]+$/.test(value)) {
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=mint-header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mint-header.js","sourceRoot":"","sources":["../../src/commands/mint-header.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { apiJson, bearer, requireIssuer, requireOriginKey } from "../config.js";
|
|
2
|
+
export function probeCommand(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("probe")
|
|
5
|
+
.description("POST /origin/v1/conformance and print a pass/fail table")
|
|
6
|
+
.option("--issuer <url>", "Authflow issuer (default: AUTHFLOW_ISSUER)")
|
|
7
|
+
.option("--origin-key <key>", "Origin API key (default: AUTHFLOW_ORIGIN_API_KEY)")
|
|
8
|
+
.action(async (opts) => {
|
|
9
|
+
const issuer = requireIssuer(opts.issuer);
|
|
10
|
+
const originKey = requireOriginKey(opts.originKey);
|
|
11
|
+
const { body } = await apiJson(`${issuer}/origin/v1/conformance`, {
|
|
12
|
+
method: "POST",
|
|
13
|
+
headers: {
|
|
14
|
+
...bearer(originKey),
|
|
15
|
+
Accept: "application/json",
|
|
16
|
+
},
|
|
17
|
+
expectedStatuses: [200],
|
|
18
|
+
});
|
|
19
|
+
console.log(`origin: ${body.origin}`);
|
|
20
|
+
if (body.error) {
|
|
21
|
+
console.log(`error: ${body.error}`);
|
|
22
|
+
}
|
|
23
|
+
console.log("");
|
|
24
|
+
const nameWidth = Math.max(4, ...body.checks.map((c) => c.name.length));
|
|
25
|
+
const header = `${pad("NAME", nameWidth)} RESULT OBSERVED`;
|
|
26
|
+
console.log(header);
|
|
27
|
+
console.log("-".repeat(header.length + 20));
|
|
28
|
+
for (const check of body.checks) {
|
|
29
|
+
const result = check.pass ? "PASS" : "FAIL";
|
|
30
|
+
console.log(`${pad(check.name, nameWidth)} ${pad(result, 6)} ${check.observed}`);
|
|
31
|
+
if (!check.pass) {
|
|
32
|
+
console.log(`${"".padEnd(nameWidth)} expected: ${check.expected}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
console.log("");
|
|
36
|
+
if (body.pass) {
|
|
37
|
+
console.log("Overall: PASS");
|
|
38
|
+
process.exitCode = 0;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
console.log("Overall: FAIL");
|
|
42
|
+
process.exitCode = 1;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function pad(value, width) {
|
|
47
|
+
return value.length >= width ? value : value + " ".repeat(width - value.length);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe.js","sourceRoot":"","sources":["../../src/commands/probe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { apiJson, bearer, requireAdminKey, requireIssuer } from "../config.js";
|
|
3
|
+
import { secretTargets, writeOriginApiKey } from "../secrets.js";
|
|
4
|
+
export function registerCommand(program) {
|
|
5
|
+
program
|
|
6
|
+
.command("register")
|
|
7
|
+
.description("POST /admin/resources and write the registration receipt to disk")
|
|
8
|
+
.requiredOption("-f, --file <path>", "Resource draft JSON (snake_case fields)")
|
|
9
|
+
.option("--issuer <url>", "Authflow issuer (default: AUTHFLOW_ISSUER)")
|
|
10
|
+
.option("--admin-key <key>", "Admin API key (default: AUTHFLOW_ADMIN_API_KEY)")
|
|
11
|
+
.option("-o, --out <path>", "Receipt output path", "receipt.json")
|
|
12
|
+
.option("--write-secrets <target>", `Store the origin API key and redact it from the receipt (${secretTargets.join(" | ")})`)
|
|
13
|
+
.option("--secret-destination <value>", "user-secrets: project dir (default .) · env-file: path (default .env.local) · keyvault: vault name")
|
|
14
|
+
.action(async (opts) => {
|
|
15
|
+
const issuer = requireIssuer(opts.issuer);
|
|
16
|
+
const adminKey = requireAdminKey(opts.adminKey);
|
|
17
|
+
const target = parseTarget(opts.writeSecrets);
|
|
18
|
+
const draftRaw = (await readFile(opts.file, "utf8")).replace(/^\uFEFF/, "");
|
|
19
|
+
let draft;
|
|
20
|
+
try {
|
|
21
|
+
draft = JSON.parse(draftRaw);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
throw new Error(`--file ${opts.file} is not valid JSON.`);
|
|
25
|
+
}
|
|
26
|
+
const { body, status } = await apiJson(`${issuer}/admin/resources`, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: {
|
|
29
|
+
...bearer(adminKey),
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
Accept: "application/json",
|
|
32
|
+
},
|
|
33
|
+
body: JSON.stringify(draft),
|
|
34
|
+
expectedStatuses: [201],
|
|
35
|
+
});
|
|
36
|
+
const originApiKey = typeof body.origin_api_key === "string" ? body.origin_api_key : undefined;
|
|
37
|
+
// Store the secret before the receipt is written: if this fails, the run stops with the key
|
|
38
|
+
// still only in memory rather than leaving a plaintext credential on disk.
|
|
39
|
+
let storedAt;
|
|
40
|
+
if (target && originApiKey) {
|
|
41
|
+
storedAt = await writeOriginApiKey({
|
|
42
|
+
target,
|
|
43
|
+
value: originApiKey,
|
|
44
|
+
destination: opts.secretDestination,
|
|
45
|
+
});
|
|
46
|
+
body.origin_api_key = `<stored in ${storedAt}>`;
|
|
47
|
+
}
|
|
48
|
+
await writeFile(opts.out, `${JSON.stringify(body, null, 2)}\n`, "utf8");
|
|
49
|
+
console.log(`Registered (${status}). Receipt written to ${opts.out}`);
|
|
50
|
+
if (typeof body.slug === "string") {
|
|
51
|
+
console.log(` slug: ${body.slug}`);
|
|
52
|
+
}
|
|
53
|
+
if (typeof body.gateway_url === "string") {
|
|
54
|
+
console.log(` gateway_url: ${body.gateway_url}`);
|
|
55
|
+
}
|
|
56
|
+
if (typeof body.status === "string") {
|
|
57
|
+
console.log(` status: ${body.status}`);
|
|
58
|
+
}
|
|
59
|
+
if (storedAt) {
|
|
60
|
+
console.log(` origin_api_key: stored in ${storedAt} (redacted from the receipt)`);
|
|
61
|
+
}
|
|
62
|
+
else if (originApiKey) {
|
|
63
|
+
console.log(` origin_api_key: ${originApiKey} (store now — shown once)`);
|
|
64
|
+
console.log(` Note: ${opts.out} now holds a plaintext credential. Re-run with --write-secrets to avoid that.`);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function parseTarget(raw) {
|
|
69
|
+
if (raw === undefined) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const value = raw.trim();
|
|
73
|
+
if (!secretTargets.includes(value)) {
|
|
74
|
+
throw new Error(`--write-secrets must be one of: ${secretTargets.join(", ")}`);
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/commands/register.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { readReceipt } from "../config.js";
|
|
3
|
+
export function scaffoldCommand(program) {
|
|
4
|
+
program
|
|
5
|
+
.command("scaffold")
|
|
6
|
+
.description("Write .env + docker-compose snippet for authflow-origin-proxy in front of upstream")
|
|
7
|
+
.requiredOption("--receipt <path>", "Registration receipt JSON")
|
|
8
|
+
.option("--upstream <url>", "Upstream MCP base URI", "http://upstream:3000")
|
|
9
|
+
.option("--env-out <path>", "Env file path", ".env")
|
|
10
|
+
.option("--compose-out <path>", "Compose file path", "docker-compose.authflow.yml")
|
|
11
|
+
.option("--proxy-port <port>", "Published proxy port", "8080")
|
|
12
|
+
.action(async (opts) => {
|
|
13
|
+
const receipt = await readReceipt(opts.receipt);
|
|
14
|
+
const issuer = receipt.issuer?.trim();
|
|
15
|
+
const resource = (receipt.resource ?? receipt.gateway_url)?.trim();
|
|
16
|
+
const originKey = receipt.origin_api_key?.trim();
|
|
17
|
+
if (!issuer || !resource) {
|
|
18
|
+
throw new Error("Receipt must include issuer and resource (or gateway_url). Re-run authflow register.");
|
|
19
|
+
}
|
|
20
|
+
const envLines = [
|
|
21
|
+
`# Generated by authflow scaffold from ${opts.receipt}`,
|
|
22
|
+
`AUTHFLOW_ISSUER=${issuer}`,
|
|
23
|
+
`AUTHFLOW_RESOURCE=${resource}`,
|
|
24
|
+
...(originKey ? [`AUTHFLOW_ORIGIN_API_KEY=${originKey}`] : []),
|
|
25
|
+
`AUTHFLOW_UPSTREAM_BASE_URI=${opts.upstream}`,
|
|
26
|
+
"",
|
|
27
|
+
];
|
|
28
|
+
await writeFile(opts.envOut, envLines.join("\n"), "utf8");
|
|
29
|
+
const compose = `# Generated by authflow scaffold from ${opts.receipt}
|
|
30
|
+
# Put authflow-origin-proxy in front of your MCP upstream.
|
|
31
|
+
# Build the image from the Authflow.Rail repo:
|
|
32
|
+
# docker build -f src/Rail.OriginProxy/Dockerfile -t authflow-origin-proxy .
|
|
33
|
+
#
|
|
34
|
+
# Replay protection is process-local until you set Authflow__NonceCacheRedis.
|
|
35
|
+
# Set it before raising replicas above 1.
|
|
36
|
+
# See docs/onboarding/origin-proxy.md
|
|
37
|
+
|
|
38
|
+
services:
|
|
39
|
+
authflow-origin-proxy:
|
|
40
|
+
image: authflow-origin-proxy
|
|
41
|
+
ports:
|
|
42
|
+
- "${opts.proxyPort}:8080"
|
|
43
|
+
environment:
|
|
44
|
+
Authflow__Issuer: \${AUTHFLOW_ISSUER}
|
|
45
|
+
Authflow__Resource: \${AUTHFLOW_RESOURCE}
|
|
46
|
+
Authflow__UpstreamBaseUri: \${AUTHFLOW_UPSTREAM_BASE_URI}
|
|
47
|
+
# Authflow__NonceCacheRedis: \${AUTHFLOW_NONCE_CACHE_REDIS}
|
|
48
|
+
env_file:
|
|
49
|
+
- ${opts.envOut}
|
|
50
|
+
# Safe to raise once Authflow__NonceCacheRedis is set.
|
|
51
|
+
deploy:
|
|
52
|
+
replicas: 1
|
|
53
|
+
|
|
54
|
+
# Replace with your MCP origin, or point Authflow__UpstreamBaseUri at an
|
|
55
|
+
# existing host and drop this service.
|
|
56
|
+
upstream:
|
|
57
|
+
image: your-mcp-origin:latest
|
|
58
|
+
expose:
|
|
59
|
+
- "3000"
|
|
60
|
+
`;
|
|
61
|
+
await writeFile(opts.composeOut, compose, "utf8");
|
|
62
|
+
console.log(`Wrote ${opts.envOut}`);
|
|
63
|
+
console.log(`Wrote ${opts.composeOut}`);
|
|
64
|
+
console.log("");
|
|
65
|
+
console.log("Next:");
|
|
66
|
+
console.log(` docker build -f src/Rail.OriginProxy/Dockerfile -t authflow-origin-proxy <Authflow.Rail>`);
|
|
67
|
+
console.log(` docker compose -f ${opts.composeOut} --env-file ${opts.envOut} up`);
|
|
68
|
+
console.log("");
|
|
69
|
+
console.log("Upstream receives X-Authflow-User / X-Authflow-Tier / X-Authflow-Nonce after verification.");
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/commands/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,oFAAoF,CAAC;SACjG,cAAc,CAAC,kBAAkB,EAAE,2BAA2B,CAAC;SAC/D,MAAM,CAAC,kBAAkB,EAAE,uBAAuB,EAAE,sBAAsB,CAAC;SAC3E,MAAM,CAAC,kBAAkB,EAAE,eAAe,EAAE,MAAM,CAAC;SACnD,MAAM,CAAC,sBAAsB,EAAE,mBAAmB,EAAE,6BAA6B,CAAC;SAClF,MAAM,CAAC,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,CAAC;SAC7D,MAAM,CACL,KAAK,EAAE,IAMN,EAAE,EAAE;QACH,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;QACtC,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC;QACnE,MAAM,SAAS,GAAG,OAAO,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC;QAEjD,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,sFAAsF,CACvF,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG;YACf,yCAAyC,IAAI,CAAC,OAAO,EAAE;YACvD,mBAAmB,MAAM,EAAE;YAC3B,qBAAqB,QAAQ,EAAE;YAC/B,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,2BAA2B,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,8BAA8B,IAAI,CAAC,QAAQ,EAAE;YAC7C,EAAE;SACH,CAAC;QACF,MAAM,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QAE1D,MAAM,OAAO,GAAG,yCAAyC,IAAI,CAAC,OAAO;;;;;;;;;;;;;WAalE,IAAI,CAAC,SAAS;;;;;;;UAOf,IAAI,CAAC,MAAM;;;;;;;;;;;CAWpB,CAAC;QAEM,MAAM,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAElD,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,OAAO,CAAC,GAAG,CACT,4FAA4F,CAC7F,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,CAAC,UAAU,eAAe,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC;QACnF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CACT,4FAA4F,CAC7F,CAAC;IACJ,CAAC,CACF,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { apiJson, bearer, requireAdminKey, requireIssuer } from "../config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Second half of the register-now / deploy-later flow: point a draft resource at its origin, which
|
|
4
|
+
* promotes it to live and makes it routable.
|
|
5
|
+
*/
|
|
6
|
+
export function setOriginCommand(program) {
|
|
7
|
+
program
|
|
8
|
+
.command("set-origin")
|
|
9
|
+
.description("Set a resource's origin base URI, promoting a draft to live")
|
|
10
|
+
.requiredOption("--slug <slug>", "Resource slug")
|
|
11
|
+
.requiredOption("--origin-url <url>", "Origin base URI (no /mcp suffix — the gateway appends it)")
|
|
12
|
+
.option("--issuer <url>", "Authflow issuer (default: AUTHFLOW_ISSUER)")
|
|
13
|
+
.option("--admin-key <key>", "Admin API key (default: AUTHFLOW_ADMIN_API_KEY)")
|
|
14
|
+
.action(async (opts) => {
|
|
15
|
+
const issuer = requireIssuer(opts.issuer);
|
|
16
|
+
const adminKey = requireAdminKey(opts.adminKey);
|
|
17
|
+
// A receipt-configured origin serves MCP at {origin}/mcp, so a URL that already ends in /mcp
|
|
18
|
+
// would be called at /mcp/mcp. Cheap to catch here, confusing to debug later.
|
|
19
|
+
if (/\/mcp\/?$/i.test(opts.originUrl)) {
|
|
20
|
+
throw new Error(`--origin-url must not end in /mcp. The gateway appends it, so ${opts.originUrl} would be called at ${opts.originUrl.replace(/\/$/, "")}/mcp.`);
|
|
21
|
+
}
|
|
22
|
+
const { body } = await apiJson(`${issuer}/admin/resources/${encodeURIComponent(opts.slug)}/origin`, {
|
|
23
|
+
method: "PUT",
|
|
24
|
+
headers: {
|
|
25
|
+
...bearer(adminKey),
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
Accept: "application/json",
|
|
28
|
+
},
|
|
29
|
+
body: JSON.stringify({ origin_base_uri: opts.originUrl }),
|
|
30
|
+
expectedStatuses: [200],
|
|
31
|
+
});
|
|
32
|
+
console.log(`${body.slug ?? opts.slug} -> ${body.origin_base_uri ?? opts.originUrl}`);
|
|
33
|
+
console.log(`status: ${body.status ?? "live"}`);
|
|
34
|
+
console.log("");
|
|
35
|
+
console.log("Verify fail-closed behaviour before sending real traffic: authflow probe");
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=set-origin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set-origin.js","sourceRoot":"","sources":["../../src/commands/set-origin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE/E;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,OAAO;SACJ,OAAO,CAAC,YAAY,CAAC;SACrB,WAAW,CAAC,6DAA6D,CAAC;SAC1E,cAAc,CAAC,eAAe,EAAE,eAAe,CAAC;SAChD,cAAc,CAAC,oBAAoB,EAAE,2DAA2D,CAAC;SACjG,MAAM,CAAC,gBAAgB,EAAE,4CAA4C,CAAC;SACtE,MAAM,CAAC,mBAAmB,EAAE,iDAAiD,CAAC;SAC9E,MAAM,CAAC,KAAK,EAAE,IAA6E,EAAE,EAAE;QAC9F,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAEhD,6FAA6F;QAC7F,8EAA8E;QAC9E,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CACb,iEAAiE,IAAI,CAAC,SAAS,uBAAuB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAC/I,CAAC;QACJ,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,OAAO,CAC5B,GAAG,MAAM,oBAAoB,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EACnE;YACE,MAAM,EAAE,KAAK;YACb,OAAO,EAAE;gBACP,GAAG,MAAM,CAAC,QAAQ,CAAC;gBACnB,cAAc,EAAE,kBAAkB;gBAClC,MAAM,EAAE,kBAAkB;aAC3B;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,eAAe,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;YACzD,gBAAgB,EAAE,CAAC,GAAG,CAAC;SACxB,CACF,CAAC;QAEF,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QACtF,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,0EAA0E,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;AACP,CAAC"}
|