@hosty-sdk/app 0.10.0 → 0.11.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/dist/oauth-resource.d.ts +35 -0
- package/dist/oauth-resource.js +42 -0
- package/package.json +5 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** The RFC 9728 document for one MCP endpoint. */
|
|
2
|
+
export interface ProtectedResourceMetadata {
|
|
3
|
+
resource: string;
|
|
4
|
+
authorization_servers: string[];
|
|
5
|
+
scopes_supported: string[];
|
|
6
|
+
bearer_methods_supported: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface ResourceMetadataOptions {
|
|
9
|
+
/** The app's own MCP endpoint URL as clients reach it. Wins over any derivation. */
|
|
10
|
+
resourceUrl?: string;
|
|
11
|
+
/** The public origin of the endpoint serving MCP. Defaults to `HOSTY_PUBLIC_ORIGIN_API` — the
|
|
12
|
+
* per-endpoint variable Core injects when an endpoint is published, for the reference layout
|
|
13
|
+
* where the MCP interface rides the `api` endpoint. An app with a different layout passes its
|
|
14
|
+
* own endpoint's origin (or the full resourceUrl). */
|
|
15
|
+
publicOrigin?: string;
|
|
16
|
+
/** The MCP endpoint's path under that origin. */
|
|
17
|
+
resourcePath?: string;
|
|
18
|
+
/** The authorization server's browser-reachable origin. Defaults to HOSTY_CORE_PUBLIC_ORIGIN —
|
|
19
|
+
* the flow's whole point is a remote client and a browser completing it, so the loopback origin
|
|
20
|
+
* this app dials Core on would send both to the wrong machine. */
|
|
21
|
+
authorizationServerOrigin?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Builds the metadata document, or null when the environment cannot name the two URLs it consists
|
|
25
|
+
* of — which is the ordinary state of an app that is not published to a public origin. Null rather
|
|
26
|
+
* than a guess: a wrong resource identity here would have clients requesting tokens for a URL
|
|
27
|
+
* nothing serves, and no metadata simply means the manual token path, which always works.
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildProtectedResourceMetadata(options?: ResourceMetadataOptions): ProtectedResourceMetadata | null;
|
|
30
|
+
/**
|
|
31
|
+
* The `WWW-Authenticate` value a 401 from the MCP endpoint should carry, pointing a stock client at
|
|
32
|
+
* the metadata. The metadata URL is the RFC 9728 derivation: the well-known prefix inserted before
|
|
33
|
+
* the resource's path.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildWwwAuthenticate(metadata: ProtectedResourceMetadata): string;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// The resource-server half of the MCP authorization handshake (RFC 9728), for an app's MCP
|
|
2
|
+
// endpoint: the metadata document naming Core as the authorization server, and the 401 challenge
|
|
3
|
+
// header pointing at it.
|
|
4
|
+
//
|
|
5
|
+
// In its own entry like ./scoped-token, and for the same reason: an MCP endpoint is usually not a
|
|
6
|
+
// Next route, and the server slice pulls in "server-only".
|
|
7
|
+
//
|
|
8
|
+
// The app serves the *pointer*; Core is the authorization server and serves everything else. An app
|
|
9
|
+
// never mints, validates, or even sees an OAuth exchange — a client that follows this pointer comes
|
|
10
|
+
// back carrying an ordinary scoped access token, which the app validates through introspection
|
|
11
|
+
// exactly as if the operator had pasted one by hand.
|
|
12
|
+
/**
|
|
13
|
+
* Builds the metadata document, or null when the environment cannot name the two URLs it consists
|
|
14
|
+
* of — which is the ordinary state of an app that is not published to a public origin. Null rather
|
|
15
|
+
* than a guess: a wrong resource identity here would have clients requesting tokens for a URL
|
|
16
|
+
* nothing serves, and no metadata simply means the manual token path, which always works.
|
|
17
|
+
*/
|
|
18
|
+
export function buildProtectedResourceMetadata(options = {}) {
|
|
19
|
+
const core = options.authorizationServerOrigin?.trim() || process.env.HOSTY_CORE_PUBLIC_ORIGIN?.trim();
|
|
20
|
+
const publicOrigin = options.publicOrigin?.trim() || process.env.HOSTY_PUBLIC_ORIGIN_API?.trim();
|
|
21
|
+
const path = options.resourcePath?.trim() || "/api/mcp";
|
|
22
|
+
const resource = options.resourceUrl?.trim() || (publicOrigin ? `${publicOrigin.replace(/\/$/, "")}${path}` : undefined);
|
|
23
|
+
if (!core || !resource) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
resource,
|
|
28
|
+
authorization_servers: [core.replace(/\/$/, "")],
|
|
29
|
+
scopes_supported: ["mcp:read"],
|
|
30
|
+
bearer_methods_supported: ["header"],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The `WWW-Authenticate` value a 401 from the MCP endpoint should carry, pointing a stock client at
|
|
35
|
+
* the metadata. The metadata URL is the RFC 9728 derivation: the well-known prefix inserted before
|
|
36
|
+
* the resource's path.
|
|
37
|
+
*/
|
|
38
|
+
export function buildWwwAuthenticate(metadata) {
|
|
39
|
+
const resource = new URL(metadata.resource);
|
|
40
|
+
const metadataUrl = `${resource.origin}/.well-known/oauth-protected-resource${resource.pathname.replace(/\/$/, "")}`;
|
|
41
|
+
return `Bearer resource_metadata="${metadataUrl}"`;
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hosty-sdk/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Hosty app auth: session state machine, recovery bridge, Core revalidation, and the embedder responder.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
"types": "./dist/scoped-token.d.ts",
|
|
22
22
|
"default": "./dist/scoped-token.js"
|
|
23
23
|
},
|
|
24
|
+
"./oauth-resource": {
|
|
25
|
+
"types": "./dist/oauth-resource.d.ts",
|
|
26
|
+
"default": "./dist/oauth-resource.js"
|
|
27
|
+
},
|
|
24
28
|
"./react": {
|
|
25
29
|
"types": "./dist/react.d.ts",
|
|
26
30
|
"default": "./dist/react.js"
|