Blame · Line-by-line history
well-known.test.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.
| 369ad65 | 1 | import { describe, it, expect, beforeAll } from "bun:test"; |
| 2 | import app from "../app"; | |
| 3 | import { SUPPORTED_SCOPES } from "../lib/oauth"; | |
| 4 | ||
| 5 | // These endpoints are what claude.ai / Cursor / Copilot read first to learn | |
| 6 | // how to authenticate. They must be anonymous, JSON, and advertise only | |
| 7 | // endpoints that actually exist. | |
| 8 | ||
| 9 | beforeAll(() => { | |
| 10 | process.env.APP_BASE_URL = "https://gluecron.com"; | |
| 11 | }); | |
| 12 | ||
| 13 | describe("RFC 8414 — authorization server metadata", () => { | |
| 14 | it("serves valid metadata anonymously", async () => { | |
| 15 | const res = await app.request("/.well-known/oauth-authorization-server"); | |
| 16 | expect(res.status).toBe(200); | |
| 17 | expect(res.headers.get("content-type") || "").toContain("application/json"); | |
| 18 | const doc = await res.json(); | |
| 19 | expect(doc.issuer).toBe("https://gluecron.com"); | |
| 20 | expect(doc.authorization_endpoint).toBe("https://gluecron.com/oauth/authorize"); | |
| 21 | expect(doc.token_endpoint).toBe("https://gluecron.com/oauth/token"); | |
| 22 | expect(doc.revocation_endpoint).toBe("https://gluecron.com/oauth/revoke"); | |
| 23 | }); | |
| 24 | ||
| 25 | it("advertises PKCE S256 and the authorization_code + refresh grants", async () => { | |
| 26 | const doc = await ( | |
| 27 | await app.request("/.well-known/oauth-authorization-server") | |
| 28 | ).json(); | |
| 29 | expect(doc.code_challenge_methods_supported).toContain("S256"); | |
| 30 | expect(doc.grant_types_supported).toContain("authorization_code"); | |
| 31 | expect(doc.grant_types_supported).toContain("refresh_token"); | |
| 32 | expect(doc.response_types_supported).toEqual(["code"]); | |
| 33 | }); | |
| 34 | ||
| 35 | it("advertises exactly the supported scopes", async () => { | |
| 36 | const doc = await ( | |
| 37 | await app.request("/.well-known/oauth-authorization-server") | |
| 38 | ).json(); | |
| 39 | expect(doc.scopes_supported).toEqual([...SUPPORTED_SCOPES]); | |
| 40 | }); | |
| 41 | ||
| 42 | it("supports the public-client (none) auth method for PKCE apps", async () => { | |
| 43 | const doc = await ( | |
| 44 | await app.request("/.well-known/oauth-authorization-server") | |
| 45 | ).json(); | |
| 46 | expect(doc.token_endpoint_auth_methods_supported).toContain("none"); | |
| 47 | }); | |
| 48 | ||
| 49 | it("does NOT advertise endpoints that don't exist yet (register/introspect)", async () => { | |
| 50 | const doc = await ( | |
| 51 | await app.request("/.well-known/oauth-authorization-server") | |
| 52 | ).json(); | |
| 53 | // Guard against re-introducing a broken discovery flow: only advertise | |
| 54 | // registration_endpoint / introspection_endpoint once they're built. | |
| 55 | expect(doc.registration_endpoint).toBeUndefined(); | |
| 56 | expect(doc.introspection_endpoint).toBeUndefined(); | |
| 57 | }); | |
| 58 | }); | |
| 59 | ||
| 60 | describe("RFC 9728 — protected resource metadata", () => { | |
| 61 | it("serves the resource metadata for the MCP endpoint", async () => { | |
| 62 | const res = await app.request("/.well-known/oauth-protected-resource"); | |
| 63 | expect(res.status).toBe(200); | |
| 64 | const doc = await res.json(); | |
| 65 | expect(doc.resource).toBe("https://gluecron.com/mcp"); | |
| 66 | expect(doc.authorization_servers).toEqual(["https://gluecron.com"]); | |
| 67 | expect(doc.bearer_methods_supported).toContain("header"); | |
| 68 | }); | |
| 69 | ||
| 70 | it("also serves the resource-specific /mcp form", async () => { | |
| 71 | const res = await app.request("/.well-known/oauth-protected-resource/mcp"); | |
| 72 | expect(res.status).toBe(200); | |
| 73 | const doc = await res.json(); | |
| 74 | expect(doc.resource).toBe("https://gluecron.com/mcp"); | |
| 75 | }); | |
| 76 | }); |