Initial commit: Complete Hive distributed AI orchestration platform

This comprehensive implementation includes:
- FastAPI backend with MCP server integration
- React/TypeScript frontend with Vite
- PostgreSQL database with Redis caching
- Grafana/Prometheus monitoring stack
- Docker Compose orchestration
- Full MCP protocol support for Claude Code integration

Features:
- Agent discovery and management across network
- Visual workflow editor and execution engine
- Real-time task coordination and monitoring
- Multi-model support with specialized agents
- Distributed development task allocation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
anthonyrawlins
2025-07-07 21:44:31 +10:00
commit d7ad321176
2631 changed files with 870175 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
import { OAuthClientInformationFull } from "../../shared/auth.js";
/**
* Stores information about registered OAuth clients for this server.
*/
export interface OAuthRegisteredClientsStore {
/**
* Returns information about a registered client, based on its ID.
*/
getClient(clientId: string): OAuthClientInformationFull | undefined | Promise<OAuthClientInformationFull | undefined>;
/**
* Registers a new client with the server. The client ID and secret will be automatically generated by the library. A modified version of the client information can be returned to reflect specific values enforced by the server.
*
* NOTE: Implementations should NOT delete expired client secrets in-place. Auth middleware provided by this library will automatically check the `client_secret_expires_at` field and reject requests with expired secrets. Any custom logic for authenticating clients should check the `client_secret_expires_at` field as well.
*
* If unimplemented, dynamic client registration is unsupported.
*/
registerClient?(client: OAuthClientInformationFull): OAuthClientInformationFull | Promise<OAuthClientInformationFull>;
}
//# sourceMappingURL=clients.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"clients.d.ts","sourceRoot":"","sources":["../../../../src/server/auth/clients.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,0BAA0B,GAAG,SAAS,GAAG,OAAO,CAAC,0BAA0B,GAAG,SAAS,CAAC,CAAC;IAEtH;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,MAAM,EAAE,0BAA0B,GAAG,0BAA0B,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;CACvH"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=clients.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"clients.js","sourceRoot":"","sources":["../../../../src/server/auth/clients.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,126 @@
import { OAuthErrorResponse } from "../../shared/auth.js";
/**
* Base class for all OAuth errors
*/
export declare class OAuthError extends Error {
readonly errorCode: string;
readonly errorUri?: string | undefined;
constructor(errorCode: string, message: string, errorUri?: string | undefined);
/**
* Converts the error to a standard OAuth error response object
*/
toResponseObject(): OAuthErrorResponse;
}
/**
* Invalid request error - The request is missing a required parameter,
* includes an invalid parameter value, includes a parameter more than once,
* or is otherwise malformed.
*/
export declare class InvalidRequestError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Invalid client error - Client authentication failed (e.g., unknown client, no client
* authentication included, or unsupported authentication method).
*/
export declare class InvalidClientError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Invalid grant error - The provided authorization grant or refresh token is
* invalid, expired, revoked, does not match the redirection URI used in the
* authorization request, or was issued to another client.
*/
export declare class InvalidGrantError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Unauthorized client error - The authenticated client is not authorized to use
* this authorization grant type.
*/
export declare class UnauthorizedClientError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Unsupported grant type error - The authorization grant type is not supported
* by the authorization server.
*/
export declare class UnsupportedGrantTypeError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Invalid scope error - The requested scope is invalid, unknown, malformed, or
* exceeds the scope granted by the resource owner.
*/
export declare class InvalidScopeError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Access denied error - The resource owner or authorization server denied the request.
*/
export declare class AccessDeniedError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Server error - The authorization server encountered an unexpected condition
* that prevented it from fulfilling the request.
*/
export declare class ServerError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Temporarily unavailable error - The authorization server is currently unable to
* handle the request due to a temporary overloading or maintenance of the server.
*/
export declare class TemporarilyUnavailableError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Unsupported response type error - The authorization server does not support
* obtaining an authorization code using this method.
*/
export declare class UnsupportedResponseTypeError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Unsupported token type error - The authorization server does not support
* the requested token type.
*/
export declare class UnsupportedTokenTypeError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Invalid token error - The access token provided is expired, revoked, malformed,
* or invalid for other reasons.
*/
export declare class InvalidTokenError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Method not allowed error - The HTTP method used is not allowed for this endpoint.
* (Custom, non-standard error)
*/
export declare class MethodNotAllowedError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Too many requests error - Rate limit exceeded.
* (Custom, non-standard error based on RFC 6585)
*/
export declare class TooManyRequestsError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Invalid client metadata error - The client metadata is invalid.
* (Custom error for dynamic client registration - RFC 7591)
*/
export declare class InvalidClientMetadataError extends OAuthError {
constructor(message: string, errorUri?: string);
}
/**
* Insufficient scope error - The request requires higher privileges than provided by the access token.
*/
export declare class InsufficientScopeError extends OAuthError {
constructor(message: string, errorUri?: string);
}
//# sourceMappingURL=errors.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../../src/server/auth/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;GAEG;AACH,qBAAa,UAAW,SAAQ,KAAK;aAEjB,SAAS,EAAE,MAAM;aAEjB,QAAQ,CAAC,EAAE,MAAM;gBAFjB,SAAS,EAAE,MAAM,EACjC,OAAO,EAAE,MAAM,EACC,QAAQ,CAAC,EAAE,MAAM,YAAA;IAMnC;;OAEG;IACH,gBAAgB,IAAI,kBAAkB;CAYvC;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,UAAU;gBACrC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,UAAU;gBACpC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;gBACnC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;;GAGG;AACH,qBAAa,uBAAwB,SAAQ,UAAU;gBACzC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;;GAGG;AACH,qBAAa,yBAA0B,SAAQ,UAAU;gBAC3C,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;gBACnC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;gBACnC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;;GAGG;AACH,qBAAa,WAAY,SAAQ,UAAU;gBAC7B,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;;GAGG;AACH,qBAAa,2BAA4B,SAAQ,UAAU;gBAC7C,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;;GAGG;AACH,qBAAa,4BAA6B,SAAQ,UAAU;gBAC9C,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;;GAGG;AACH,qBAAa,yBAA0B,SAAQ,UAAU;gBAC3C,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,UAAU;gBACnC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,UAAU;gBACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,UAAU;gBACtC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;;GAGG;AACH,qBAAa,0BAA2B,SAAQ,UAAU;gBAC5C,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C;AAED;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,UAAU;gBACxC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM;CAG/C"}

View File

