-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathsetup.js
More file actions
102 lines (95 loc) · 3.65 KB
/
Copy pathsetup.js
File metadata and controls
102 lines (95 loc) · 3.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// Registers the three service_account clients in Authorizer's client registry
// via the admin GraphQL API (`_create_client`, super-admin auth via the
// x-authorizer-admin-secret header) and writes the resulting credentials to
// ./.env. Client secrets are returned exactly ONCE by the API — this script is
// the only moment they are visible, which is why it persists them immediately.
//
// Usage:
// AUTHORIZER_URL=http://localhost:8080 AUTHORIZER_ADMIN_SECRET=admin node setup.js
import { writeFileSync } from "node:fs";
const AUTHORIZER_URL = process.env.AUTHORIZER_URL || "http://localhost:8080";
const ADMIN_SECRET = process.env.AUTHORIZER_ADMIN_SECRET || "admin";
// The instance's global client ID (--client-id). Every JWT minted by this
// Authorizer instance carries it as the `aud` claim, so all three services
// need it to verify tokens. `make dev` default shown here.
const AUTHORIZER_CLIENT_ID =
process.env.AUTHORIZER_CLIENT_ID || "kbyuFDidLLm280LIwVFiazOqjO3ty8KH";
// Each service gets its own identity with a distinct scope ceiling
// (allowed_scopes is an allow-list: token requests outside it fail with
// invalid_scope; an empty list is rejected at creation — deny-all).
const SERVICES = [
{
envPrefix: "GATEWAY",
name: "example-gateway",
description: "Public API gateway — calls orders-service",
allowed_scopes: ["orders:read", "orders:write"],
},
{
envPrefix: "ORDERS",
name: "example-orders-service",
description: "Orders service — calls billing-service to charge",
allowed_scopes: ["billing:charge"],
},
{
envPrefix: "BILLING",
name: "example-billing-service",
description:
"Billing service — leaf service; scope ceiling reserved for future refund calls",
allowed_scopes: ["billing:refund"],
},
];
async function adminGraphQL(query, variables) {
const res = await fetch(`${AUTHORIZER_URL}/graphql`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-authorizer-admin-secret": ADMIN_SECRET,
// Authorizer's CSRF middleware requires an allow-listed Origin on
// state-changing requests, even server-to-server.
Origin: AUTHORIZER_URL,
},
body: JSON.stringify({ query, variables }),
});
const body = await res.json();
if (body.errors?.length) {
throw new Error(`GraphQL error: ${body.errors.map((e) => e.message).join("; ")}`);
}
return body.data;
}
const CREATE_CLIENT = `
mutation CreateClient($params: CreateClientRequest!) {
_create_client(params: $params) {
client { id client_id name allowed_scopes is_active }
client_secret
}
}
`;
const lines = [
`# Generated by setup.js on ${new Date().toISOString()}`,
`AUTHORIZER_URL=${AUTHORIZER_URL}`,
`AUTHORIZER_CLIENT_ID=${AUTHORIZER_CLIENT_ID}`,
`GATEWAY_PORT=4000`,
`ORDERS_PORT=4001`,
`BILLING_PORT=4002`,
`ORDERS_URL=http://localhost:4001`,
`BILLING_URL=http://localhost:4002`,
];
for (const svc of SERVICES) {
const data = await adminGraphQL(CREATE_CLIENT, {
params: {
name: svc.name,
description: svc.description,
allowed_scopes: svc.allowed_scopes,
},
});
const { client, client_secret } = data._create_client;
console.log(
`registered ${svc.name}: client_id=${client.client_id} scopes=[${client.allowed_scopes.join(", ")}]`,
);
lines.push(`${svc.envPrefix}_CLIENT_ID=${client.client_id}`);
lines.push(`${svc.envPrefix}_CLIENT_SECRET=${client_secret}`);
}
writeFileSync(new URL("./.env", import.meta.url), lines.join("\n") + "\n");
console.log("\nWrote .env — start the services:");
console.log(" npm run start:billing & npm run start:orders & npm run start:gateway &");
console.log(" npm run demo");