Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f7c9f3dc94 | |||
| b71833ef79 | |||
| 9c7bc2881c |
8
check-oauth.sh
Executable file
8
check-oauth.sh
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
docker exec gearbox bun -e "
|
||||||
|
const{Database}=require('bun:sqlite');
|
||||||
|
const db=new Database('./data/gearbox.db');
|
||||||
|
console.log('CLIENTS:', JSON.stringify(db.query('SELECT * FROM oauth_clients ORDER BY id DESC LIMIT 5').all(), null, 2));
|
||||||
|
console.log('CODES:', JSON.stringify(db.query('SELECT id,client_id,used,expires_at FROM oauth_codes ORDER BY id DESC LIMIT 5').all(), null, 2));
|
||||||
|
console.log('TOKENS:', JSON.stringify(db.query('SELECT id,client_id,expires_at FROM oauth_tokens ORDER BY id DESC LIMIT 5').all(), null, 2));
|
||||||
|
"
|
||||||
BIN
e2e/test.db-shm
Normal file
BIN
e2e/test.db-shm
Normal file
Binary file not shown.
BIN
e2e/test.db-wal
Normal file
BIN
e2e/test.db-wal
Normal file
Binary file not shown.
@@ -1,5 +1,6 @@
|
|||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { serveStatic } from "hono/bun";
|
import { serveStatic } from "hono/bun";
|
||||||
|
import { cors } from "hono/cors";
|
||||||
import { db as prodDb } from "../db/index.ts";
|
import { db as prodDb } from "../db/index.ts";
|
||||||
import { seedDefaults } from "../db/seed.ts";
|
import { seedDefaults } from "../db/seed.ts";
|
||||||
import { mcpRoutes } from "./mcp/index.ts";
|
import { mcpRoutes } from "./mcp/index.ts";
|
||||||
@@ -34,6 +35,11 @@ app.get("/api/health", (c) => {
|
|||||||
return c.json({ status: "ok" });
|
return c.json({ status: "ok" });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// CORS for OAuth and MCP endpoints (required for claude.ai browser-based flows)
|
||||||
|
app.use("/.well-known/*", cors());
|
||||||
|
app.use("/oauth/*", cors());
|
||||||
|
app.use("/mcp/*", cors());
|
||||||
|
|
||||||
// OAuth routes (must be before /api/* middleware)
|
// OAuth routes (must be before /api/* middleware)
|
||||||
app.use("/oauth/*", async (c, next) => {
|
app.use("/oauth/*", async (c, next) => {
|
||||||
c.set("db", prodDb);
|
c.set("db", prodDb);
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ mcpRoutes.use("/*", async (c, next) => {
|
|||||||
const authHeader = c.req.header("Authorization");
|
const authHeader = c.req.header("Authorization");
|
||||||
if (authHeader?.startsWith("Bearer ")) {
|
if (authHeader?.startsWith("Bearer ")) {
|
||||||
const token = authHeader.slice(7);
|
const token = authHeader.slice(7);
|
||||||
if (verifyAccessToken(db, token)) {
|
if (await verifyAccessToken(db, token)) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
return c.json({ error: "invalid_token" }, 401);
|
return c.json({ error: "invalid_token" }, 401);
|
||||||
@@ -115,10 +115,12 @@ mcpRoutes.use("/*", async (c, next) => {
|
|||||||
return c.json({ error: "Invalid API key" }, 401);
|
return c.json({ error: "Invalid API key" }, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No auth provided — return 401 with WWW-Authenticate to trigger OAuth flow
|
// No auth provided — return 401 with WWW-Authenticate to trigger OAuth flow (RFC 9728)
|
||||||
|
const baseUrl = (
|
||||||
|
process.env.GEARBOX_URL || new URL(c.req.url).origin
|
||||||
|
).replace(/\/$/, "");
|
||||||
return c.text("Unauthorized", 401, {
|
return c.text("Unauthorized", 401, {
|
||||||
"WWW-Authenticate":
|
"WWW-Authenticate": `Bearer resource_metadata="${baseUrl}/.well-known/oauth-protected-resource"`,
|
||||||
'Bearer resource_metadata="/.well-known/oauth-authorization-server"',
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,16 @@ function renderLoginForm(params: {
|
|||||||
|
|
||||||
export const wellKnownRoute = new Hono<Env>();
|
export const wellKnownRoute = new Hono<Env>();
|
||||||
|
|
||||||
|
// Protected Resource Metadata (RFC 9728) — Claude fetches this first after 401
|
||||||
|
wellKnownRoute.get("/oauth-protected-resource", (c) => {
|
||||||
|
const baseUrl = getBaseUrl(c);
|
||||||
|
return c.json({
|
||||||
|
resource: `${baseUrl}/mcp`,
|
||||||
|
authorization_servers: [baseUrl],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// OAuth Authorization Server Metadata (RFC 8414) — Claude fetches this second
|
||||||
wellKnownRoute.get("/oauth-authorization-server", (c) => {
|
wellKnownRoute.get("/oauth-authorization-server", (c) => {
|
||||||
const baseUrl = getBaseUrl(c);
|
const baseUrl = getBaseUrl(c);
|
||||||
return c.json({
|
return c.json({
|
||||||
|
|||||||
Reference in New Issue
Block a user