@@ -0,0 +1,169 @@
/**
* Base class for all OAuth errors
*/
export class OAuthError extends Error {
constructor(errorCode, message, errorUri) {
super(message);
this.errorCode = errorCode;
this.errorUri = errorUri;
this.name = this.constructor.name;
}
/**
* Converts the error to a standard OAuth error response object
*/
toResponseObject() {
const response = {
error: this.errorCode,
error_description: this.message
};
if (this.errorUri) {
response.error_uri = this.errorUri;
}
return response;
}
}
/**
* Invalid request error - The request is missing a required parameter,
* includes an invalid parameter value, includes a parameter more than once,
* or is otherwise malformed.
*/
export class InvalidRequestError extends OAuthError {
constructor(message, errorUri) {
super("invalid_request", message, errorUri);
}
}
/**
* Invalid client error - Client authentication failed (e.g., unknown client, no client
* authentication included, or unsupported authentication method).
*/
export class InvalidClientError extends OAuthError {
constructor(message, errorUri) {
super("invalid_client", message, errorUri);
}
}
/**
* Invalid grant error - The provided authorization grant or refresh token is
* invalid, expired, revoked, does not match the redirection URI used in the
* authorization request, or was issued to another client.
*/
export class InvalidGrantError extends OAuthError {
constructor(message, errorUri) {
super("invalid_grant", message, errorUri);
}
}
/**
* Unauthorized client error - The authenticated client is not authorized to use
* this authorization grant type.
*/
export class UnauthorizedClientError extends OAuthError {
constructor(message, errorUri) {
super("unauthorized_client", message, errorUri);
}
}
/**
* Unsupported grant type error - The authorization grant type is not supported
* by the authorization server.
*/
export class UnsupportedGrantTypeError extends OAuthError {
constructor(message, errorUri) {
super("unsupported_grant_type", message, errorUri);
}
}
/**
* Invalid scope error - The requested scope is invalid, unknown, malformed, or
* exceeds the scope granted by the resource owner.
*/
export class InvalidScopeError extends OAuthError {
constructor(message, errorUri) {
super("invalid_scope", message, errorUri);
}
}
/**
* Access denied error - The resource owner or authorization server denied the request.
*/
export class AccessDeniedError extends OAuthError {
constructor(message, errorUri) {
super("access_denied", message, errorUri);
}
}
/**
* Server error - The authorization server encountered an unexpected condition
* that prevented it from fulfilling the request.
*/
export class ServerError extends OAuthError {
constructor(message, errorUri) {
super("server_error", message, errorUri);
}
}
/**
* Temporarily unavailable error - The authorization server is currently unable to
* handle the request due to a temporary overloading or maintenance of the server.
*/
export class TemporarilyUnavailableError extends OAuthError {
constructor(message, errorUri) {
super("temporarily_unavailable", message, errorUri);
}
}
/**
* Unsupported response type error - The authorization server does not support
* obtaining an authorization code using this method.
*/
export class UnsupportedResponseTypeError extends OAuthError {
constructor(message, errorUri) {
super("unsupported_response_type", message, errorUri);
}
}
/**
* Unsupported token type error - The authorization server does not support
* the requested token type.
*/
export class UnsupportedTokenTypeError extends OAuthError {
constructor(message, errorUri) {
super("unsupported_token_type", message, errorUri);
}
}
/**
* Invalid token error - The access token provided is expired, revoked, malformed,
* or invalid for other reasons.
*/
export class InvalidTokenError extends OAuthError {
constructor(message, errorUri) {
super("invalid_token", message, errorUri);
}
}
/**
* Method not allowed error - The HTTP method used is not allowed for this endpoint.
* (Custom, non-standard error)
*/
export class MethodNotAllowedError extends OAuthError {
constructor(message, errorUri) {
super("method_not_allowed", message, errorUri);
}
}
/**
* Too many requests error - Rate limit exceeded.
* (Custom, non-standard error based on RFC 6585)
*/
export class TooManyRequestsError extends OAuthError {
constructor(message, errorUri) {
super("too_many_requests", message, errorUri);
}
}
/**
* Invalid client metadata error - The client metadata is invalid.
* (Custom error for dynamic client registration - RFC 7591)
*/
export class InvalidClientMetadataError extends OAuthError {
constructor(message, errorUri) {
super("invalid_client_metadata", message, errorUri);
}
}
/**
* Insufficient scope error - The request requires higher privileges than provided by the access token.
*/
export class InsufficientScopeError extends OAuthError {
constructor(message, errorUri) {
super("insufficient_scope", message, errorUri);
}
}
//# sourceMappingURL=errors.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../../src/server/auth/errors.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,13 @@
import { RequestHandler } from "express";
import { OAuthServerProvider } from "../provider.js";
import { Options as RateLimitOptions } from "express-rate-limit";
export type AuthorizationHandlerOptions = {
provider: OAuthServerProvider;
/**
* Rate limiting configuration for the authorization endpoint.
* Set to false to disable rate limiting for this endpoint.
*/
rateLimit?: Partial<RateLimitOptions> | false;
};
export declare function authorizationHandler({ provider, rateLimit: rateLimitConfig }: AuthorizationHandlerOptions): RequestHandler;
//# sourceMappingURL=authorize.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"authorize.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/authorize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAa,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAW5E,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,KAAK,CAAC;CAC/C,CAAC;AAkBF,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,2BAA2B,GAAG,cAAc,CAkH1H"}

View File

@@ -0,0 +1,143 @@
import { z } from "zod";
import express from "express";
import { rateLimit } from "express-rate-limit";
import { allowedMethods } from "../middleware/allowedMethods.js";
import { InvalidRequestError, InvalidClientError, InvalidScopeError, ServerError, TooManyRequestsError, OAuthError } from "../errors.js";
// Parameters that must be validated in order to issue redirects.
const ClientAuthorizationParamsSchema = z.object({
client_id: z.string(),
redirect_uri: z.string().optional().refine((value) => value === undefined || URL.canParse(value), { message: "redirect_uri must be a valid URL" }),
});
// Parameters that must be validated for a successful authorization request. Failure can be reported to the redirect URI.
const RequestAuthorizationParamsSchema = z.object({
response_type: z.literal("code"),
code_challenge: z.string(),
code_challenge_method: z.literal("S256"),
scope: z.string().optional(),
state: z.string().optional(),
resource: z.string().url().optional(),
});
export function authorizationHandler({ provider, rateLimit: rateLimitConfig }) {
// Create a router to apply middleware
const router = express.Router();
router.use(allowedMethods(["GET", "POST"]));
router.use(express.urlencoded({ extended: false }));
// Apply rate limiting unless explicitly disabled
if (rateLimitConfig !== false) {
router.use(rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per windowMs
standardHeaders: true,
legacyHeaders: false,
message: new TooManyRequestsError('You have exceeded the rate limit for authorization requests').toResponseObject(),
...rateLimitConfig
}));
}
router.all("/", async (req, res) => {
var _a;
res.setHeader('Cache-Control', 'no-store');
// In the authorization flow, errors are split into two categories:
// 1. Pre-redirect errors (direct response with 400)
// 2. Post-redirect errors (redirect with error parameters)
// Phase 1: Validate client_id and redirect_uri. Any errors here must be direct responses.
let client_id, redirect_uri, client;
try {
const result = ClientAuthorizationParamsSchema.safeParse(req.method === 'POST' ? req.body : req.query);
if (!result.success) {
throw new InvalidRequestError(result.error.message);
}
client_id = result.data.client_id;
redirect_uri = result.data.redirect_uri;
client = await provider.clientsStore.getClient(client_id);
if (!client) {
throw new InvalidClientError("Invalid client_id");
}
if (redirect_uri !== undefined) {
if (!client.redirect_uris.includes(redirect_uri)) {
throw new InvalidRequestError("Unregistered redirect_uri");
}
}
else if (client.redirect_uris.length === 1) {
redirect_uri = client.redirect_uris[0];
}
else {
throw new InvalidRequestError("redirect_uri must be specified when client has multiple registered URIs");
}
}
catch (error) {
// Pre-redirect errors - return direct response
//
// These don't need to be JSON encoded, as they'll be displayed in a user
// agent, but OTOH they all represent exceptional situations (arguably,
// "programmer error"), so presenting a nice HTML page doesn't help the
// user anyway.
if (error instanceof OAuthError) {
const status = error instanceof ServerError ? 500 : 400;
res.status(status).json(error.toResponseObject());
}
else {
const serverError = new ServerError("Internal Server Error");
res.status(500).json(serverError.toResponseObject());
}
return;
}
// Phase 2: Validate other parameters. Any errors here should go into redirect responses.
let state;
try {
// Parse and validate authorization parameters
const parseResult = RequestAuthorizationParamsSchema.safeParse(req.method === 'POST' ? req.body : req.query);
if (!parseResult.success) {
throw new InvalidRequestError(parseResult.error.message);
}
const { scope, code_challenge, resource } = parseResult.data;
state = parseResult.data.state;
// Validate scopes
let requestedScopes = [];
if (scope !== undefined) {
requestedScopes = scope.split(" ");
const allowedScopes = new Set((_a = client.scope) === null || _a === void 0 ? void 0 : _a.split(" "));
// Check each requested scope against allowed scopes
for (const scope of requestedScopes) {
if (!allowedScopes.has(scope)) {
throw new InvalidScopeError(`Client was not registered with scope ${scope}`);
}
}
}
// All validation passed, proceed with authorization
await provider.authorize(client, {
state,
scopes: requestedScopes,
redirectUri: redirect_uri,
codeChallenge: code_challenge,
resource: resource ? new URL(resource) : undefined,
}, res);
}
catch (error) {
// Post-redirect errors - redirect with error parameters
if (error instanceof OAuthError) {
res.redirect(302, createErrorRedirect(redirect_uri, error, state));
}
else {
const serverError = new ServerError("Internal Server Error");
res.redirect(302, createErrorRedirect(redirect_uri, serverError, state));
}
}
});
return router;
}
/**
* Helper function to create redirect URL with error parameters
*/
function createErrorRedirect(redirectUri, error, state) {
const errorUrl = new URL(redirectUri);
errorUrl.searchParams.set("error", error.errorCode);
errorUrl.searchParams.set("error_description", error.message);
if (error.errorUri) {
errorUrl.searchParams.set("error_uri", error.errorUri);
}
if (state) {
errorUrl.searchParams.set("state", state);
}
return errorUrl.href;
}
//# sourceMappingURL=authorize.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
import { RequestHandler } from "express";
import { OAuthMetadata, OAuthProtectedResourceMetadata } from "../../../shared/auth.js";
export declare function metadataHandler(metadata: OAuthMetadata | OAuthProtectedResourceMetadata): RequestHandler;
//# sourceMappingURL=metadata.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/metadata.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,8BAA8B,EAAE,MAAM,yBAAyB,CAAC;AAIxF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,aAAa,GAAG,8BAA8B,GAAG,cAAc,CAaxG"}

View File

