@sentropic/h2a-cli 0.32.0 → 0.33.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.
@@ -10,5 +10,6 @@ export { startHostedServer, buildHostedConfigFromEnv, type HostedEnv, type Hoste
10
10
  export { FileOAuthStore } from "./oauth/file-store.js";
11
11
  export { SingleTenantOAuthProvider } from "./oauth/single-tenant-provider.js";
12
12
  export { buildOAuthRoutes } from "./oauth/hono-oauth-router.js";
13
+ export { buildUpstreamAuthorizeUrl, exchangeUpstreamCode, type H2AUpstreamOidcConfig, type UpstreamFetch, type UpstreamLogin } from "./oauth/oidc-rp.js";
13
14
  export { oauthConfigFromEnv, H2A_HOSTED_OAUTH_SCOPE, type H2AHostedOAuthConfig, type H2AHostedOAuthEnv } from "./oauth/config.js";
14
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/runtime/mcp-http/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,yBAAyB,EACzB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACzB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/runtime/mcp-http/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,yBAAyB,EACzB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACzB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC"}
@@ -10,5 +10,6 @@ export { startHostedServer, buildHostedConfigFromEnv } from "./serve.js";
10
10
  export { FileOAuthStore } from "./oauth/file-store.js";
11
11
  export { SingleTenantOAuthProvider } from "./oauth/single-tenant-provider.js";
12
12
  export { buildOAuthRoutes } from "./oauth/hono-oauth-router.js";
13
+ export { buildUpstreamAuthorizeUrl, exchangeUpstreamCode } from "./oauth/oidc-rp.js";
13
14
  export { oauthConfigFromEnv, H2A_HOSTED_OAUTH_SCOPE } from "./oauth/config.js";
14
15
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/runtime/mcp-http/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,yBAAyB,EACzB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,eAAe,EAAsB,MAAM,UAAU,CAAC;AAC/D,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EAIzB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EAGvB,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/runtime/mcp-http/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,yBAAyB,EACzB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,eAAe,EAAsB,MAAM,UAAU,CAAC;AAC/D,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EAIzB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EAIrB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EAGvB,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * EVO-12 P2 (mode 3, gateway broker) — upstream OIDC Relying Party for 39-auth.
3
+ *
4
+ * The hosted shim keeps the DCR + token surface claude.ai needs (self-AS), but
5
+ * delegates the USER login to 39-auth (the sentropic OIDC IdP, live at
6
+ * `…/api/v1/auth/oauth/authorize`): redirect to its `/authorize`, then exchange
7
+ * the code at `/token` and read the authenticated `sub`. The `sub` is what the
8
+ * gateway maps to a per-user h2a root (multi-tenant).
9
+ *
10
+ * Pure + fetch-injected → unit-testable against a mock IdP. The real client
11
+ * (client_id/secret) is seeded operator-side in 39-auth (no DCR there); that
12
+ * seed is the live-integration point, not a code dependency.
13
+ */
14
+ export interface H2AUpstreamOidcConfig {
15
+ /** 39-auth issuer (informational / id_token `iss` check later). */
16
+ readonly issuer: string;
17
+ /** 39-auth authorization endpoint (e.g. …/api/v1/auth/oauth/authorize). */
18
+ readonly authorizeUrl: string;
19
+ /** 39-auth token endpoint (e.g. …/api/v1/auth/oauth/token). */
20
+ readonly tokenUrl: string;
21
+ /** This gateway's seeded client id at 39-auth. */
22
+ readonly clientId: string;
23
+ /** This gateway's client secret (k8s Secret). */
24
+ readonly clientSecret: string;
25
+ /** The gateway's callback URL registered at 39-auth. */
26
+ readonly redirectUri: string;
27
+ /** Scopes to request (openid required for `sub`). */
28
+ readonly scopes: readonly string[];
29
+ }
30
+ /** Minimal fetch shape (injected for tests; global fetch satisfies it). */
31
+ export type UpstreamFetch = (url: string, init: {
32
+ method: string;
33
+ headers: Record<string, string>;
34
+ body: string;
35
+ }) => Promise<{
36
+ ok: boolean;
37
+ status: number;
38
+ json: () => Promise<unknown>;
39
+ }>;
40
+ export interface UpstreamLogin {
41
+ /** The authenticated user — the gateway's per-tenant key. */
42
+ readonly sub: string;
43
+ readonly idToken: string;
44
+ readonly accessToken?: string;
45
+ }
46
+ /** Build the 39-auth `/authorize` URL to redirect the user to (authorization_code + PKCE). */
47
+ export declare function buildUpstreamAuthorizeUrl(config: H2AUpstreamOidcConfig, params: {
48
+ state: string;
49
+ codeChallenge: string;
50
+ }): string;
51
+ /** Exchange an authorization_code (+ PKCE verifier) at 39-auth for the user's `sub`. */
52
+ export declare function exchangeUpstreamCode(config: H2AUpstreamOidcConfig, params: {
53
+ code: string;
54
+ codeVerifier: string;
55
+ }, fetchImpl: UpstreamFetch): Promise<UpstreamLogin>;
56
+ //# sourceMappingURL=oidc-rp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc-rp.d.ts","sourceRoot":"","sources":["../../../../src/runtime/mcp-http/oauth/oidc-rp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,iDAAiD;IACjD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,wDAAwD;IACxD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,2EAA2E;AAC3E,MAAM,MAAM,aAAa,GAAG,CAC1B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KACpE,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC;AAE5E,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,8FAA8F;AAC9F,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,qBAAqB,EAC7B,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GAC/C,MAAM,CAUR;AAqBD,wFAAwF;AACxF,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,qBAAqB,EAC7B,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EAC9C,SAAS,EAAE,aAAa,GACvB,OAAO,CAAC,aAAa,CAAC,CA0BxB"}
@@ -0,0 +1,63 @@
1
+ /** Build the 39-auth `/authorize` URL to redirect the user to (authorization_code + PKCE). */
2
+ export function buildUpstreamAuthorizeUrl(config, params) {
3
+ const url = new URL(config.authorizeUrl);
4
+ url.searchParams.set("response_type", "code");
5
+ url.searchParams.set("client_id", config.clientId);
6
+ url.searchParams.set("redirect_uri", config.redirectUri);
7
+ url.searchParams.set("scope", config.scopes.join(" "));
8
+ url.searchParams.set("state", params.state);
9
+ url.searchParams.set("code_challenge", params.codeChallenge);
10
+ url.searchParams.set("code_challenge_method", "S256");
11
+ return url.href;
12
+ }
13
+ /** Read `sub` from an id_token payload. The token came from our authenticated
14
+ * server-to-server exchange with 39-auth (TLS + client_secret), so the payload
15
+ * is trusted here; verifying the EdDSA signature against 39-auth JWKS is the
16
+ * hardening step (needs JWKS publicly reachable). */
17
+ function subFromIdToken(idToken) {
18
+ const parts = idToken.split(".");
19
+ if (parts.length < 2)
20
+ throw new Error("malformed id_token");
21
+ let payload;
22
+ try {
23
+ payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
24
+ }
25
+ catch {
26
+ throw new Error("unparseable id_token payload");
27
+ }
28
+ if (typeof payload.sub !== "string" || payload.sub.length === 0) {
29
+ throw new Error("id_token has no sub");
30
+ }
31
+ return payload.sub;
32
+ }
33
+ /** Exchange an authorization_code (+ PKCE verifier) at 39-auth for the user's `sub`. */
34
+ export async function exchangeUpstreamCode(config, params, fetchImpl) {
35
+ const body = new URLSearchParams({
36
+ grant_type: "authorization_code",
37
+ code: params.code,
38
+ redirect_uri: config.redirectUri,
39
+ client_id: config.clientId,
40
+ code_verifier: params.codeVerifier
41
+ }).toString();
42
+ const basic = Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64");
43
+ const res = await fetchImpl(config.tokenUrl, {
44
+ method: "POST",
45
+ headers: {
46
+ "content-type": "application/x-www-form-urlencoded",
47
+ accept: "application/json",
48
+ authorization: `Basic ${basic}`
49
+ },
50
+ body
51
+ });
52
+ if (!res.ok)
53
+ throw new Error(`upstream token exchange failed: HTTP ${res.status}`);
54
+ const tok = (await res.json());
55
+ if (typeof tok.id_token !== "string")
56
+ throw new Error("upstream token response missing id_token");
57
+ return {
58
+ sub: subFromIdToken(tok.id_token),
59
+ idToken: tok.id_token,
60
+ ...(typeof tok.access_token === "string" ? { accessToken: tok.access_token } : {})
61
+ };
62
+ }
63
+ //# sourceMappingURL=oidc-rp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc-rp.js","sourceRoot":"","sources":["../../../../src/runtime/mcp-http/oauth/oidc-rp.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentropic/h2a-cli",
3
- "version": "0.32.0",
3
+ "version": "0.33.0",
4
4
  "description": "Unified CLI surface for h2a hosts and MCP-oriented coordination flows.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -43,7 +43,7 @@
43
43
  "@hono/mcp": "^0.3.0",
44
44
  "@hono/node-server": "^2.0.4",
45
45
  "@modelcontextprotocol/sdk": "^1.29.0",
46
- "@sentropic/h2a": "^0.32.0",
46
+ "@sentropic/h2a": "^0.33.0",
47
47
  "hono": "^4.12.23"
48
48
  },
49
49
  "publishConfig": {