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.clientRegistrationOptions,
        }));
    }
    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