@@ -0,0 +1,15 @@
import express from "express";
import cors from 'cors';
import { allowedMethods } from "../middleware/allowedMethods.js";
export function metadataHandler(metadata) {
// Nested router so we can configure middleware and restrict HTTP method
const router = express.Router();
// Configure CORS to allow any origin, to make accessible to web-based MCP clients
router.use(cors());
router.use(allowedMethods(['GET']));
router.get("/", (req, res) => {
res.status(200).json(metadata);
});
return router;
}
//# sourceMappingURL=metadata.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,OAA2B,MAAM,SAAS,CAAC;AAElD,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE,MAAM,UAAU,eAAe,CAAC,QAAwD;IACtF,wEAAwE;IACxE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,kFAAkF;IAClF,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAEnB,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC3B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}

View File

@@ -0,0 +1,23 @@
import { RequestHandler } from "express";
import { OAuthRegisteredClientsStore } from "../clients.js";
import { Options as RateLimitOptions } from "express-rate-limit";
export type ClientRegistrationHandlerOptions = {
/**
* A store used to save information about dynamically registered OAuth clients.
*/
clientsStore: OAuthRegisteredClientsStore;
/**
* The number of seconds after which to expire issued client secrets, or 0 to prevent expiration of client secrets (not recommended).
*
* If not set, defaults to 30 days.
*/
clientSecretExpirySeconds?: number;
/**
* Rate limiting configuration for the client registration endpoint.
* Set to false to disable rate limiting for this endpoint.
* Registration endpoints are particularly sensitive to abuse and should be rate limited.
*/
rateLimit?: Partial<RateLimitOptions> | false;
};
export declare function clientRegistrationHandler({ clientsStore, clientSecretExpirySeconds, rateLimit: rateLimitConfig }: ClientRegistrationHandlerOptions): RequestHandler;
//# sourceMappingURL=register.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/register.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAIlD,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAa,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAS5E,MAAM,MAAM,gCAAgC,GAAG;IAC7C;;OAEG;IACH,YAAY,EAAE,2BAA2B,CAAC;IAE1C;;;;OAIG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAEnC;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,KAAK,CAAC;CAC/C,CAAC;AAIF,wBAAgB,yBAAyB,CAAC,EACxC,YAAY,EACZ,yBAAgE,EAChE,SAAS,EAAE,eAAe,EAC3B,EAAE,gCAAgC,GAAG,cAAc,CAwEnD"}

View File

@@ -0,0 +1,72 @@
import express from "express";
import { OAuthClientMetadataSchema } from "../../../shared/auth.js";
import crypto from 'node:crypto';
import cors from 'cors';
import { rateLimit } from "express-rate-limit";
import { allowedMethods } from "../middleware/allowedMethods.js";
import { InvalidClientMetadataError, ServerError, TooManyRequestsError, OAuthError } from "../errors.js";
const DEFAULT_CLIENT_SECRET_EXPIRY_SECONDS = 30 * 24 * 60 * 60; // 30 days
export function clientRegistrationHandler({ clientsStore, clientSecretExpirySeconds = DEFAULT_CLIENT_SECRET_EXPIRY_SECONDS, rateLimit: rateLimitConfig }) {
if (!clientsStore.registerClient) {
throw new Error("Client registration store does not support registering clients");
}
// Nested router so we can configure middleware and restrict HTTP method
const router = express.Router();
// Configure CORS to allow any origin, to make accessible to web-based MCP clients
router.use(cors());
router.use(allowedMethods(["POST"]));
router.use(express.json());
// Apply rate limiting unless explicitly disabled - stricter limits for registration
if (rateLimitConfig !== false) {
router.use(rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 20, // 20 requests per hour - stricter as registration is sensitive
standardHeaders: true,
legacyHeaders: false,
message: new TooManyRequestsError('You have exceeded the rate limit for client registration requests').toResponseObject(),
...rateLimitConfig
}));
}
router.post("/", async (req, res) => {
res.setHeader('Cache-Control', 'no-store');
try {
const parseResult = OAuthClientMetadataSchema.safeParse(req.body);
if (!parseResult.success) {
throw new InvalidClientMetadataError(parseResult.error.message);
}
const clientMetadata = parseResult.data;
const isPublicClient = clientMetadata.token_endpoint_auth_method === 'none';
// Generate client credentials
const clientId = crypto.randomUUID();
const clientSecret = isPublicClient
? undefined
: crypto.randomBytes(32).toString('hex');
const clientIdIssuedAt = Math.floor(Date.now() / 1000);
// Calculate client secret expiry time
const clientsDoExpire = clientSecretExpirySeconds > 0;
const secretExpiryTime = clientsDoExpire ? clientIdIssuedAt + clientSecretExpirySeconds : 0;
const clientSecretExpiresAt = isPublicClient ? undefined : secretExpiryTime;
let clientInfo = {
...clientMetadata,
client_id: clientId,
client_secret: clientSecret,
client_id_issued_at: clientIdIssuedAt,
client_secret_expires_at: clientSecretExpiresAt,
};
clientInfo = await clientsStore.registerClient(clientInfo);
res.status(201).json(clientInfo);
}
catch (error) {
if (error instanceof OAuthError) {
const status = error instanceof ServerError ? 500 : 400;
res.status(status).json(error.toResponseObject());
}
else {
const serverError = new ServerError("Internal Server Error");
res.status(500).json(serverError.toResponseObject());
}
}
});
return router;
}
//# sourceMappingURL=register.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/register.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,13 @@
import { OAuthServerProvider } from "../provider.js";
import { RequestHandler } from "express";
import { Options as RateLimitOptions } from "express-rate-limit";
export type RevocationHandlerOptions = {
provider: OAuthServerProvider;
/**
* Rate limiting configuration for the token revocation endpoint.
* Set to false to disable rate limiting for this endpoint.
*/
rateLimit?: Partial<RateLimitOptions> | false;
};
export declare function revocationHandler({ provider, rateLimit: rateLimitConfig, }: RevocationHandlerOptions): RequestHandler;
//# sourceMappingURL=revoke.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"revoke.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/revoke.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAIlD,OAAO,EAAa,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAS5E,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,KAAK,CAAC;CAC/C,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,SAAS,EAAE,eAAe,GAC3B,EAAE,wBAAwB,GAAG,cAAc,CA8D3C"}

View File

@@ -0,0 +1,59 @@
import express from "express";
import cors from "cors";
import { authenticateClient } from "../middleware/clientAuth.js";
import { OAuthTokenRevocationRequestSchema } from "../../../shared/auth.js";
import { rateLimit } from "express-rate-limit";
import { allowedMethods } from "../middleware/allowedMethods.js";
import { InvalidRequestError, ServerError, TooManyRequestsError, OAuthError, } from "../errors.js";
export function revocationHandler({ provider, rateLimit: rateLimitConfig, }) {
if (!provider.revokeToken) {
throw new Error("Auth provider does not support revoking tokens");
}
// Nested router so we can configure middleware and restrict HTTP method
const router = express.Router();
// Configure CORS to allow any origin, to make accessible to web-based MCP clients
router.use(cors());
router.use(allowedMethods(["POST"]));
router.use(express.urlencoded({ extended: false }));
// Apply rate limiting unless explicitly disabled
if (rateLimitConfig !== false) {
router.use(rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 50, // 50 requests per windowMs
standardHeaders: true,
legacyHeaders: false,
message: new TooManyRequestsError("You have exceeded the rate limit for token revocation requests").toResponseObject(),
...rateLimitConfig,
}));
}
// Authenticate and extract client details
router.use(authenticateClient({ clientsStore: provider.clientsStore }));
router.post("/", async (req, res) => {
res.setHeader("Cache-Control", "no-store");
try {
const parseResult = OAuthTokenRevocationRequestSchema.safeParse(req.body);
if (!parseResult.success) {
throw new InvalidRequestError(parseResult.error.message);
}
const client = req.client;
if (!client) {
// This should never happen
throw new ServerError("Internal Server Error");
}
await provider.revokeToken(client, parseResult.data);
res.status(200).json({});
}
catch (error) {
if (error instanceof OAuthError) {
const status = error instanceof ServerError ? 500 : 400;
res.status(status).json(error.toResponseObject());
}
else {
const serverError = new ServerError("Internal Server Error");
res.status(500).json(serverError.toResponseObject());
}
}
});
return router;
}
//# sourceMappingURL=revoke.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"revoke.js","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/revoke.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,13 @@
import { RequestHandler } from "express";
import { OAuthServerProvider } from "../provider.js";
import { Options as RateLimitOptions } from "express-rate-limit";
export type TokenHandlerOptions = {
provider: OAuthServerProvider;
/**
* Rate limiting configuration for the token endpoint.
* Set to false to disable rate limiting for this endpoint.
*/
rateLimit?: Partial<RateLimitOptions> | false;
};
export declare function tokenHandler({ provider, rateLimit: rateLimitConfig }: TokenHandlerOptions): RequestHandler;
//# sourceMappingURL=token.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/token.ts"],"names":[],"mappings":"AACA,OAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAIrD,OAAO,EAAa,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAW5E,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,mBAAmB,CAAC;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,KAAK,CAAC;CAC/C,CAAC;AAmBF,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,EAAE,mBAAmB,GAAG,cAAc,CA4G1G"}

