Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesPull RequestsActionsSecurityInsightsSettings
✨ AI
More
Blame · Line-by-line history

well-known.ts

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

well-known.tsBlame97 lines · 1 contributor
369ad65ccanty labs1/**
2 * OAuth / agent discovery metadata under `/.well-known/*`.
3 *
4 * These endpoints let an AI agent (claude.ai remote connectors, Cursor,
5 * Copilot, etc.) auto-discover how to authenticate against Gluecron without
6 * any manual configuration. They advertise ONLY endpoints that actually
7 * exist today:
8 *
9 * - RFC 8414 Authorization Server Metadata
10 * GET /.well-known/oauth-authorization-server
11 * - RFC 9728 Protected Resource Metadata
12 * GET /.well-known/oauth-protected-resource
13 * GET /.well-known/oauth-protected-resource/mcp (resource-specific)
14 *
15 * `registration_endpoint` (RFC 7591 dynamic client registration) and
16 * `introspection_endpoint` (RFC 7662) are intentionally omitted until those
17 * endpoints ship — advertising a 404 would break the discovery flow. When
18 * they land, add them here in the same change.
19 *
20 * Pure/read-only, no auth, no DB. Safe to serve to anyone.
21 */
22
23import { Hono } from "hono";
24import { config } from "../lib/config";
25import { SUPPORTED_SCOPES } from "../lib/oauth";
26
27const wellKnown = new Hono();
28
29/** Absolute URL on this deployment's public origin (no trailing slash). */
30function base(): string {
31 // config.appBaseUrl already strips a trailing slash.
32 return config.appBaseUrl;
33}
34
35/**
36 * RFC 8414 — OAuth 2.0 Authorization Server Metadata.
37 * The document a client reads to learn where /authorize and /token live,
38 * which PKCE methods are supported, and which scopes exist.
39 */
40wellKnown.get("/.well-known/oauth-authorization-server", (c) => {
41 const b = base();
42 return c.json(
43 {
44 issuer: b,
45 authorization_endpoint: `${b}/oauth/authorize`,
46 token_endpoint: `${b}/oauth/token`,
47 revocation_endpoint: `${b}/oauth/revoke`,
48 scopes_supported: SUPPORTED_SCOPES,
49 response_types_supported: ["code"],
50 grant_types_supported: ["authorization_code", "refresh_token"],
51 // PKCE (RFC 7636) — the provider verifies S256 and plain.
52 code_challenge_methods_supported: ["S256", "plain"],
53 token_endpoint_auth_methods_supported: [
54 "client_secret_basic",
55 "client_secret_post",
56 "none", // public clients (PKCE, no secret)
57 ],
58 revocation_endpoint_auth_methods_supported: [
59 "client_secret_basic",
60 "client_secret_post",
61 "none",
62 ],
63 service_documentation: `${b}/api/docs`,
64 },
65 200,
66 { "cache-control": "public, max-age=3600" }
67 );
68});
69
70/**
71 * RFC 9728 — OAuth 2.0 Protected Resource Metadata.
72 *
73 * Served both at the root path and at `/mcp` (resource-specific). A remote
74 * MCP connector reads this to learn which authorization server guards the
75 * `/mcp` resource and which scopes it accepts.
76 */
77function protectedResourceDoc(c: Parameters<Parameters<typeof wellKnown.get>[1]>[0]) {
78 const b = base();
79 return c.json(
80 {
81 resource: `${b}/mcp`,
82 authorization_servers: [b],
83 scopes_supported: SUPPORTED_SCOPES,
84 bearer_methods_supported: ["header"],
85 resource_documentation: `${b}/api/docs`,
86 },
87 200,
88 { "cache-control": "public, max-age=3600" }
89 );
90}
91
92wellKnown.get("/.well-known/oauth-protected-resource", protectedResourceDoc);
93// Resource-specific form (RFC 9728 §3.1) — some clients append the resource
94// path when the WWW-Authenticate challenge points at `<origin>/mcp`.
95wellKnown.get("/.well-known/oauth-protected-resource/mcp", protectedResourceDoc);
96
97export default wellKnown;