@briefgate/mcp 0.7.4 → 0.7.6

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.
@@ -3,7 +3,9 @@ import type { BriefGateConfig } from './client.js';
3
3
  export interface HttpAuthOptions {
4
4
  /** Hostname this server is published under, or undefined when it is loopback-only. */
5
5
  publicHost?: string | undefined;
6
- /** BRIEFGATE_API_KEY, used only when the server is NOT published. */
6
+ /** The operator's own key, from --api-key, used only when NOT published. */
7
+ cliApiKey?: string | undefined;
8
+ /** The operator's own key, from BRIEFGATE_API_KEY, used only when NOT published. */
7
9
  envApiKey?: string | undefined;
8
10
  baseUrl: string;
9
11
  }
@@ -11,13 +13,17 @@ export interface HttpAuthOptions {
11
13
  * The API key for one request.
12
14
  *
13
15
  * Published mode reads it from the Authorization header and nowhere else. The
14
- * environment fallback exists so a laptop can run `--http` against the
15
- * operator's own key; on a public endpoint it would hand that key to whoever
16
- * asked first, so it is switched off there rather than merely discouraged.
16
+ * --api-key / environment / locally-stored-credential fallbacks exist so a
17
+ * laptop can run `--http` against the operator's own key (or one saved by
18
+ * `login`); on a public endpoint any of those would hand that key to whoever
19
+ * asked first, so all three are switched off there rather than merely
20
+ * discouraged.
17
21
  *
18
22
  * An absent key is not an error here. `initialize` and `tools/list` have to work
19
23
  * without one, or no registry can read the tool list before anybody signs up —
20
- * a tool call with no key fails later with a sentence saying so.
24
+ * a tool call with no key fails later with a sentence saying so. Published
25
+ * mode is stricter about this at the HTTP layer (see index.ts): a request with
26
+ * no Bearer token never reaches this function at all.
21
27
  */
22
28
  export declare function configForRequest(req: IncomingMessage, opts: HttpAuthOptions): BriefGateConfig;
23
29
  /**
@@ -27,5 +33,33 @@ export declare function configForRequest(req: IncomingMessage, opts: HttpAuthOpt
27
33
  * pointed at this box by whoever noticed the IP.
28
34
  */
29
35
  export declare function isAllowedHost(host: string, publicHost?: string | undefined): boolean;
36
+ /**
37
+ * Published mode allows any origin: the resource is protected by the Bearer
38
+ * token, which — unlike a cookie — a browser never attaches on its own, so
39
+ * restricting origins buys no extra security and would only stop legitimate
40
+ * OAuth-registered clients (claude.ai and others) from connecting. Loopback
41
+ * mode keeps the strict allowlist: there, a plain `--http` run may still be
42
+ * relying on an operator key with no per-request Bearer at all, so the origin
43
+ * check is doing real work.
44
+ */
30
45
  export declare function isAllowedOrigin(origin: string, publicHost?: string | undefined): boolean;
46
+ export declare const WELL_KNOWN_PROTECTED_RESOURCE_PATH = "/.well-known/oauth-protected-resource";
47
+ export declare const WELL_KNOWN_PROTECTED_RESOURCE_MCP_PATH = "/.well-known/oauth-protected-resource/mcp";
48
+ export interface ProtectedResourceMetadata {
49
+ resource: string;
50
+ authorization_servers: string[];
51
+ bearer_methods_supported: string[];
52
+ scopes_supported: string[];
53
+ resource_name: string;
54
+ }
55
+ export declare function protectedResourceMetadata(publicHost: string, authServer: string): ProtectedResourceMetadata;
56
+ /**
57
+ * Value for the `WWW-Authenticate` header on a 401, pointing the client at
58
+ * the resource metadata above so it knows where to start the OAuth dance.
59
+ * Pass `error: 'invalid_token'` when the caller did send a Bearer token and
60
+ * it was specifically rejected (expired/revoked) — as opposed to no token
61
+ * having been sent at all — so the client knows to refresh rather than just
62
+ * retry the same one.
63
+ */
64
+ export declare function wwwAuthenticateHeader(publicHost: string, error?: 'invalid_token'): string;
31
65
  //# sourceMappingURL=http-auth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"http-auth.d.ts","sourceRoot":"","sources":["../src/http-auth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,GAAG,eAAe,CAO7F;AAID;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAIpF;AAMD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAGxF"}