View File

@@ -0,0 +1,107 @@
import { z } from "zod";
import express from "express";
import cors from "cors";
import { verifyChallenge } from "pkce-challenge";
import { authenticateClient } from "../middleware/clientAuth.js";
import { rateLimit } from "express-rate-limit";
import { allowedMethods } from "../middleware/allowedMethods.js";
import { InvalidRequestError, InvalidGrantError, UnsupportedGrantTypeError, ServerError, TooManyRequestsError, OAuthError } from "../errors.js";
const TokenRequestSchema = z.object({
grant_type: z.string(),
});
const AuthorizationCodeGrantSchema = z.object({
code: z.string(),
code_verifier: z.string(),
redirect_uri: z.string().optional(),
resource: z.string().url().optional(),
});
const RefreshTokenGrantSchema = z.object({
refresh_token: z.string(),
scope: z.string().optional(),
resource: z.string().url().optional(),
});
export function tokenHandler({ provider, rateLimit: rateLimitConfig }) {
// Nested router so we can configure middleware and restrict HTTP method
const router = express.Router();
// Configure CORS to allow any origin, to make accessible to web-based MCP clients
router.use(cors());
router.use(allowedMethods(["POST"]));
router.use(express.urlencoded({ extended: false }));
// Apply rate limiting unless explicitly disabled
if (rateLimitConfig !== false) {
router.use(rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 50, // 50 requests per windowMs
standardHeaders: true,
legacyHeaders: false,
message: new TooManyRequestsError('You have exceeded the rate limit for token requests').toResponseObject(),
...rateLimitConfig
}));
}
// Authenticate and extract client details
router.use(authenticateClient({ clientsStore: provider.clientsStore }));
router.post("/", async (req, res) => {
res.setHeader('Cache-Control', 'no-store');
try {
const parseResult = TokenRequestSchema.safeParse(req.body);
if (!parseResult.success) {
throw new InvalidRequestError(parseResult.error.message);
}
const { grant_type } = parseResult.data;
const client = req.client;
if (!client) {
// This should never happen
throw new ServerError("Internal Server Error");
}
switch (grant_type) {
case "authorization_code": {
const parseResult = AuthorizationCodeGrantSchema.safeParse(req.body);
if (!parseResult.success) {
throw new InvalidRequestError(parseResult.error.message);
}
const { code, code_verifier, redirect_uri, resource } = parseResult.data;
const skipLocalPkceValidation = provider.skipLocalPkceValidation;
// Perform local PKCE validation unless explicitly skipped
// (e.g. to validate code_verifier in upstream server)
if (!skipLocalPkceValidation) {
const codeChallenge = await provider.challengeForAuthorizationCode(client, code);
if (!(await verifyChallenge(code_verifier, codeChallenge))) {
throw new InvalidGrantError("code_verifier does not match the challenge");
}
}
// Passes the code_verifier to the provider if PKCE validation didn't occur locally
const tokens = await provider.exchangeAuthorizationCode(client, code, skipLocalPkceValidation ? code_verifier : undefined, redirect_uri, resource ? new URL(resource) : undefined);
res.status(200).json(tokens);
break;
}
case "refresh_token": {
const parseResult = RefreshTokenGrantSchema.safeParse(req.body);
if (!parseResult.success) {
throw new InvalidRequestError(parseResult.error.message);
}
const { refresh_token, scope, resource } = parseResult.data;
const scopes = scope === null || scope === void 0 ? void 0 : scope.split(" ");
const tokens = await provider.exchangeRefreshToken(client, refresh_token, scopes, resource ? new URL(resource) : undefined);
res.status(200).json(tokens);
break;
}
// Not supported right now
//case "client_credentials":
default:
throw new UnsupportedGrantTypeError("The grant type is not supported by this authorization server.");
}
}
catch (error) {
if (error instanceof OAuthError) {
const status = error instanceof ServerError ? 500 : 400;
res.status(status).json(error.toResponseObject());
}
else {
const serverError = new ServerError("Internal Server Error");
res.status(500).json(serverError.toResponseObject());
}
}
});
return router;
}
//# sourceMappingURL=token.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"token.js","sourceRoot":"","sources":["../../../../../src/server/auth/handlers/token.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,9 @@
import { RequestHandler } from "express";
/**
* Middleware to handle unsupported HTTP methods with a 405 Method Not Allowed response.
*
* @param allowedMethods Array of allowed HTTP methods for this endpoint (e.g., ['GET', 'POST'])
* @returns Express middleware that returns a 405 error if method not in allowed list
*/
export declare function allowedMethods(allowedMethods: string[]): RequestHandler;
//# sourceMappingURL=allowedMethods.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"allowedMethods.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/middleware/allowedMethods.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGzC;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,cAAc,CAYvE"}

View File

@@ -0,0 +1,20 @@
import { MethodNotAllowedError } from "../errors.js";
/**
* Middleware to handle unsupported HTTP methods with a 405 Method Not Allowed response.
*
* @param allowedMethods Array of allowed HTTP methods for this endpoint (e.g., ['GET', 'POST'])
* @returns Express middleware that returns a 405 error if method not in allowed list
*/
export function allowedMethods(allowedMethods) {
return (req, res, next) => {
if (allowedMethods.includes(req.method)) {
next();
return;
}
const error = new MethodNotAllowedError(`The method ${req.method} is not allowed for this endpoint`);
res.status(405)
.set('Allow', allowedMethods.join(', '))
.json(error.toResponseObject());
};
}
//# sourceMappingURL=allowedMethods.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"allowedMethods.js","sourceRoot":"","sources":["../../../../../src/server/auth/middleware/allowedMethods.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAErD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,cAAwB;IACrD,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxB,IAAI,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACxC,IAAI,EAAE,CAAC;YACP,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,qBAAqB,CAAC,cAAc,GAAG,CAAC,MAAM,mCAAmC,CAAC,CAAC;QACrG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC;aACZ,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aACvC,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACpC,CAAC,CAAC;AACJ,CAAC"}

View File

@@ -0,0 +1,35 @@
import { RequestHandler } from "express";
import { OAuthTokenVerifier } from "../provider.js";
import { AuthInfo } from "../types.js";
export type BearerAuthMiddlewareOptions = {
/**
* A provider used to verify tokens.
*/
verifier: OAuthTokenVerifier;
/**
* Optional scopes that the token must have.
*/
requiredScopes?: string[];
/**
* Optional resource metadata URL to include in WWW-Authenticate header.
*/
resourceMetadataUrl?: string;
};
declare module "express-serve-static-core" {
interface Request {
/**
* Information about the validated access token, if the `requireBearerAuth` middleware was used.
*/
auth?: AuthInfo;
}
}
/**
* Middleware that requires a valid Bearer token in the Authorization header.
*
* This will validate the token with the auth provider and add the resulting auth info to the request object.
*
* If resourceMetadataUrl is provided, it will be included in the WWW-Authenticate header
* for 401 responses as per the OAuth 2.0 Protected Resource Metadata spec.
*/
export declare function requireBearerAuth({ verifier, requiredScopes, resourceMetadataUrl }: BearerAuthMiddlewareOptions): RequestHandler;
//# sourceMappingURL=bearerAuth.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"bearerAuth.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/middleware/bearerAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,MAAM,2BAA2B,GAAG;IACxC;;OAEG;IACH,QAAQ,EAAE,kBAAkB,CAAC;IAE7B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAE1B;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,OAAO,QAAQ,2BAA2B,CAAC;IACzC,UAAU,OAAO;QACf;;WAEG;QACH,IAAI,CAAC,EAAE,QAAQ,CAAC;KACjB;CACF;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,cAAmB,EAAE,mBAAmB,EAAE,EAAE,2BAA2B,GAAG,cAAc,CAwDrI"}

View File

