@ory/mcp-oauth-provider
Version:
OAuth provider for Ory MCP
1 lines • 20.5 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts"],"names":["OryProvider","options","_a","client","request","revocationUrl","params","response","ServerError","registrationUrl","data","OAuthClientInformationFullSchema","acc","error","clientId","res","state","h","targetUrl","searchParams","resolve","_client","_authorizationCode","authorizationCode","codeVerifier","OAuthTokensSchema","refreshToken","scopes","token","introspection"],"mappings":"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","file":"index.cjs","sourcesContent":["// Copyright © 2025 Ory Corp\n\nimport { OAuthRegisteredClientsStore } from '@modelcontextprotocol/sdk/server/auth/clients.js';\nimport { ServerError } from '@modelcontextprotocol/sdk/server/auth/errors.js';\nimport {\n AuthorizationParams,\n OAuthServerProvider,\n} from '@modelcontextprotocol/sdk/server/auth/provider.js';\nimport { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';\nimport {\n OAuthClientInformationFull,\n OAuthClientInformationFullSchema,\n OAuthTokenRevocationRequest,\n OAuthTokensSchema,\n OAuthTokens,\n} from '@modelcontextprotocol/sdk/shared/auth.js';\nimport * as crypto from 'crypto';\nimport { Response } from 'express';\n\nexport type OryEndpoints = {\n authorizationUrl: string;\n tokenUrl: string;\n revocationUrl?: string;\n registrationUrl?: string;\n};\n\nexport type HydraClient = {\n client_id: string;\n redirect_uris: string[];\n};\n\nexport type OryProviderType = 'network' | 'hydra';\n\nexport type BaseOryOptions = {\n endpoints: OryEndpoints;\n providerType: OryProviderType;\n hydraAdminUrl?: string;\n hydraApiKey?: string;\n networkProjectUrl?: string;\n networkProjectApiKey?: string;\n};\n\nexport type OryOptions = BaseOryOptions;\n\nexport class OryProvider implements OAuthServerProvider {\n protected readonly _endpoints: OryEndpoints;\n protected readonly _providerType: OryProviderType;\n protected readonly _hydraAdminUrl?: string;\n protected readonly _hydraApiKey?: string;\n protected readonly _networkProjectUrl?: string;\n protected readonly _networkProjectApiKey?: string;\n\n skipLocalPasswordGrant = false;\n skipLocalPkceValidation = true;\n\n revokeToken?: (\n client: OAuthClientInformationFull,\n request: OAuthTokenRevocationRequest\n ) => Promise<void>;\n\n constructor(options: OryOptions) {\n this._endpoints = options.endpoints;\n this._providerType = options.providerType;\n this._hydraAdminUrl = options.hydraAdminUrl;\n this._hydraApiKey = options.hydraApiKey;\n this._networkProjectUrl = options.networkProjectUrl;\n this._networkProjectApiKey = options.networkProjectApiKey;\n\n if (options.endpoints?.revocationUrl) {\n this.revokeToken = async (\n client: OAuthClientInformationFull,\n request: OAuthTokenRevocationRequest\n ): Promise<void> => {\n const revocationUrl = this._endpoints.revocationUrl;\n\n if (!revocationUrl) {\n throw new Error('No revocation endpoint configured');\n }\n\n const params = new URLSearchParams();\n params.set('token', request.token);\n params.set('client_id', client.client_id);\n if (client.client_secret) {\n params.set('client_secret', client.client_secret);\n }\n if (request.token_type_hint) {\n params.set('token_type_hint', request.token_type_hint);\n }\n\n const response = await fetch(revocationUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: params.toString(),\n });\n\n if (!response.ok) {\n throw new ServerError(`Token revocation failed: ${response.status}`);\n }\n };\n }\n }\n\n get clientsStore(): OAuthRegisteredClientsStore {\n const registrationUrl = this._endpoints.registrationUrl;\n return {\n getClient: this.getClient.bind(this),\n ...(registrationUrl && {\n registerClient: async (\n client: OAuthClientInformationFull\n ): Promise<OAuthClientInformationFull> => {\n const response = await fetch(registrationUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(client),\n });\n\n if (!response.ok) {\n throw new ServerError(`Client registration failed: ${response.status}`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n return OAuthClientInformationFullSchema.parse(data);\n },\n }),\n };\n }\n\n private async fetchClients(): Promise<OAuthClientInformationFull[]> {\n if (this._providerType === 'hydra') {\n if (!this._hydraAdminUrl) {\n throw new Error('Hydra admin URL is required for hydra provider type');\n }\n const response = await fetch(`${this._hydraAdminUrl}/admin/clients`, {\n headers: {\n Authorization: `Bearer ${this._hydraApiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to list OAuth2 clients: ${response.statusText}`);\n }\n\n const clients = (await response.json()) as unknown[];\n return clients.map((client: unknown) => OAuthClientInformationFullSchema.parse(client));\n }\n\n if (this._providerType === 'network') {\n if (!this._networkProjectUrl) {\n throw new Error('Network project URL is required for network provider type');\n }\n const response = await fetch(`${this._networkProjectUrl}/admin/clients`, {\n headers: {\n Authorization: `Bearer ${this._networkProjectApiKey}`,\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to list OAuth2 clients: ${response.statusText}`);\n }\n\n const clients = (await response.json()) as unknown[];\n return clients.map((client: unknown) => OAuthClientInformationFullSchema.parse(client));\n }\n\n throw new Error('Invalid provider type');\n }\n\n async listOAuth2Clients(): Promise<Record<string, OAuthClientInformationFull>> {\n try {\n const clients = await this.fetchClients();\n return clients.reduce(\n (acc: Record<string, OAuthClientInformationFull>, client: OAuthClientInformationFull) => {\n acc[client.client_id] = client;\n return acc;\n },\n {}\n );\n } catch (error) {\n console.error('Error listing OAuth2 clients:', error);\n throw error;\n }\n }\n\n async getClient(clientId: string): Promise<OAuthClientInformationFull | undefined> {\n try {\n const clients = await this.listOAuth2Clients();\n return clients[clientId];\n } catch (error) {\n console.error('Error getting client:', error);\n throw error;\n }\n }\n\n async authorize(\n client: OAuthClientInformationFull,\n params: AuthorizationParams,\n res: Response\n ): Promise<void> {\n let state = '';\n if (!params.state) {\n state = crypto.randomBytes(32).toString('hex');\n params.state = state;\n }\n\n // Start with required OAuth parameters\n const targetUrl = new URL(this._endpoints.authorizationUrl);\n const searchParams = new URLSearchParams({\n client_id: client.client_id,\n response_type: 'code',\n redirect_uri: params.redirectUri,\n code_challenge: params.codeChallenge,\n code_challenge_method: 'S256',\n });\n\n // Add optional standard OAuth parameters\n if (params.state) searchParams.set('state', params.state);\n if (params.scopes?.length) searchParams.set('scope', params.scopes.join(' '));\n\n targetUrl.search = searchParams.toString();\n // This below line is just a hack to make the function async as lint will complain\n await new Promise(resolve => setTimeout(resolve, 100));\n res.redirect(targetUrl.toString());\n }\n\n async challengeForAuthorizationCode(\n _client: OAuthClientInformationFull,\n _authorizationCode: string\n ): Promise<string> {\n // In a proxy setup, we don't store the code challenge ourselves\n // Instead, we proxy the token request and let the upstream server validate it\n // This below line is just a hack to make the function async as lint will complain\n await new Promise(resolve => setTimeout(resolve, 100));\n return '';\n }\n\n async exchangeAuthorizationCode(\n client: OAuthClientInformationFull,\n authorizationCode: string,\n codeVerifier?: string\n ): Promise<OAuthTokens> {\n const params = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: client.client_id,\n code: authorizationCode,\n redirect_uri: client.redirect_uris[0],\n });\n\n if (client.client_secret) {\n params.append('client_secret', client.client_secret);\n }\n\n if (codeVerifier) {\n params.append('code_verifier', codeVerifier);\n }\n\n const response = await fetch(this._endpoints.tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: params.toString(),\n });\n\n if (!response.ok) {\n throw new ServerError(`Token exchange failed: ${response.status}`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n return OAuthTokensSchema.parse(data);\n }\n\n async exchangeRefreshToken(\n client: OAuthClientInformationFull,\n refreshToken: string,\n scopes?: string[]\n ): Promise<OAuthTokens> {\n const params = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: client.client_id,\n refresh_token: refreshToken,\n });\n\n if (client.client_secret) {\n params.set('client_secret', client.client_secret);\n }\n\n if (scopes?.length) {\n params.set('scope', scopes.join(' '));\n }\n\n const response = await fetch(this._endpoints.tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: params.toString(),\n });\n\n if (!response.ok) {\n throw new ServerError(`Token refresh failed: ${response.status}`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n return OAuthTokensSchema.parse(data);\n }\n\n private async introspectToken(token: string): Promise<{\n active: boolean;\n client_id: string;\n scope?: string;\n exp: number;\n }> {\n if (this._providerType === 'hydra') {\n if (!this._hydraAdminUrl) {\n throw new Error('Hydra admin URL is required for hydra provider type');\n }\n const response = await fetch(`${this._hydraAdminUrl}/admin/oauth2/introspect`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Authorization: `Basic ${Buffer.from(token).toString('base64')}`,\n },\n body: new URLSearchParams({\n token,\n token_type_hint: 'access_token',\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Token introspection failed: ${response.statusText}`);\n }\n\n return response.json() as Promise<{\n active: boolean;\n client_id: string;\n scope?: string;\n exp: number;\n }>;\n }\n\n if (this._providerType === 'network') {\n if (!this._networkProjectUrl) {\n throw new Error('Network project URL is required for network provider type');\n }\n const response = await fetch(`${this._networkProjectUrl}/admin/oauth2/introspect`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Authorization: `Bearer ${this._networkProjectApiKey}`,\n },\n body: new URLSearchParams({\n token,\n token_type_hint: 'access_token',\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Token introspection failed: ${response.statusText}`);\n }\n\n return response.json() as Promise<{\n active: boolean;\n client_id: string;\n scope?: string;\n exp: number;\n }>;\n }\n\n throw new Error('Invalid provider type');\n }\n\n async verifyAccessToken(token: string): Promise<AuthInfo> {\n try {\n const introspection = await this.introspectToken(token);\n\n if (!introspection.active) {\n throw new Error('Token is not active');\n }\n\n const clients = await this.listOAuth2Clients();\n const client = clients[introspection.client_id];\n\n if (!client) {\n throw new Error('Token client ID mismatch');\n }\n\n return {\n token,\n clientId: introspection.client_id,\n scopes: introspection.scope?.split(' ') || [],\n expiresAt: introspection.exp,\n };\n } catch (error) {\n console.error('Token verification failed:', error);\n throw error;\n }\n }\n}\n"]}