1
+ {"version":3,"file":"http-auth.d.ts","sourceRoot":"","sources":["../src/http-auth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnD,MAAM,WAAW,eAAe;IAC9B,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,oFAAoF;IACpF,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,GAAG,eAAe,CAkB7F;AAID;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAIpF;AAMD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAGxF;AAWD,eAAO,MAAM,kCAAkC,0CAA0C,CAAC;AAC1F,eAAO,MAAM,sCAAsC,8CAA8C,CAAC;AAElG,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,wBAAwB,EAAE,MAAM,EAAE,CAAC;IACnC,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,yBAAyB,CAQ3B;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,eAAe,GAAG,MAAM,CAGzF"}
package/dist/http-auth.js CHANGED
@@ -1,20 +1,36 @@
1
+ import { resolveApiKey } from './credentials.js';
1
2
  /**
2
3
  * The API key for one request.
3
4
  *
4
5
  * Published mode reads it from the Authorization header and nowhere else. The
5
- * environment fallback exists so a laptop can run `--http` against the
6
- * operator's own key; on a public endpoint it would hand that key to whoever
7
- * asked first, so it is switched off there rather than merely discouraged.
6
+ * --api-key / environment / locally-stored-credential fallbacks exist so a
7
+ * laptop can run `--http` against the operator's own key (or one saved by
8
+ * `login`); on a public endpoint any of those would hand that key to whoever
9
+ * asked first, so all three are switched off there rather than merely
10
+ * discouraged.
8
11
  *
9
12
  * An absent key is not an error here. `initialize` and `tools/list` have to work
10
13
  * without one, or no registry can read the tool list before anybody signs up —
11
- * a tool call with no key fails later with a sentence saying so.
14
+ * a tool call with no key fails later with a sentence saying so. Published
15
+ * mode is stricter about this at the HTTP layer (see index.ts): a request with
16
+ * no Bearer token never reaches this function at all.
12
17
  */
13
18
  export function configForRequest(req, opts) {
14
19
  const header = req.headers['authorization'];
15
20
  const bearer = typeof header === 'string' ? /^Bearer\s+(\S+)$/i.exec(header.trim())?.[1] : undefined;
16
- const apiKey = opts.publicHost ? (bearer ?? '') : (bearer ?? opts.envApiKey ?? '');
17
- return { apiKey, baseUrl: opts.baseUrl };
21
+ if (opts.publicHost) {
22
+ return { apiKey: bearer ?? '', baseUrl: opts.baseUrl, apiKeySource: bearer ? 'header' : 'none' };
23
+ }
24
+ if (bearer)
25
+ return { apiKey: bearer, baseUrl: opts.baseUrl, apiKeySource: 'header' };
26
+ const { apiKey, source } = resolveApiKey({
27
+ explicitApiKey: opts.cliApiKey,
28
+ explicitSource: 'flag',
29
+ envApiKey: opts.envApiKey,
30
+ baseUrl: opts.baseUrl,
31
+ allowFileFallback: true,
32
+ });
33
+ return { apiKey, baseUrl: opts.baseUrl, apiKeySource: source };
18
34
  }
19
35
  const LOOPBACK_HOST_RE = /^(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/;
20
36
  /**
@@ -31,9 +47,49 @@ export function isAllowedHost(host, publicHost) {
31
47
  }
32
48
  /** Loopback origins and claude.ai, which proxies MCP for its own clients. */
33
49
  const SAFE_ORIGIN_RE = /^https?:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$|^https:\/\/claude\.ai$/;
50
+ /**
51
+ * Published mode allows any origin: the resource is protected by the Bearer
52
+ * token, which — unlike a cookie — a browser never attaches on its own, so
53
+ * restricting origins buys no extra security and would only stop legitimate
54
+ * OAuth-registered clients (claude.ai and others) from connecting. Loopback
55
+ * mode keeps the strict allowlist: there, a plain `--http` run may still be
56
+ * relying on an operator key with no per-request Bearer at all, so the origin
57
+ * check is doing real work.
58
+ */
34
59
  export function isAllowedOrigin(origin, publicHost) {
35
- if (publicHost && origin.toLowerCase() === `https://${publicHost.toLowerCase()}`)
60
+ if (publicHost)
36
61
  return true;
37
62
  return SAFE_ORIGIN_RE.test(origin);
38
63
  }
64
+ // ─── OAuth 2.1 protected-resource metadata (published mode only) ─────────────
65
+ //
66
+ // Lets an MCP client (claude.ai's connector UI) discover, from the server URL
67
+ // alone, which authorization server to run OAuth against — RFC 9728 as
68
+ // profiled by the MCP authorization spec.
69
+ // RFC 9728 defines the bare path; the MCP authorization spec also has clients
70
+ // look under a path scoped to the resource itself (the resource being `/mcp`
71
+ // here) — both must serve identical content.
72
+ export const WELL_KNOWN_PROTECTED_RESOURCE_PATH = '/.well-known/oauth-protected-resource';
73
+ export const WELL_KNOWN_PROTECTED_RESOURCE_MCP_PATH = `${WELL_KNOWN_PROTECTED_RESOURCE_PATH}/mcp`;
74
+ export function protectedResourceMetadata(publicHost, authServer) {
75
+ return {
76
+ resource: `https://${publicHost}/mcp`,
77
+ authorization_servers: [authServer],
78
+ bearer_methods_supported: ['header'],
79
+ scopes_supported: ['admin', 'intakes:read', 'intakes:write', 'secrets:read'],
80
+ resource_name: 'BriefGate MCP',
81
+ };
82
+ }
83
+ /**
84
+ * Value for the `WWW-Authenticate` header on a 401, pointing the client at
85
+ * the resource metadata above so it knows where to start the OAuth dance.
86
+ * Pass `error: 'invalid_token'` when the caller did send a Bearer token and
87
+ * it was specifically rejected (expired/revoked) — as opposed to no token
88
+ * having been sent at all — so the client knows to refresh rather than just
89
+ * retry the same one.
90
+ */
91
+ export function wwwAuthenticateHeader(publicHost, error) {
92
+ const resourceMetadata = `resource_metadata="https://${publicHost}${WELL_KNOWN_PROTECTED_RESOURCE_PATH}"`;
93
+ return error ? `Bearer error="${error}", ${resourceMetadata}` : `Bearer ${resourceMetadata}`;
94
+ }
39
95
  //# sourceMappingURL=http-auth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"http-auth.js","sourceRoot":"","sources":["../src/http-auth.ts"],"names":[],"mappings":"AAeA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAoB,EAAE,IAAqB;IAC1E,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5C,MAAM,MAAM,GACV,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAExF,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IACnF,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;AAC3C,CAAC;AAED,MAAM,gBAAgB,GAAG,+CAA+C,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,UAA+B;IACzE,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5D,IAAI,UAAU,IAAI,WAAW,KAAK,UAAU,CAAC,WAAW,EAAE;QAAE,OAAO,IAAI,CAAC;IACxE,OAAO,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,6EAA6E;AAC7E,MAAM,cAAc,GAClB,iFAAiF,CAAC;AAEpF,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,UAA+B;IAC7E,IAAI,UAAU,IAAI,MAAM,CAAC,WAAW,EAAE,KAAK,WAAW,UAAU,CAAC,WAAW,EAAE,EAAE;QAAE,OAAO,IAAI,CAAC;IAC9F,OAAO,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC"}
1
+ {"version":3,"file":"http-auth.js","sourceRoot":"","sources":["../src/http-auth.ts"],"names":[],"mappings":"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"}