@@ -0,0 +1,64 @@
import { InsufficientScopeError, InvalidTokenError, OAuthError, ServerError } from "../errors.js";
/**
* Middleware that requires a valid Bearer token in the Authorization header.
*
* This will validate the token with the auth provider and add the resulting auth info to the request object.
*
* If resourceMetadataUrl is provided, it will be included in the WWW-Authenticate header
* for 401 responses as per the OAuth 2.0 Protected Resource Metadata spec.
*/
export function requireBearerAuth({ verifier, requiredScopes = [], resourceMetadataUrl }) {
return async (req, res, next) => {
try {
const authHeader = req.headers.authorization;
if (!authHeader) {
throw new InvalidTokenError("Missing Authorization header");
}
const [type, token] = authHeader.split(' ');
if (type.toLowerCase() !== 'bearer' || !token) {
throw new InvalidTokenError("Invalid Authorization header format, expected 'Bearer TOKEN'");
}
const authInfo = await verifier.verifyAccessToken(token);
// Check if token has the required scopes (if any)
if (requiredScopes.length > 0) {
const hasAllScopes = requiredScopes.every(scope => authInfo.scopes.includes(scope));
if (!hasAllScopes) {
throw new InsufficientScopeError("Insufficient scope");
}
}
// Check if the token is expired
if (!!authInfo.expiresAt && authInfo.expiresAt < Date.now() / 1000) {
throw new InvalidTokenError("Token has expired");
}
req.auth = authInfo;
next();
}
catch (error) {
if (error instanceof InvalidTokenError) {
const wwwAuthValue = resourceMetadataUrl
? `Bearer error="${error.errorCode}", error_description="${error.message}", resource_metadata="${resourceMetadataUrl}"`
: `Bearer error="${error.errorCode}", error_description="${error.message}"`;
res.set("WWW-Authenticate", wwwAuthValue);
res.status(401).json(error.toResponseObject());
}
else if (error instanceof InsufficientScopeError) {
const wwwAuthValue = resourceMetadataUrl
? `Bearer error="${error.errorCode}", error_description="${error.message}", resource_metadata="${resourceMetadataUrl}"`
: `Bearer error="${error.errorCode}", error_description="${error.message}"`;
res.set("WWW-Authenticate", wwwAuthValue);
res.status(403).json(error.toResponseObject());
}
else if (error instanceof ServerError) {
res.status(500).json(error.toResponseObject());
}
else if (error instanceof OAuthError) {
res.status(400).json(error.toResponseObject());
}
else {
const serverError = new ServerError("Internal Server Error");
res.status(500).json(serverError.toResponseObject());
}
}
};
}
//# sourceMappingURL=bearerAuth.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"bearerAuth.js","sourceRoot":"","sources":["../../../../../src/server/auth/middleware/bearerAuth.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,19 @@
import { RequestHandler } from "express";
import { OAuthRegisteredClientsStore } from "../clients.js";
import { OAuthClientInformationFull } from "../../../shared/auth.js";
export type ClientAuthenticationMiddlewareOptions = {
/**
* A store used to read information about registered OAuth clients.
*/
clientsStore: OAuthRegisteredClientsStore;
};
declare module "express-serve-static-core" {
interface Request {
/**
* The authenticated client for this request, if the `authenticateClient` middleware was used.
*/
client?: OAuthClientInformationFull;
}
}
export declare function authenticateClient({ clientsStore }: ClientAuthenticationMiddlewareOptions): RequestHandler;
//# sourceMappingURL=clientAuth.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"clientAuth.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/middleware/clientAuth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAGrE,MAAM,MAAM,qCAAqC,GAAG;IAClD;;OAEG;IACH,YAAY,EAAE,2BAA2B,CAAC;CAC3C,CAAA;AAOD,OAAO,QAAQ,2BAA2B,CAAC;IACzC,UAAU,OAAO;QACf;;WAEG;QACH,MAAM,CAAC,EAAE,0BAA0B,CAAC;KACrC;CACF;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,EAAE,qCAAqC,GAAG,cAAc,CA4C1G"}

View File

@@ -0,0 +1,49 @@
import { z } from "zod";
import { InvalidRequestError, InvalidClientError, ServerError, OAuthError } from "../errors.js";
const ClientAuthenticatedRequestSchema = z.object({
client_id: z.string(),
client_secret: z.string().optional(),
});
export function authenticateClient({ clientsStore }) {
return async (req, res, next) => {
try {
const result = ClientAuthenticatedRequestSchema.safeParse(req.body);
if (!result.success) {
throw new InvalidRequestError(String(result.error));
}
const { client_id, client_secret } = result.data;
const client = await clientsStore.getClient(client_id);
if (!client) {
throw new InvalidClientError("Invalid client_id");
}
// If client has a secret, validate it
if (client.client_secret) {
// Check if client_secret is required but not provided
if (!client_secret) {
throw new InvalidClientError("Client secret is required");
}
// Check if client_secret matches
if (client.client_secret !== client_secret) {
throw new InvalidClientError("Invalid client_secret");
}
// Check if client_secret has expired
if (client.client_secret_expires_at && client.client_secret_expires_at < Math.floor(Date.now() / 1000)) {
throw new InvalidClientError("Client secret has expired");
}
}
req.client = client;
next();
}
catch (error) {
if (error instanceof OAuthError) {
const status = error instanceof ServerError ? 500 : 400;
res.status(status).json(error.toResponseObject());
}
else {
const serverError = new ServerError("Internal Server Error");
res.status(500).json(serverError.toResponseObject());
}
}
};
}
//# sourceMappingURL=clientAuth.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"clientAuth.js","sourceRoot":"","sources":["../../../../../src/server/auth/middleware/clientAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAShG,MAAM,gCAAgC,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC;AAWH,MAAM,UAAU,kBAAkB,CAAC,EAAE,YAAY,EAAyC;IACxF,OAAO,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC9B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,gCAAgC,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACpE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM,IAAI,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACtD,CAAC;YAED,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;YACjD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YACvD,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,kBAAkB,CAAC,mBAAmB,CAAC,CAAC;YACpD,CAAC;YAED,sCAAsC;YACtC,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;gBACzB,sDAAsD;gBACtD,IAAI,CAAC,aAAa,EAAE,CAAC;oBACnB,MAAM,IAAI,kBAAkB,CAAC,2BAA2B,CAAC,CAAC;gBAC5D,CAAC;gBAED,iCAAiC;gBACjC,IAAI,MAAM,CAAC,aAAa,KAAK,aAAa,EAAE,CAAC;oBAC3C,MAAM,IAAI,kBAAkB,CAAC,uBAAuB,CAAC,CAAC;gBACxD,CAAC;gBAED,qCAAqC;gBACrC,IAAI,MAAM,CAAC,wBAAwB,IAAI,MAAM,CAAC,wBAAwB,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;oBACvG,MAAM,IAAI,kBAAkB,CAAC,2BAA2B,CAAC,CAAC;gBAC5D,CAAC;YACH,CAAC;YAED,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;YACpB,IAAI,EAAE,CAAC;QACT,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,UAAU,EAAE,CAAC;gBAChC,MAAM,MAAM,GAAG,KAAK,YAAY,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;gBACxD,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC;YACpD,CAAC;iBAAM,CAAC;gBACN,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,uBAAuB,CAAC,CAAC;gBAC7D,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,gBAAgB,EAAE,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;IACH,CAAC,CAAA;AACH,CAAC"}

View File

