mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-09 20:27:44 +00:00
Merge remote-tracking branch 'upstream' into v1.3.8-staging
This commit is contained in:
@@ -66,6 +66,17 @@ import { auth } from "../../../lib/auth";
|
||||
export const GET = oAuthDiscoveryMetadata(auth);
|
||||
```
|
||||
|
||||
### OAuth Protected Resource Metadata
|
||||
|
||||
Add a route to expose protected resource metadata for MCP clients:
|
||||
|
||||
```ts title=".well-known/oauth-authorization-server/route.ts"
|
||||
import { oAuthProtectedResourceMetadata } from "better-auth/plugins";
|
||||
import { auth } from "@/lib/auth";
|
||||
|
||||
export const GET = oAuthProtectedResourceMetadata(auth);
|
||||
```
|
||||
|
||||
### MCP Session Handling
|
||||
|
||||
You can use the helper function `withMcpAuth` to get the session and handle unauthenticated calls automatically.
|
||||
|
||||
@@ -83,6 +83,27 @@ export const getMCPProviderMetadata = (
|
||||
};
|
||||
};
|
||||
|
||||
export const getMCPProtectedResourceMetadata = (
|
||||
ctx: GenericEndpointContext,
|
||||
options?: OIDCOptions,
|
||||
) => {
|
||||
const baseURL = ctx.context.baseURL;
|
||||
|
||||
return {
|
||||
resource: baseURL,
|
||||
authorization_servers: [baseURL],
|
||||
jwks_uri: options?.metadata?.jwks_uri ?? `${baseURL}/mcp/jwks`,
|
||||
scopes_supported: options?.metadata?.scopes_supported ?? [
|
||||
"openid",
|
||||
"profile",
|
||||
"email",
|
||||
"offline_access",
|
||||
],
|
||||
bearer_methods_supported: ["header"],
|
||||
resource_signing_alg_values_supported: ["RS256", "none"],
|
||||
};
|
||||
};
|
||||
|
||||
export const mcp = (options: MCPOptions) => {
|
||||
const opts = {
|
||||
codeExpiresIn: 600,
|
||||
@@ -168,6 +189,19 @@ export const mcp = (options: MCPOptions) => {
|
||||
}
|
||||
},
|
||||
),
|
||||
getMCPProtectedResource: createAuthEndpoint(
|
||||
"/.well-known/oauth-protected-resource",
|
||||
{
|
||||
method: "GET",
|
||||
metadata: {
|
||||
client: false,
|
||||
},
|
||||
},
|
||||
async (c) => {
|
||||
const metadata = getMCPProtectedResourceMetadata(c, options);
|
||||
return c.json(metadata);
|
||||
},
|
||||
),
|
||||
mcpOAuthAuthroize: createAuthEndpoint(
|
||||
"/mcp/authorize",
|
||||
{
|
||||
@@ -912,7 +946,7 @@ export const withMcpAuth = <
|
||||
const session = await auth.api.getMcpSession({
|
||||
headers: req.headers,
|
||||
});
|
||||
const wwwAuthenticateValue = `Bearer resource_metadata=${baseURL}/api/auth/.well-known/oauth-authorization-server`;
|
||||
const wwwAuthenticateValue = `Bearer resource_metadata=${baseURL}/api/auth/.well-known/oauth-protected-resource`;
|
||||
if (!session) {
|
||||
return Response.json(
|
||||
{
|
||||
@@ -959,3 +993,27 @@ export const oAuthDiscoveryMetadata = <
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const oAuthProtectedResourceMetadata = <
|
||||
Auth extends {
|
||||
api: {
|
||||
getMCPProtectedResource: (...args: any) => any;
|
||||
};
|
||||
},
|
||||
>(
|
||||
auth: Auth,
|
||||
) => {
|
||||
return async (request: Request) => {
|
||||
const res = await auth.api.getMCPProtectedResource();
|
||||
return new Response(JSON.stringify(res), {
|
||||
status: 200,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
||||
"Access-Control-Max-Age": "86400",
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user