@@ -0,0 +1,68 @@
import { Response } from "express";
import { OAuthRegisteredClientsStore } from "./clients.js";
import { OAuthClientInformationFull, OAuthTokenRevocationRequest, OAuthTokens } from "../../shared/auth.js";
import { AuthInfo } from "./types.js";
export type AuthorizationParams = {
state?: string;
scopes?: string[];
codeChallenge: string;
redirectUri: string;
resource?: URL;
};
/**
* Implements an end-to-end OAuth server.
*/
export interface OAuthServerProvider {
/**
* A store used to read information about registered OAuth clients.
*/
get clientsStore(): OAuthRegisteredClientsStore;
/**
* Begins the authorization flow, which can either be implemented by this server itself or via redirection to a separate authorization server.
*
* This server must eventually issue a redirect with an authorization response or an error response to the given redirect URI. Per OAuth 2.1:
* - In the successful case, the redirect MUST include the `code` and `state` (if present) query parameters.
* - In the error case, the redirect MUST include the `error` query parameter, and MAY include an optional `error_description` query parameter.
*/
authorize(client: OAuthClientInformationFull, params: AuthorizationParams, res: Response): Promise<void>;
/**
* Returns the `codeChallenge` that was used when the indicated authorization began.
*/
challengeForAuthorizationCode(client: OAuthClientInformationFull, authorizationCode: string): Promise<string>;
/**
* Exchanges an authorization code for an access token.
*/
exchangeAuthorizationCode(client: OAuthClientInformationFull, authorizationCode: string, codeVerifier?: string, redirectUri?: string, resource?: URL): Promise<OAuthTokens>;
/**
* Exchanges a refresh token for an access token.
*/
exchangeRefreshToken(client: OAuthClientInformationFull, refreshToken: string, scopes?: string[], resource?: URL): Promise<OAuthTokens>;
/**
* Verifies an access token and returns information about it.
*/
verifyAccessToken(token: string): Promise<AuthInfo>;
/**
* Revokes an access or refresh token. If unimplemented, token revocation is not supported (not recommended).
*
* If the given token is invalid or already revoked, this method should do nothing.
*/
revokeToken?(client: OAuthClientInformationFull, request: OAuthTokenRevocationRequest): Promise<void>;
/**
* Whether to skip local PKCE validation.
*
* If true, the server will not perform PKCE validation locally and will pass the code_verifier to the upstream server.
*
* NOTE: This should only be true if the upstream server is performing the actual PKCE validation.
*/
skipLocalPkceValidation?: boolean;
}
/**
* Slim implementation useful for token verification
*/
export interface OAuthTokenVerifier {
/**
* Verifies an access token and returns information about it.
*/
verifyAccessToken(token: string): Promise<AuthInfo>;
}
//# sourceMappingURL=provider.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../../src/server/auth/provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC5G,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,GAAG,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,IAAI,YAAY,IAAI,2BAA2B,CAAC;IAEhD;;;;;;OAMG;IACH,SAAS,CAAC,MAAM,EAAE,0BAA0B,EAAE,MAAM,EAAE,mBAAmB,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzG;;OAEG;IACH,6BAA6B,CAAC,MAAM,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAE9G;;OAEG;IACH,yBAAyB,CACvB,MAAM,EAAE,0BAA0B,EAClC,iBAAiB,EAAE,MAAM,EACzB,YAAY,CAAC,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,GAAG,GACb,OAAO,CAAC,WAAW,CAAC,CAAC;IAExB;;OAEG;IACH,oBAAoB,CAAC,MAAM,EAAE,0BAA0B,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAExI;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAEpD;;;;OAIG;IACH,WAAW,CAAC,CAAC,MAAM,EAAE,0BAA0B,EAAE,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtG;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAGD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACrD"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=provider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../../src/server/auth/provider.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,43 @@
import { Response } from "express";
import { OAuthRegisteredClientsStore } from "../clients.js";
import { OAuthClientInformationFull, OAuthTokenRevocationRequest, OAuthTokens } from "../../../shared/auth.js";
import { AuthInfo } from "../types.js";
import { AuthorizationParams, OAuthServerProvider } from "../provider.js";
export type ProxyEndpoints = {
authorizationUrl: string;
tokenUrl: string;
revocationUrl?: string;
registrationUrl?: string;
};
export type ProxyOptions = {
/**
* Individual endpoint URLs for proxying specific OAuth operations
*/
endpoints: ProxyEndpoints;
/**
* Function to verify access tokens and return auth info
*/
verifyAccessToken: (token: string) => Promise<AuthInfo>;
/**
* Function to fetch client information from the upstream server
*/
getClient: (clientId: string) => Promise<OAuthClientInformationFull | undefined>;
};
/**
* Implements an OAuth server that proxies requests to another OAuth server.
*/
export declare class ProxyOAuthServerProvider implements OAuthServerProvider {
protected readonly _endpoints: ProxyEndpoints;
protected readonly _verifyAccessToken: (token: string) => Promise<AuthInfo>;
protected readonly _getClient: (clientId: string) => Promise<OAuthClientInformationFull | undefined>;
skipLocalPkceValidation: boolean;
revokeToken?: (client: OAuthClientInformationFull, request: OAuthTokenRevocationRequest) => Promise<void>;
constructor(options: ProxyOptions);
get clientsStore(): OAuthRegisteredClientsStore;
authorize(client: OAuthClientInformationFull, params: AuthorizationParams, res: Response): Promise<void>;
challengeForAuthorizationCode(_client: OAuthClientInformationFull, _authorizationCode: string): Promise<string>;
exchangeAuthorizationCode(client: OAuthClientInformationFull, authorizationCode: string, codeVerifier?: string, redirectUri?: string, resource?: URL): Promise<OAuthTokens>;
exchangeRefreshToken(client: OAuthClientInformationFull, refreshToken: string, scopes?: string[], resource?: URL): Promise<OAuthTokens>;
verifyAccessToken(token: string): Promise<AuthInfo>;
}
//# sourceMappingURL=proxyProvider.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"proxyProvider.d.ts","sourceRoot":"","sources":["../../../../../src/server/auth/providers/proxyProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EACL,0BAA0B,EAE1B,2BAA2B,EAC3B,WAAW,EAEZ,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAG1E,MAAM,MAAM,cAAc,GAAG;IAC3B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB;;OAEG;IACH,SAAS,EAAE,cAAc,CAAC;IAE1B;;MAEE;IACF,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAExD;;MAEE;IACF,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,GAAG,SAAS,CAAC,CAAC;CAElF,CAAC;AAEF;;GAEG;AACH,qBAAa,wBAAyB,YAAW,mBAAmB;IAClE,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IAC9C,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC5E,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,0BAA0B,GAAG,SAAS,CAAC,CAAC;IAErG,uBAAuB,UAAQ;IAE/B,WAAW,CAAC,EAAE,CACZ,MAAM,EAAE,0BAA0B,EAClC,OAAO,EAAE,2BAA2B,KACjC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAEP,OAAO,EAAE,YAAY;IAwCjC,IAAI,YAAY,IAAI,2BAA2B,CAuB9C;IAEK,SAAS,CACb,MAAM,EAAE,0BAA0B,EAClC,MAAM,EAAE,mBAAmB,EAC3B,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC,IAAI,CAAC;IAoBV,6BAA6B,CACjC,OAAO,EAAE,0BAA0B,EACnC,kBAAkB,EAAE,MAAM,GACzB,OAAO,CAAC,MAAM,CAAC;IAMZ,yBAAyB,CAC7B,MAAM,EAAE,0BAA0B,EAClC,iBAAiB,EAAE,MAAM,EACzB,YAAY,CAAC,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,GAAG,GACb,OAAO,CAAC,WAAW,CAAC;IAwCjB,oBAAoB,CACxB,MAAM,EAAE,0BAA0B,EAClC,YAAY,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,MAAM,EAAE,EACjB,QAAQ,CAAC,EAAE,GAAG,GACb,OAAO,CAAC,WAAW,CAAC;IAoCjB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;CAG1D"}

View File

@@ -0,0 +1,152 @@
import { OAuthClientInformationFullSchema, OAuthTokensSchema, } from "../../../shared/auth.js";
import { ServerError } from "../errors.js";
/**
* Implements an OAuth server that proxies requests to another OAuth server.
*/
export class ProxyOAuthServerProvider {
constructor(options) {
var _a;
this.skipLocalPkceValidation = true;
this._endpoints = options.endpoints;
this._verifyAccessToken = options.verifyAccessToken;
this._getClient = options.getClient;
if ((_a = options.endpoints) === null || _a === void 0 ? void 0 : _a.revocationUrl) {
this.revokeToken = async (client, request) => {
const revocationUrl = this._endpoints.revocationUrl;
if (!revocationUrl) {
throw new Error("No revocation endpoint configured");
}
const params = new URLSearchParams();
params.set("token", request.token);
params.set("client_id", client.client_id);
if (client.client_secret) {
params.set("client_secret", client.client_secret);
}
if (request.token_type_hint) {
params.set("token_type_hint", request.token_type_hint);
}
const response = await fetch(revocationUrl, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: params.toString(),
});
if (!response.ok) {
throw new ServerError(`Token revocation failed: ${response.status}`);
}
};
}
}
get clientsStore() {
const registrationUrl = this._endpoints.registrationUrl;
return {
getClient: this._getClient,
...(registrationUrl && {
registerClient: async (client) => {
const response = await fetch(registrationUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(client),
});
if (!response.ok) {
throw new ServerError(`Client registration failed: ${response.status}`);
}
const data = await response.json();
return OAuthClientInformationFullSchema.parse(data);
}
})
};
}
async authorize(client, params, res) {
var _a;
// Start with required OAuth parameters
const targetUrl = new URL(this._endpoints.authorizationUrl);
const searchParams = new URLSearchParams({
client_id: client.client_id,
response_type: "code",
redirect_uri: params.redirectUri,
code_challenge: params.codeChallenge,
code_challenge_method: "S256"
});
// Add optional standard OAuth parameters
if (params.state)
searchParams.set("state", params.state);
if ((_a = params.scopes) === null || _a === void 0 ? void 0 : _a.length)
searchParams.set("scope", params.scopes.join(" "));
if (params.resource)
searchParams.set("resource", params.resource.href);
targetUrl.search = searchParams.toString();
res.redirect(targetUrl.toString());
}
async challengeForAuthorizationCode(_client, _authorizationCode) {
// In a proxy setup, we don't store the code challenge ourselves
// Instead, we proxy the token request and let the upstream server validate it
return "";
}
async exchangeAuthorizationCode(client, authorizationCode, codeVerifier, redirectUri, resource) {
const params = new URLSearchParams({
grant_type: "authorization_code",
client_id: client.client_id,
code: authorizationCode,
});
if (client.client_secret) {
params.append("client_secret", client.client_secret);
}
if (codeVerifier) {
params.append("code_verifier", codeVerifier);
}
if (redirectUri) {
params.append("redirect_uri", redirectUri);
}
if (resource) {
params.append("resource", resource.href);
}
const response = await fetch(this._endpoints.tokenUrl, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: params.toString(),
});
if (!response.ok) {
throw new ServerError(`Token exchange failed: ${response.status}`);
}
const data = await response.json();
return OAuthTokensSchema.parse(data);
}
async exchangeRefreshToken(client, refreshToken, scopes, resource) {
const params = new URLSearchParams({
grant_type: "refresh_token",
client_id: client.client_id,
refresh_token: refreshToken,
});
if (client.client_secret) {
params.set("client_secret", client.client_secret);
}
if (scopes === null || scopes === void 0 ? void 0 : scopes.length) {
params.set("scope", scopes.join(" "));
}
if (resource) {
params.set("resource", resource.href);
}
const response = await fetch(this._endpoints.tokenUrl, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: params.toString(),
});
if (!response.ok) {
throw new ServerError(`Token refresh failed: ${response.status}`);
}
const data = await response.json();
return OAuthTokensSchema.parse(data);
}
async verifyAccessToken(token) {
return this._verifyAccessToken(token);
}
}
//# sourceMappingURL=proxyProvider.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"proxyProvider.js","sourceRoot":"","sources":["../../../../../src/server/auth/providers/proxyProvider.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,gCAAgC,EAGhC,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AA2B3C;;GAEG;AACH,MAAM,OAAO,wBAAwB;IAYnC,YAAY,OAAqB;;QAPjC,4BAAuB,GAAG,IAAI,CAAC;QAQ7B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;QACpC,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;QACpD,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;QACpC,IAAI,MAAA,OAAO,CAAC,SAAS,0CAAE,aAAa,EAAE,CAAC;YACrC,IAAI,CAAC,WAAW,GAAG,KAAK,EACtB,MAAkC,EAClC,OAAoC,EACpC,EAAE;gBACF,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;gBAEpD,IAAI,CAAC,aAAa,EAAE,CAAC;oBACnB,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;gBACvD,CAAC;gBAED,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;gBACrC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;gBACnC,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC1C,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;oBACzB,MAAM,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;gBACpD,CAAC;gBACD,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;oBAC5B,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;gBACzD,CAAC;gBAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,aAAa,EAAE;oBAC1C,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE;wBACP,cAAc,EAAE,mCAAmC;qBACpD;oBACD,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE;iBACxB,CAAC,CAAC;gBAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;oBACjB,MAAM,IAAI,WAAW,CAAC,4BAA4B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;gBACvE,CAAC;YACH,CAAC,CAAA;QACH,CAAC;IACH,CAAC;IAED,IAAI,YAAY;QACd,MAAM,eAAe,GAAG,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC;QACxD,OAAO;YACL,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,GAAG,CAAC,eAAe,IAAI;gBACrB,cAAc,EAAE,KAAK,EAAE,MAAkC,EAAE,EAAE;oBAC3D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,eAAe,EAAE;wBAC5C,MAAM,EAAE,MAAM;wBACd,OAAO,EAAE;4BACP,cAAc,EAAE,kBAAkB;yBACnC;wBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;qBAC7B,CAAC,CAAC;oBAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;wBACjB,MAAM,IAAI,WAAW,CAAC,+BAA+B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;oBAC1E,CAAC;oBAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;oBACnC,OAAO,gCAAgC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACtD,CAAC;aACF,CAAC;SACH,CAAA;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CACb,MAAkC,EAClC,MAA2B,EAC3B,GAAa;;QAEb,uCAAuC;QACvC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;QAC5D,MAAM,YAAY,GAAG,IAAI,eAAe,CAAC;YACvC,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,aAAa,EAAE,MAAM;YACrB,YAAY,EAAE,MAAM,CAAC,WAAW;YAChC,cAAc,EAAE,MAAM,CAAC,aAAa;YACpC,qBAAqB,EAAE,MAAM;SAC9B,CAAC,CAAC;QAEH,yCAAyC;QACzC,IAAI,MAAM,CAAC,KAAK;YAAE,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1D,IAAI,MAAA,MAAM,CAAC,MAAM,0CAAE,MAAM;YAAE,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9E,IAAI,MAAM,CAAC,QAAQ;YAAE,YAAY,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAExE,SAAS,CAAC,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,CAAC;QAC3C,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,6BAA6B,CACjC,OAAmC,EACnC,kBAA0B;QAE1B,gEAAgE;QAChE,8EAA8E;QAC9E,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,yBAAyB,CAC7B,MAAkC,EAClC,iBAAyB,EACzB,YAAqB,EACrB,WAAoB,EACpB,QAAc;QAEd,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YACjC,UAAU,EAAE,oBAAoB;YAChC,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,IAAI,EAAE,iBAAiB;SACxB,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;YACzB,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;QACvD,CAAC;QAED,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;QAC/C,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;QAC7C,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE;YACrD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,mCAAmC;aACpD;YACD,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE;SACxB,CAAC,CAAC;QAGH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,WAAW,CAAC,0BAA0B,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,OAAO,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,oBAAoB,CACxB,MAAkC,EAClC,YAAoB,EACpB,MAAiB,EACjB,QAAc;QAGd,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YACjC,UAAU,EAAE,eAAe;YAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,aAAa,EAAE,YAAY;SAC5B,CAAC,CAAC;QAEH,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;YACzB,MAAM,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;QACpD,CAAC;QAED,IAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,MAAM,EAAE,CAAC;YACnB,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACxC,CAAC;QAED,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE;YACrD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,mCAAmC;aACpD;YACD,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE;SACxB,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,WAAW,CAAC,yBAAyB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnC,OAAO,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,KAAa;QACnC,OAAO,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC;CACF"}

View File

@@ -0,0 +1,96 @@
import { RequestHandler } from "express";
import { ClientRegistrationHandlerOptions } from "./handlers/register.js";
import { TokenHandlerOptions } from "./handlers/token.js";
import { AuthorizationHandlerOptions } from "./handlers/authorize.js";
import { RevocationHandlerOptions } from "./handlers/revoke.js";
import { OAuthServerProvider } from "./provider.js";
import { OAuthMetadata } from "../../shared/auth.js";
export type AuthRouterOptions = {
/**
* A provider implementing the actual authorization logic for this router.
*/
provider: OAuthServerProvider;
/**
* The authorization server's issuer identifier, which is a URL that uses the "https" scheme and has no query or fragment components.
*/
issuerUrl: URL;
/**
* The base URL of the authorization server to use for the metadata endpoints.
*
* If not provided, the issuer URL will be used as the base URL.
*/
baseUrl?: URL;
/**
* An optional URL of a page containing human-readable information that developers might want or need to know when using the authorization server.
*/
serviceDocumentationUrl?: URL;
/**
* An optional list of scopes supported by this authorization server
*/
scopesSupported?: string[];
/**
* The resource name to be displayed in protected resource metadata
*/
resourceName?: string;
authorizationOptions?: Omit<AuthorizationHandlerOptions, "provider">;
clientRegistrationOptions?: Omit<ClientRegistrationHandlerOptions, "clientsStore">;
revocationOptions?: Omit<RevocationHandlerOptions, "provider">;
tokenOptions?: Omit<TokenHandlerOptions, "provider">;
};
export declare const createOAuthMetadata: (options: {
provider: OAuthServerProvider;
issuerUrl: URL;
baseUrl?: URL;
serviceDocumentationUrl?: URL;
scopesSupported?: string[];
}) => OAuthMetadata;
/**
* Installs standard MCP authorization server endpoints, including dynamic client registration and token revocation (if supported).
* Also advertises standard authorization server metadata, for easier discovery of supported configurations by clients.
* Note: if your MCP server is only a resource server and not an authorization server, use mcpAuthMetadataRouter instead.
*
* By default, rate limiting is applied to all endpoints to prevent abuse.
*
* This router MUST be installed at the application root, like so:
*
* const app = express();
* app.use(mcpAuthRouter(...));
*/
export declare function mcpAuthRouter(options: AuthRouterOptions): RequestHandler;
export type AuthMetadataOptions = {
/**
* OAuth Metadata as would be returned from the authorization server
* this MCP server relies on
*/
oauthMetadata: OAuthMetadata;
/**
* The url of the MCP server, for use in protected resource metadata
*/
resourceServerUrl: URL;
/**
* The url for documentation for the MCP server
*/
serviceDocumentationUrl?: URL;
/**
* An optional list of scopes supported by this MCP server
*/
scopesSupported?: string[];
/**
* An optional resource name to display in resource metadata
*/
resourceName?: string;
};
export declare function mcpAuthMetadataRouter(options: AuthMetadataOptions): import("express-serve-static-core").Router;
/**
* Helper function to construct the OAuth 2.0 Protected Resource Metadata URL
* from a given server URL. This replaces the path with the standard metadata endpoint.
*
* @param serverUrl - The base URL of the protected resource server
* @returns The URL for the OAuth protected resource metadata endpoint
*
* @example
* getOAuthProtectedResourceMetadataUrl(new URL('https://api.example.com/mcp'))
* // Returns: 'https://api.example.com/.well-known/oauth-protected-resource'
*/
export declare function getOAuthProtectedResourceMetadataUrl(serverUrl: URL): string;
//# sourceMappingURL=router.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../../src/server/auth/router.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,EAA6B,gCAAgC,EAAE,MAAM,wBAAwB,CAAC;AACrG,OAAO,EAAgB,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAwB,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAC5F,OAAO,EAAqB,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAEnF,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,aAAa,EAAkC,MAAM,sBAAsB,CAAC;AAErF,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;OAEG;IACH,QAAQ,EAAE,mBAAmB,CAAC;IAE9B;;OAEG;IACH,SAAS,EAAE,GAAG,CAAC;IAEf;;;;OAIG;IACH,OAAO,CAAC,EAAE,GAAG,CAAC;IAEd;;OAEG;IACH,uBAAuB,CAAC,EAAE,GAAG,CAAC;IAE9B;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAG3B;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,oBAAoB,CAAC,EAAE,IAAI,CAAC,2BAA2B,EAAE,UAAU,CAAC,CAAC;IACrE,yBAAyB,CAAC,EAAE,IAAI,CAAC,gCAAgC,EAAE,cAAc,CAAC,CAAC;IACnF,iBAAiB,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,UAAU,CAAC,CAAC;IAC/D,YAAY,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,UAAU,CAAC,CAAC;CACtD,CAAC;AAeF,eAAO,MAAM,mBAAmB,YAAa;IAC3C,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,SAAS,EAAE,GAAG,CAAC;IACf,OAAO,CAAC,EAAE,GAAG,CAAA;IACb,uBAAuB,CAAC,EAAE,GAAG,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B,KAAG,aAgCH,CAAA;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CA0CxE;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;OAGG;IACH,aAAa,EAAE,aAAa,CAAC;IAE7B;;OAEG;IACH,iBAAiB,EAAE,GAAG,CAAC;IAEvB;;OAEG;IACH,uBAAuB,CAAC,EAAE,GAAG,CAAC;IAE9B;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAA;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,mBAAmB,8CAuBjE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oCAAoC,CAAC,SAAS,EAAE,GAAG,GAAG,MAAM,CAE3E"}

View File

@@ -0,0 +1,112 @@
import express from "express";
import { clientRegistrationHandler } from "./handlers/register.js";
import { tokenHandler } from "./handlers/token.js";
import { authorizationHandler } from "./handlers/authorize.js";
import { revocationHandler } from "./handlers/revoke.js";
import { metadataHandler } from "./handlers/metadata.js";
const checkIssuerUrl = (issuer) => {
// Technically RFC 8414 does not permit a localhost HTTPS exemption, but this will be necessary for ease of testing
if (issuer.protocol !== "https:" && issuer.hostname !== "localhost" && issuer.hostname !== "127.0.0.1") {
throw new Error("Issuer URL must be HTTPS");
}
if (issuer.hash) {
throw new Error(`Issuer URL must not have a fragment: ${issuer}`);
}
if (issuer.search) {
throw new Error(`Issuer URL must not have a query string: ${issuer}`);
}
};
export const createOAuthMetadata = (options) => {
var _a;
const issuer = options.issuerUrl;
const baseUrl = options.baseUrl;
checkIssuerUrl(issuer);
const authorization_endpoint = "/authorize";
const token_endpoint = "/token";
const registration_endpoint = options.provider.clientsStore.registerClient ? "/register" : undefined;
const revocation_endpoint = options.provider.revokeToken ? "/revoke" : undefined;
const metadata = {
issuer: issuer.href,
service_documentation: (_a = options.serviceDocumentationUrl) === null || _a === void 0 ? void 0 : _a.href,
authorization_endpoint: new URL(authorization_endpoint, baseUrl || issuer).href,
response_types_supported: ["code"],
code_challenge_methods_supported: ["S256"],
token_endpoint: new URL(token_endpoint, baseUrl || issuer).href,
token_endpoint_auth_methods_supported: ["client_secret_post"],
grant_types_supported: ["authorization_code", "refresh_token"],
scopes_supported: options.scopesSupported,
revocation_endpoint: revocation_endpoint ? new URL(revocation_endpoint, baseUrl || issuer).href : undefined,
revocation_endpoint_auth_methods_supported: revocation_endpoint ? ["client_secret_post"] : undefined,
registration_endpoint: registration_endpoint ? new URL(registration_endpoint, baseUrl || issuer).href : undefined,
};
return metadata;
};
/**
* Installs standard MCP authorization server endpoints, including dynamic client registration and token revocation (if supported).
* Also advertises standard authorization server metadata, for easier discovery of supported configurations by clients.
* Note: if your MCP server is only a resource server and not an authorization server, use mcpAuthMetadataRouter instead.
*
* By default, rate limiting is applied to all endpoints to prevent abuse.
*
* This router MUST be installed at the application root, like so:
*
* const app = express();
* app.use(mcpAuthRouter(...));
*/
export function mcpAuthRouter(options) {
const oauthMetadata = createOAuthMetadata(options);
const router = express.Router();
router.use(new URL(oauthMetadata.authorization_endpoint).pathname, authorizationHandler({ provider: options.provider, ...options.authorizationOptions }));
router.use(new URL(oauthMetadata.token_endpoint).pathname, tokenHandler({ provider: options.provider, ...options.tokenOptions }));
router.use(mcpAuthMetadataRouter({
oauthMetadata,
// This router is used for AS+RS combo's, so the issuer is also the resource server
resourceServerUrl: new URL(oauthMetadata.issuer),
serviceDocumentationUrl: options.serviceDocumentationUrl,
scopesSupported: options.scopesSupported,
resourceName: options.resourceName
}));
if (oauthMetadata.registration_endpoint) {
router.use(new URL(oauthMetadata.registration_endpoint).pathname, clientRegistrationHandler({
clientsStore: options.provider.clientsStore,
...options,
}));
}
if (oauthMetadata.revocation_endpoint) {
router.use(new URL(oauthMetadata.revocation_endpoint).pathname, revocationHandler({ provider: options.provider, ...options.revocationOptions }));
}
return router;
}
export function mcpAuthMetadataRouter(options) {
var _a;
checkIssuerUrl(new URL(options.oauthMetadata.issuer));
const router = express.Router();
const protectedResourceMetadata = {
resource: options.resourceServerUrl.href,
authorization_servers: [
options.oauthMetadata.issuer
],
scopes_supported: options.scopesSupported,
resource_name: options.resourceName,
resource_documentation: (_a = options.serviceDocumentationUrl) === null || _a === void 0 ? void 0 : _a.href,
};
router.use("/.well-known/oauth-protected-resource", metadataHandler(protectedResourceMetadata));
// Always add this for backwards compatibility
router.use("/.well-known/oauth-authorization-server", metadataHandler(options.oauthMetadata));
return router;
}
/**
* Helper function to construct the OAuth 2.0 Protected Resource Metadata URL
* from a given server URL. This replaces the path with the standard metadata endpoint.
*
* @param serverUrl - The base URL of the protected resource server
* @returns The URL for the OAuth protected resource metadata endpoint
*
* @example
* getOAuthProtectedResourceMetadataUrl(new URL('https://api.example.com/mcp'))
* // Returns: 'https://api.example.com/.well-known/oauth-protected-resource'
*/
export function getOAuthProtectedResourceMetadataUrl(serverUrl) {
return new URL('/.well-known/oauth-protected-resource', serverUrl).href;
}
//# sourceMappingURL=router.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../../../src/server/auth/router.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,32 @@
/**
* Information about a validated access token, provided to request handlers.
*/
export interface AuthInfo {
/**
* The access token.
*/
token: string;
/**
* The client ID associated with this token.
*/
clientId: string;
/**
* Scopes associated with this token.
*/
scopes: string[];
/**
* When the token expires (in seconds since epoch).
*/
expiresAt?: number;
/**
* The RFC 8707 resource server identifier for which this token is valid.
* If set, this MUST match the MCP server's resource identifier (minus hash fragment).
*/
resource?: URL;
/**
* Additional data associated with the token.
* This field should be used for any additional data that needs to be attached to the auth info.
*/
extra?: Record<string, unknown>;
}
//# sourceMappingURL=types.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/server/auth/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IAEjB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,GAAG,CAAC;IAEf;;;MAGE;IACF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC"}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/server/auth/types.ts"],"names":[],"mappings":""}