@mcp-z/oauth-google 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +8 -0
  2. package/dist/cjs/index.d.cts +2 -1
  3. package/dist/cjs/index.d.ts +2 -1
  4. package/dist/cjs/index.js +4 -0
  5. package/dist/cjs/index.js.map +1 -1
  6. package/dist/cjs/lib/dcr-router.js.map +1 -1
  7. package/dist/cjs/lib/dcr-utils.js.map +1 -1
  8. package/dist/cjs/lib/dcr-verify.js.map +1 -1
  9. package/dist/cjs/lib/fetch-with-timeout.js.map +1 -1
  10. package/dist/cjs/lib/loopback-router.d.cts +8 -0
  11. package/dist/cjs/lib/loopback-router.d.ts +8 -0
  12. package/dist/cjs/lib/loopback-router.js +219 -0
  13. package/dist/cjs/lib/loopback-router.js.map +1 -0
  14. package/dist/cjs/lib/token-verifier.js.map +1 -1
  15. package/dist/cjs/providers/dcr.js.map +1 -1
  16. package/dist/cjs/providers/loopback-oauth.d.cts +15 -17
  17. package/dist/cjs/providers/loopback-oauth.d.ts +15 -17
  18. package/dist/cjs/providers/loopback-oauth.js +223 -156
  19. package/dist/cjs/providers/loopback-oauth.js.map +1 -1
  20. package/dist/cjs/providers/service-account.js.map +1 -1
  21. package/dist/cjs/schemas/index.js.map +1 -1
  22. package/dist/cjs/setup/config.d.cts +6 -1
  23. package/dist/cjs/setup/config.d.ts +6 -1
  24. package/dist/cjs/setup/config.js +3 -0
  25. package/dist/cjs/setup/config.js.map +1 -1
  26. package/dist/cjs/types.js.map +1 -1
  27. package/dist/esm/index.d.ts +2 -1
  28. package/dist/esm/index.js +1 -0
  29. package/dist/esm/index.js.map +1 -1
  30. package/dist/esm/lib/dcr-router.js.map +1 -1
  31. package/dist/esm/lib/dcr-utils.js.map +1 -1
  32. package/dist/esm/lib/dcr-verify.js.map +1 -1
  33. package/dist/esm/lib/fetch-with-timeout.js.map +1 -1
  34. package/dist/esm/lib/loopback-router.d.ts +8 -0
  35. package/dist/esm/lib/loopback-router.js +32 -0
  36. package/dist/esm/lib/loopback-router.js.map +1 -0
  37. package/dist/esm/lib/token-verifier.js.map +1 -1
  38. package/dist/esm/providers/dcr.js.map +1 -1
  39. package/dist/esm/providers/loopback-oauth.d.ts +15 -17
  40. package/dist/esm/providers/loopback-oauth.js +152 -114
  41. package/dist/esm/providers/loopback-oauth.js.map +1 -1
  42. package/dist/esm/providers/service-account.js.map +1 -1
  43. package/dist/esm/schemas/index.js.map +1 -1
  44. package/dist/esm/setup/config.d.ts +6 -1
  45. package/dist/esm/setup/config.js +4 -0
  46. package/dist/esm/setup/config.js.map +1 -1
  47. package/dist/esm/types.js.map +1 -1
  48. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-google/src/lib/dcr-router.ts"],"sourcesContent":["/**\n * DCR Router - OAuth 2.0 Authorization Server\n *\n * Implements OAuth 2.0 Dynamic Client Registration Protocol (RFC 7591)\n * and OAuth 2.0 Authorization Server endpoints (RFC 6749, RFC 8414, RFC 9728).\n *\n * Endpoints:\n * - GET /.well-known/oauth-authorization-server (RFC 8414 metadata)\n * - GET /.well-known/oauth-protected-resource (RFC 9728 metadata - root)\n * - GET /.well-known/oauth-protected-resource/mcp (RFC 9728 metadata - sub-path)\n * - POST /oauth/register (RFC 7591 client registration)\n * - GET /oauth/authorize (RFC 6749 authorization endpoint)\n * - POST /oauth/token (RFC 6749 token endpoint)\n * - POST /oauth/revoke (RFC 7009 token revocation)\n * - GET /oauth/verify (token verification for Resource Server)\n */\n\nimport type { ProviderTokens, RFC8414Metadata, RFC9728Metadata } from '@mcp-z/oauth';\nimport { createHash, randomUUID } from 'crypto';\nimport type { Request, Response } from 'express';\nimport express from 'express';\nimport type { Keyv } from 'keyv';\nimport { DcrOAuthProvider } from '../providers/dcr.ts';\nimport type { AccessToken, AuthorizationCode, OAuthClientConfig } from '../types.ts';\nimport * as dcrUtils from './dcr-utils.ts';\n\n/**\n * Configuration for DCR Router (self-hosted mode only)\n */\nexport interface DcrRouterConfig {\n /** Single Keyv store for all DCR data */\n store: Keyv;\n\n /** Authorization Server issuer URL */\n issuerUrl: string;\n\n /** Base URL for OAuth endpoints */\n baseUrl: string;\n\n /** Supported OAuth scopes */\n scopesSupported: string[];\n\n /** OAuth client configuration for upstream provider */\n clientConfig: OAuthClientConfig;\n}\n\n/**\n * Create DCR Router with OAuth 2.0 endpoints (self-hosted mode)\n *\n * For external mode (Auth0/Stitch), don't call this function - no router needed.\n * The server code should check DcrConfig.mode and only call this for 'self-hosted'.\n *\n * @param config - Router configuration\n * @returns Express router with OAuth endpoints\n */\nexport function createDcrRouter(config: DcrRouterConfig): express.Router {\n const router = express.Router();\n const { store, issuerUrl, baseUrl, scopesSupported, clientConfig } = config;\n\n router.use(express.json());\n router.use(express.urlencoded({ extended: true }));\n\n /**\n * OAuth Authorization Server Metadata (RFC 8414)\n * GET /.well-known/oauth-authorization-server\n */\n router.get('/.well-known/oauth-authorization-server', (_req: Request, res: Response) => {\n const metadata: RFC8414Metadata = {\n issuer: issuerUrl,\n authorization_endpoint: `${baseUrl}/oauth/authorize`,\n token_endpoint: `${baseUrl}/oauth/token`,\n registration_endpoint: `${baseUrl}/oauth/register`,\n revocation_endpoint: `${baseUrl}/oauth/revoke`,\n scopes_supported: scopesSupported,\n response_types_supported: ['code'],\n grant_types_supported: ['authorization_code', 'refresh_token'],\n token_endpoint_auth_methods_supported: ['client_secret_basic', 'client_secret_post'],\n code_challenge_methods_supported: ['S256', 'plain'],\n service_documentation: `${baseUrl}/docs`,\n };\n res.json(metadata);\n });\n\n /**\n * OAuth Protected Resource Metadata (RFC 9728 - Root)\n * GET /.well-known/oauth-protected-resource\n */\n router.get('/.well-known/oauth-protected-resource', (_req: Request, res: Response) => {\n const metadata: RFC9728Metadata = {\n resource: baseUrl,\n authorization_servers: [baseUrl],\n scopes_supported: scopesSupported,\n bearer_methods_supported: ['header'],\n };\n res.json(metadata);\n });\n\n /**\n * OAuth Protected Resource Metadata (RFC 9728 - Sub-path /mcp)\n * GET /.well-known/oauth-protected-resource/mcp\n */\n router.get('/.well-known/oauth-protected-resource/mcp', (_req: Request, res: Response) => {\n const metadata: RFC9728Metadata = {\n resource: `${baseUrl}/mcp`,\n authorization_servers: [baseUrl],\n scopes_supported: scopesSupported,\n bearer_methods_supported: ['header'],\n };\n res.json(metadata);\n });\n\n /**\n * Dynamic Client Registration (RFC 7591)\n * POST /oauth/register\n */\n router.post('/oauth/register', async (req: Request, res: Response) => {\n try {\n const registrationRequest = req.body;\n const client = await dcrUtils.registerClient(store, registrationRequest);\n res.status(201).json(client);\n } catch (error) {\n res.status(400).json({\n error: 'invalid_client_metadata',\n error_description: error instanceof Error ? error.message : 'Invalid registration request',\n });\n }\n });\n\n /**\n * OAuth Authorization Endpoint (RFC 6749 Section 3.1)\n * GET /oauth/authorize\n *\n * Initiates Google OAuth flow, then generates DCR authorization code\n */\n router.get('/oauth/authorize', async (req: Request, res: Response) => {\n const { response_type, client_id, redirect_uri, scope = '', state = '', code_challenge, code_challenge_method } = req.query;\n\n if (response_type !== 'code') {\n return res.status(400).json({\n error: 'unsupported_response_type',\n error_description: 'Only response_type=code is supported',\n });\n }\n\n if (!client_id || typeof client_id !== 'string') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'client_id is required',\n });\n }\n\n if (!redirect_uri || typeof redirect_uri !== 'string') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'redirect_uri is required',\n });\n }\n\n const client = await dcrUtils.getClient(store, client_id);\n if (!client) {\n return res.status(400).json({\n error: 'invalid_client',\n error_description: 'Unknown client_id',\n });\n }\n\n const isValidRedirect = await dcrUtils.validateRedirectUri(store, client_id, redirect_uri);\n if (!isValidRedirect) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Invalid redirect_uri',\n });\n }\n\n const googleState = randomUUID();\n const dcrRequestState = {\n client_id,\n redirect_uri,\n scope: typeof scope === 'string' ? scope : '',\n state: typeof state === 'string' ? state : undefined,\n code_challenge: typeof code_challenge === 'string' ? code_challenge : undefined,\n code_challenge_method: typeof code_challenge_method === 'string' ? code_challenge_method : undefined,\n created_at: Date.now(),\n expires_at: Date.now() + 600000, // 10 minutes\n };\n\n await store.set(`dcr:google-state:${googleState}`, dcrRequestState, 600000);\n\n const googleAuthUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');\n googleAuthUrl.searchParams.set('client_id', clientConfig.clientId);\n googleAuthUrl.searchParams.set('redirect_uri', `${baseUrl}/oauth/callback`);\n googleAuthUrl.searchParams.set('response_type', 'code');\n googleAuthUrl.searchParams.set('scope', typeof scope === 'string' ? scope : '');\n googleAuthUrl.searchParams.set('state', googleState);\n googleAuthUrl.searchParams.set('access_type', 'offline');\n googleAuthUrl.searchParams.set('prompt', 'consent');\n\n return res.redirect(googleAuthUrl.toString());\n });\n\n /**\n * OAuth Callback Handler\n * GET /oauth/callback\n *\n * Handles OAuth callback from Google, exchanges authorization code for tokens,\n * and redirects back to client with DCR authorization code.\n */\n router.get('/oauth/callback', async (req: Request, res: Response) => {\n const { code: googleCode, state: googleState, error } = req.query;\n\n if (error) {\n return res.status(400).json({\n error: typeof error === 'string' ? error : 'access_denied',\n error_description: 'Google OAuth authorization failed',\n });\n }\n\n if (!googleCode || typeof googleCode !== 'string') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Missing authorization code from Google',\n });\n }\n\n if (!googleState || typeof googleState !== 'string') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Missing state parameter',\n });\n }\n\n const dcrRequestState = await store.get(`dcr:google-state:${googleState}`);\n if (!dcrRequestState) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Invalid or expired state parameter',\n });\n }\n\n await store.delete(`dcr:google-state:${googleState}`);\n\n if (Date.now() > dcrRequestState.expires_at) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'DCR request state expired',\n });\n }\n\n const tokenUrl = 'https://oauth2.googleapis.com/token';\n // Build token params - only include client_secret for confidential clients\n // Public clients (Desktop apps) should NOT send client_secret at all\n const tokenParamsObj: Record<string, string> = {\n code: googleCode,\n client_id: clientConfig.clientId,\n redirect_uri: `${baseUrl}/oauth/callback`,\n grant_type: 'authorization_code',\n };\n if (clientConfig.clientSecret) {\n tokenParamsObj.client_secret = clientConfig.clientSecret;\n }\n const tokenParams = new URLSearchParams(tokenParamsObj);\n\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: tokenParams.toString(),\n });\n\n if (!tokenResponse.ok) {\n const errorText = await tokenResponse.text();\n return res.status(400).json({\n error: 'server_error',\n error_description: `Failed to exchange Google authorization code: ${errorText}`,\n });\n }\n\n const tokenData = (await tokenResponse.json()) as {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n scope: string;\n };\n\n // Create provider tokens from Google response\n const providerTokens: ProviderTokens = {\n accessToken: tokenData.access_token,\n ...(tokenData.refresh_token && { refreshToken: tokenData.refresh_token }),\n expiresAt: Date.now() + tokenData.expires_in * 1000,\n scope: tokenData.scope,\n };\n\n const dcrCode = randomUUID();\n const authCode: AuthorizationCode = {\n code: dcrCode,\n client_id: dcrRequestState.client_id,\n redirect_uri: dcrRequestState.redirect_uri,\n scope: dcrRequestState.scope,\n ...(dcrRequestState.code_challenge && { code_challenge: dcrRequestState.code_challenge }),\n ...(dcrRequestState.code_challenge_method && { code_challenge_method: dcrRequestState.code_challenge_method }),\n providerTokens,\n created_at: Date.now(),\n expires_at: Date.now() + 600000, // 10 minutes\n };\n\n await dcrUtils.setAuthCode(store, dcrCode, authCode);\n\n const clientRedirectUrl = new URL(dcrRequestState.redirect_uri);\n clientRedirectUrl.searchParams.set('code', dcrCode);\n if (dcrRequestState.state) {\n clientRedirectUrl.searchParams.set('state', dcrRequestState.state);\n }\n\n return res.redirect(clientRedirectUrl.toString());\n });\n\n /**\n * OAuth Token Endpoint (RFC 6749 Section 3.2)\n * POST /oauth/token\n */\n router.post('/oauth/token', async (req: Request, res: Response) => {\n let client_id = req.body.client_id;\n let client_secret = req.body.client_secret;\n\n const authHeader = req.headers.authorization;\n if (authHeader && authHeader.startsWith('Basic ')) {\n const base64Credentials = authHeader.substring(6);\n const credentials = Buffer.from(base64Credentials, 'base64').toString('utf-8');\n const [id, secret] = credentials.split(':');\n client_id = id;\n client_secret = secret;\n }\n\n const { grant_type, code, redirect_uri, refresh_token, code_verifier } = req.body;\n\n if (!grant_type) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'grant_type is required',\n });\n }\n\n if (grant_type === 'authorization_code') {\n if (!code || !client_id || !redirect_uri) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'code, client_id, and redirect_uri are required',\n });\n }\n\n // Validate client credentials\n const isValidClient = await dcrUtils.validateClient(store, client_id, client_secret ?? '');\n if (!isValidClient) {\n return res.status(401).json({\n error: 'invalid_client',\n error_description: 'Invalid client credentials',\n });\n }\n\n const authCode = await dcrUtils.getAuthCode(store, code);\n if (!authCode) {\n return res.status(400).json({\n error: 'invalid_grant',\n error_description: 'Invalid or expired authorization code',\n });\n }\n\n if (authCode.client_id !== client_id || authCode.redirect_uri !== redirect_uri) {\n return res.status(400).json({\n error: 'invalid_grant',\n error_description: 'Authorization code mismatch',\n });\n }\n\n if (Date.now() > authCode.expires_at) {\n await dcrUtils.deleteAuthCode(store, code);\n return res.status(400).json({\n error: 'invalid_grant',\n error_description: 'Authorization code expired',\n });\n }\n\n if (authCode.code_challenge) {\n if (!code_verifier) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'code_verifier is required for PKCE',\n });\n }\n\n const method = authCode.code_challenge_method ?? 'plain';\n const computedChallenge = method === 'S256' ? createHash('sha256').update(code_verifier).digest('base64url') : code_verifier;\n\n if (computedChallenge !== authCode.code_challenge) {\n return res.status(400).json({\n error: 'invalid_grant',\n error_description: 'Invalid code_verifier',\n });\n }\n }\n\n await dcrUtils.deleteAuthCode(store, code);\n\n const accessToken = randomUUID();\n const refreshTokenValue = randomUUID();\n\n const tokenData: AccessToken = {\n access_token: accessToken,\n token_type: 'Bearer',\n expires_in: 3600,\n refresh_token: refreshTokenValue,\n scope: authCode.scope,\n client_id,\n providerTokens: authCode.providerTokens,\n created_at: Date.now(),\n };\n\n await dcrUtils.setAccessToken(store, accessToken, tokenData);\n await dcrUtils.setRefreshToken(store, refreshTokenValue, tokenData);\n await dcrUtils.setProviderTokens(store, accessToken, authCode.providerTokens);\n\n return res.json({\n access_token: tokenData.access_token,\n token_type: tokenData.token_type,\n expires_in: tokenData.expires_in,\n refresh_token: tokenData.refresh_token,\n scope: tokenData.scope,\n });\n }\n if (grant_type === 'refresh_token') {\n if (!refresh_token || !client_id) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'refresh_token and client_id are required',\n });\n }\n\n const isValidClient = await dcrUtils.validateClient(store, client_id, client_secret ?? '');\n if (!isValidClient) {\n return res.status(401).json({\n error: 'invalid_client',\n error_description: 'Invalid client credentials',\n });\n }\n\n const tokenData = await dcrUtils.getRefreshToken(store, refresh_token);\n if (!tokenData || tokenData.client_id !== client_id) {\n return res.status(400).json({\n error: 'invalid_grant',\n error_description: 'Invalid refresh token',\n });\n }\n\n let refreshedProviderTokens = tokenData.providerTokens;\n if (tokenData.providerTokens.refreshToken) {\n try {\n // Create DcrOAuthProvider instance to refresh Google tokens\n const provider = new DcrOAuthProvider({\n ...clientConfig,\n scope: tokenData.scope,\n verifyEndpoint: `${baseUrl}/oauth/verify`,\n logger: {\n info: console.log,\n error: console.error,\n warn: console.warn,\n debug: () => {},\n },\n });\n\n // Refresh the Google access token\n refreshedProviderTokens = await provider.refreshAccessToken(tokenData.providerTokens.refreshToken);\n } catch (error) {\n // If refresh fails, continue with existing tokens (they may still be valid)\n console.warn('Provider token refresh failed, using existing tokens:', error instanceof Error ? error.message : String(error));\n }\n }\n\n const newAccessToken = randomUUID();\n const newTokenData: AccessToken = {\n ...tokenData,\n access_token: newAccessToken,\n created_at: Date.now(),\n };\n\n await dcrUtils.setAccessToken(store, newAccessToken, newTokenData);\n await dcrUtils.setProviderTokens(store, newAccessToken, refreshedProviderTokens);\n\n return res.json({\n access_token: newTokenData.access_token,\n token_type: newTokenData.token_type,\n expires_in: newTokenData.expires_in,\n scope: newTokenData.scope,\n });\n }\n return res.status(400).json({\n error: 'unsupported_grant_type',\n error_description: 'Only authorization_code and refresh_token grants are supported',\n });\n });\n\n /**\n * OAuth Token Revocation (RFC 7009)\n * POST /oauth/revoke\n */\n router.post('/oauth/revoke', async (req: Request, res: Response) => {\n const { token, token_type_hint, client_id, client_secret } = req.body;\n\n if (!token) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'token is required',\n });\n }\n\n if (client_id && client_secret) {\n const isValidClient = await dcrUtils.validateClient(store, client_id, client_secret);\n if (!isValidClient) {\n return res.status(401).json({\n error: 'invalid_client',\n error_description: 'Invalid client credentials',\n });\n }\n }\n\n if (token_type_hint === 'refresh_token') {\n await dcrUtils.deleteRefreshToken(store, token);\n } else if (token_type_hint === 'access_token') {\n await dcrUtils.deleteAccessToken(store, token);\n await dcrUtils.deleteProviderTokens(store, token);\n } else {\n // No hint - try both\n await dcrUtils.deleteRefreshToken(store, token);\n await dcrUtils.deleteAccessToken(store, token);\n await dcrUtils.deleteProviderTokens(store, token);\n }\n\n return res.status(200).send();\n });\n\n /**\n * Token Verification Endpoint\n * GET /oauth/verify\n *\n * Validates bearer tokens for Resource Server.\n * Returns AuthInfo with provider tokens for stateless DCR pattern.\n */\n router.get('/oauth/verify', async (req: Request, res: Response) => {\n const authHeader = req.headers.authorization;\n\n if (!authHeader || !authHeader.startsWith('Bearer ')) {\n return res.status(401).json({\n error: 'invalid_request',\n error_description: 'Missing or invalid Authorization header',\n });\n }\n\n const token = authHeader.substring(7);\n const tokenData = await dcrUtils.getAccessToken(store, token);\n\n if (!tokenData) {\n return res.status(401).json({\n error: 'invalid_token',\n error_description: 'Unknown or expired access token',\n });\n }\n\n const now = Date.now();\n const expiresAt = tokenData.created_at + tokenData.expires_in * 1000;\n\n if (now > expiresAt) {\n await dcrUtils.deleteAccessToken(store, token);\n await dcrUtils.deleteProviderTokens(store, token);\n return res.status(401).json({\n error: 'invalid_token',\n error_description: 'Access token has expired',\n });\n }\n\n const authInfo = {\n token,\n clientId: tokenData.client_id,\n scopes: tokenData.scope ? tokenData.scope.split(' ') : [],\n expiresAt,\n providerTokens: tokenData.providerTokens,\n };\n\n return res.json(authInfo);\n });\n\n /**\n * Debug endpoint to list registered clients (development only)\n */\n router.get('/debug/clients', async (_req: Request, res: Response) => {\n const clients = await dcrUtils.listClients(store);\n return res.json(clients);\n });\n\n return router;\n}\n"],"names":["createHash","randomUUID","express","DcrOAuthProvider","dcrUtils","createDcrRouter","config","router","Router","store","issuerUrl","baseUrl","scopesSupported","clientConfig","use","json","urlencoded","extended","get","_req","res","metadata","issuer","authorization_endpoint","token_endpoint","registration_endpoint","revocation_endpoint","scopes_supported","response_types_supported","grant_types_supported","token_endpoint_auth_methods_supported","code_challenge_methods_supported","service_documentation","resource","authorization_servers","bearer_methods_supported","post","req","registrationRequest","body","client","registerClient","status","error","error_description","Error","message","response_type","client_id","redirect_uri","scope","state","code_challenge","code_challenge_method","query","getClient","isValidRedirect","validateRedirectUri","googleState","dcrRequestState","undefined","created_at","Date","now","expires_at","set","googleAuthUrl","URL","searchParams","clientId","redirect","toString","code","googleCode","delete","tokenUrl","tokenParamsObj","grant_type","clientSecret","client_secret","tokenParams","URLSearchParams","tokenResponse","fetch","method","headers","ok","errorText","text","tokenData","providerTokens","accessToken","access_token","refresh_token","refreshToken","expiresAt","expires_in","dcrCode","authCode","setAuthCode","clientRedirectUrl","authHeader","authorization","startsWith","base64Credentials","substring","credentials","Buffer","from","id","secret","split","code_verifier","isValidClient","validateClient","getAuthCode","deleteAuthCode","computedChallenge","update","digest","refreshTokenValue","token_type","setAccessToken","setRefreshToken","setProviderTokens","getRefreshToken","refreshedProviderTokens","provider","verifyEndpoint","logger","info","console","log","warn","debug","refreshAccessToken","String","newAccessToken","newTokenData","token","token_type_hint","deleteRefreshToken","deleteAccessToken","deleteProviderTokens","send","getAccessToken","authInfo","scopes","clients","listClients"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-google/src/lib/dcr-router.ts"],"sourcesContent":["/**\n * DCR Router - OAuth 2.0 Authorization Server\n *\n * Implements OAuth 2.0 Dynamic Client Registration Protocol (RFC 7591)\n * and OAuth 2.0 Authorization Server endpoints (RFC 6749, RFC 8414, RFC 9728).\n *\n * Endpoints:\n * - GET /.well-known/oauth-authorization-server (RFC 8414 metadata)\n * - GET /.well-known/oauth-protected-resource (RFC 9728 metadata - root)\n * - GET /.well-known/oauth-protected-resource/mcp (RFC 9728 metadata - sub-path)\n * - POST /oauth/register (RFC 7591 client registration)\n * - GET /oauth/authorize (RFC 6749 authorization endpoint)\n * - POST /oauth/token (RFC 6749 token endpoint)\n * - POST /oauth/revoke (RFC 7009 token revocation)\n * - GET /oauth/verify (token verification for Resource Server)\n */\n\nimport type { ProviderTokens, RFC8414Metadata, RFC9728Metadata } from '@mcp-z/oauth';\nimport { createHash, randomUUID } from 'crypto';\nimport type { Request, Response } from 'express';\nimport express from 'express';\nimport type { Keyv } from 'keyv';\nimport { DcrOAuthProvider } from '../providers/dcr.ts';\nimport type { AccessToken, AuthorizationCode, OAuthClientConfig } from '../types.ts';\nimport * as dcrUtils from './dcr-utils.ts';\n\n/**\n * Configuration for DCR Router (self-hosted mode only)\n */\nexport interface DcrRouterConfig {\n /** Single Keyv store for all DCR data */\n store: Keyv;\n\n /** Authorization Server issuer URL */\n issuerUrl: string;\n\n /** Base URL for OAuth endpoints */\n baseUrl: string;\n\n /** Supported OAuth scopes */\n scopesSupported: string[];\n\n /** OAuth client configuration for upstream provider */\n clientConfig: OAuthClientConfig;\n}\n\n/**\n * Create DCR Router with OAuth 2.0 endpoints (self-hosted mode)\n *\n * For external mode (Auth0/Stitch), don't call this function - no router needed.\n * The server code should check DcrConfig.mode and only call this for 'self-hosted'.\n *\n * @param config - Router configuration\n * @returns Express router with OAuth endpoints\n */\nexport function createDcrRouter(config: DcrRouterConfig): express.Router {\n const router = express.Router();\n const { store, issuerUrl, baseUrl, scopesSupported, clientConfig } = config;\n\n router.use(express.json());\n router.use(express.urlencoded({ extended: true }));\n\n /**\n * OAuth Authorization Server Metadata (RFC 8414)\n * GET /.well-known/oauth-authorization-server\n */\n router.get('/.well-known/oauth-authorization-server', (_req: Request, res: Response) => {\n const metadata: RFC8414Metadata = {\n issuer: issuerUrl,\n authorization_endpoint: `${baseUrl}/oauth/authorize`,\n token_endpoint: `${baseUrl}/oauth/token`,\n registration_endpoint: `${baseUrl}/oauth/register`,\n revocation_endpoint: `${baseUrl}/oauth/revoke`,\n scopes_supported: scopesSupported,\n response_types_supported: ['code'],\n grant_types_supported: ['authorization_code', 'refresh_token'],\n token_endpoint_auth_methods_supported: ['client_secret_basic', 'client_secret_post'],\n code_challenge_methods_supported: ['S256', 'plain'],\n service_documentation: `${baseUrl}/docs`,\n };\n res.json(metadata);\n });\n\n /**\n * OAuth Protected Resource Metadata (RFC 9728 - Root)\n * GET /.well-known/oauth-protected-resource\n */\n router.get('/.well-known/oauth-protected-resource', (_req: Request, res: Response) => {\n const metadata: RFC9728Metadata = {\n resource: baseUrl,\n authorization_servers: [baseUrl],\n scopes_supported: scopesSupported,\n bearer_methods_supported: ['header'],\n };\n res.json(metadata);\n });\n\n /**\n * OAuth Protected Resource Metadata (RFC 9728 - Sub-path /mcp)\n * GET /.well-known/oauth-protected-resource/mcp\n */\n router.get('/.well-known/oauth-protected-resource/mcp', (_req: Request, res: Response) => {\n const metadata: RFC9728Metadata = {\n resource: `${baseUrl}/mcp`,\n authorization_servers: [baseUrl],\n scopes_supported: scopesSupported,\n bearer_methods_supported: ['header'],\n };\n res.json(metadata);\n });\n\n /**\n * Dynamic Client Registration (RFC 7591)\n * POST /oauth/register\n */\n router.post('/oauth/register', async (req: Request, res: Response) => {\n try {\n const registrationRequest = req.body;\n const client = await dcrUtils.registerClient(store, registrationRequest);\n res.status(201).json(client);\n } catch (error) {\n res.status(400).json({\n error: 'invalid_client_metadata',\n error_description: error instanceof Error ? error.message : 'Invalid registration request',\n });\n }\n });\n\n /**\n * OAuth Authorization Endpoint (RFC 6749 Section 3.1)\n * GET /oauth/authorize\n *\n * Initiates Google OAuth flow, then generates DCR authorization code\n */\n router.get('/oauth/authorize', async (req: Request, res: Response) => {\n const { response_type, client_id, redirect_uri, scope = '', state = '', code_challenge, code_challenge_method } = req.query;\n\n if (response_type !== 'code') {\n return res.status(400).json({\n error: 'unsupported_response_type',\n error_description: 'Only response_type=code is supported',\n });\n }\n\n if (!client_id || typeof client_id !== 'string') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'client_id is required',\n });\n }\n\n if (!redirect_uri || typeof redirect_uri !== 'string') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'redirect_uri is required',\n });\n }\n\n const client = await dcrUtils.getClient(store, client_id);\n if (!client) {\n return res.status(400).json({\n error: 'invalid_client',\n error_description: 'Unknown client_id',\n });\n }\n\n const isValidRedirect = await dcrUtils.validateRedirectUri(store, client_id, redirect_uri);\n if (!isValidRedirect) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Invalid redirect_uri',\n });\n }\n\n const googleState = randomUUID();\n const dcrRequestState = {\n client_id,\n redirect_uri,\n scope: typeof scope === 'string' ? scope : '',\n state: typeof state === 'string' ? state : undefined,\n code_challenge: typeof code_challenge === 'string' ? code_challenge : undefined,\n code_challenge_method: typeof code_challenge_method === 'string' ? code_challenge_method : undefined,\n created_at: Date.now(),\n expires_at: Date.now() + 600000, // 10 minutes\n };\n\n await store.set(`dcr:google-state:${googleState}`, dcrRequestState, 600000);\n\n const googleAuthUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');\n googleAuthUrl.searchParams.set('client_id', clientConfig.clientId);\n googleAuthUrl.searchParams.set('redirect_uri', `${baseUrl}/oauth/callback`);\n googleAuthUrl.searchParams.set('response_type', 'code');\n googleAuthUrl.searchParams.set('scope', typeof scope === 'string' ? scope : '');\n googleAuthUrl.searchParams.set('state', googleState);\n googleAuthUrl.searchParams.set('access_type', 'offline');\n googleAuthUrl.searchParams.set('prompt', 'consent');\n\n return res.redirect(googleAuthUrl.toString());\n });\n\n /**\n * OAuth Callback Handler\n * GET /oauth/callback\n *\n * Handles OAuth callback from Google, exchanges authorization code for tokens,\n * and redirects back to client with DCR authorization code.\n */\n router.get('/oauth/callback', async (req: Request, res: Response) => {\n const { code: googleCode, state: googleState, error } = req.query;\n\n if (error) {\n return res.status(400).json({\n error: typeof error === 'string' ? error : 'access_denied',\n error_description: 'Google OAuth authorization failed',\n });\n }\n\n if (!googleCode || typeof googleCode !== 'string') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Missing authorization code from Google',\n });\n }\n\n if (!googleState || typeof googleState !== 'string') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Missing state parameter',\n });\n }\n\n const dcrRequestState = await store.get(`dcr:google-state:${googleState}`);\n if (!dcrRequestState) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Invalid or expired state parameter',\n });\n }\n\n await store.delete(`dcr:google-state:${googleState}`);\n\n if (Date.now() > dcrRequestState.expires_at) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'DCR request state expired',\n });\n }\n\n const tokenUrl = 'https://oauth2.googleapis.com/token';\n // Build token params - only include client_secret for confidential clients\n // Public clients (Desktop apps) should NOT send client_secret at all\n const tokenParamsObj: Record<string, string> = {\n code: googleCode,\n client_id: clientConfig.clientId,\n redirect_uri: `${baseUrl}/oauth/callback`,\n grant_type: 'authorization_code',\n };\n if (clientConfig.clientSecret) {\n tokenParamsObj.client_secret = clientConfig.clientSecret;\n }\n const tokenParams = new URLSearchParams(tokenParamsObj);\n\n const tokenResponse = await fetch(tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: tokenParams.toString(),\n });\n\n if (!tokenResponse.ok) {\n const errorText = await tokenResponse.text();\n return res.status(400).json({\n error: 'server_error',\n error_description: `Failed to exchange Google authorization code: ${errorText}`,\n });\n }\n\n const tokenData = (await tokenResponse.json()) as {\n access_token: string;\n refresh_token?: string;\n expires_in: number;\n scope: string;\n };\n\n // Create provider tokens from Google response\n const providerTokens: ProviderTokens = {\n accessToken: tokenData.access_token,\n ...(tokenData.refresh_token && { refreshToken: tokenData.refresh_token }),\n expiresAt: Date.now() + tokenData.expires_in * 1000,\n scope: tokenData.scope,\n };\n\n const dcrCode = randomUUID();\n const authCode: AuthorizationCode = {\n code: dcrCode,\n client_id: dcrRequestState.client_id,\n redirect_uri: dcrRequestState.redirect_uri,\n scope: dcrRequestState.scope,\n ...(dcrRequestState.code_challenge && { code_challenge: dcrRequestState.code_challenge }),\n ...(dcrRequestState.code_challenge_method && { code_challenge_method: dcrRequestState.code_challenge_method }),\n providerTokens,\n created_at: Date.now(),\n expires_at: Date.now() + 600000, // 10 minutes\n };\n\n await dcrUtils.setAuthCode(store, dcrCode, authCode);\n\n const clientRedirectUrl = new URL(dcrRequestState.redirect_uri);\n clientRedirectUrl.searchParams.set('code', dcrCode);\n if (dcrRequestState.state) {\n clientRedirectUrl.searchParams.set('state', dcrRequestState.state);\n }\n\n return res.redirect(clientRedirectUrl.toString());\n });\n\n /**\n * OAuth Token Endpoint (RFC 6749 Section 3.2)\n * POST /oauth/token\n */\n router.post('/oauth/token', async (req: Request, res: Response) => {\n let client_id = req.body.client_id;\n let client_secret = req.body.client_secret;\n\n const authHeader = req.headers.authorization;\n if (authHeader && authHeader.startsWith('Basic ')) {\n const base64Credentials = authHeader.substring(6);\n const credentials = Buffer.from(base64Credentials, 'base64').toString('utf-8');\n const [id, secret] = credentials.split(':');\n client_id = id;\n client_secret = secret;\n }\n\n const { grant_type, code, redirect_uri, refresh_token, code_verifier } = req.body;\n\n if (!grant_type) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'grant_type is required',\n });\n }\n\n if (grant_type === 'authorization_code') {\n if (!code || !client_id || !redirect_uri) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'code, client_id, and redirect_uri are required',\n });\n }\n\n // Validate client credentials\n const isValidClient = await dcrUtils.validateClient(store, client_id, client_secret ?? '');\n if (!isValidClient) {\n return res.status(401).json({\n error: 'invalid_client',\n error_description: 'Invalid client credentials',\n });\n }\n\n const authCode = await dcrUtils.getAuthCode(store, code);\n if (!authCode) {\n return res.status(400).json({\n error: 'invalid_grant',\n error_description: 'Invalid or expired authorization code',\n });\n }\n\n if (authCode.client_id !== client_id || authCode.redirect_uri !== redirect_uri) {\n return res.status(400).json({\n error: 'invalid_grant',\n error_description: 'Authorization code mismatch',\n });\n }\n\n if (Date.now() > authCode.expires_at) {\n await dcrUtils.deleteAuthCode(store, code);\n return res.status(400).json({\n error: 'invalid_grant',\n error_description: 'Authorization code expired',\n });\n }\n\n if (authCode.code_challenge) {\n if (!code_verifier) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'code_verifier is required for PKCE',\n });\n }\n\n const method = authCode.code_challenge_method ?? 'plain';\n const computedChallenge = method === 'S256' ? createHash('sha256').update(code_verifier).digest('base64url') : code_verifier;\n\n if (computedChallenge !== authCode.code_challenge) {\n return res.status(400).json({\n error: 'invalid_grant',\n error_description: 'Invalid code_verifier',\n });\n }\n }\n\n await dcrUtils.deleteAuthCode(store, code);\n\n const accessToken = randomUUID();\n const refreshTokenValue = randomUUID();\n\n const tokenData: AccessToken = {\n access_token: accessToken,\n token_type: 'Bearer',\n expires_in: 3600,\n refresh_token: refreshTokenValue,\n scope: authCode.scope,\n client_id,\n providerTokens: authCode.providerTokens,\n created_at: Date.now(),\n };\n\n await dcrUtils.setAccessToken(store, accessToken, tokenData);\n await dcrUtils.setRefreshToken(store, refreshTokenValue, tokenData);\n await dcrUtils.setProviderTokens(store, accessToken, authCode.providerTokens);\n\n return res.json({\n access_token: tokenData.access_token,\n token_type: tokenData.token_type,\n expires_in: tokenData.expires_in,\n refresh_token: tokenData.refresh_token,\n scope: tokenData.scope,\n });\n }\n if (grant_type === 'refresh_token') {\n if (!refresh_token || !client_id) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'refresh_token and client_id are required',\n });\n }\n\n const isValidClient = await dcrUtils.validateClient(store, client_id, client_secret ?? '');\n if (!isValidClient) {\n return res.status(401).json({\n error: 'invalid_client',\n error_description: 'Invalid client credentials',\n });\n }\n\n const tokenData = await dcrUtils.getRefreshToken(store, refresh_token);\n if (!tokenData || tokenData.client_id !== client_id) {\n return res.status(400).json({\n error: 'invalid_grant',\n error_description: 'Invalid refresh token',\n });\n }\n\n let refreshedProviderTokens = tokenData.providerTokens;\n if (tokenData.providerTokens.refreshToken) {\n try {\n // Create DcrOAuthProvider instance to refresh Google tokens\n const provider = new DcrOAuthProvider({\n ...clientConfig,\n scope: tokenData.scope,\n verifyEndpoint: `${baseUrl}/oauth/verify`,\n logger: {\n info: console.log,\n error: console.error,\n warn: console.warn,\n debug: () => {},\n },\n });\n\n // Refresh the Google access token\n refreshedProviderTokens = await provider.refreshAccessToken(tokenData.providerTokens.refreshToken);\n } catch (error) {\n // If refresh fails, continue with existing tokens (they may still be valid)\n console.warn('Provider token refresh failed, using existing tokens:', error instanceof Error ? error.message : String(error));\n }\n }\n\n const newAccessToken = randomUUID();\n const newTokenData: AccessToken = {\n ...tokenData,\n access_token: newAccessToken,\n created_at: Date.now(),\n };\n\n await dcrUtils.setAccessToken(store, newAccessToken, newTokenData);\n await dcrUtils.setProviderTokens(store, newAccessToken, refreshedProviderTokens);\n\n return res.json({\n access_token: newTokenData.access_token,\n token_type: newTokenData.token_type,\n expires_in: newTokenData.expires_in,\n scope: newTokenData.scope,\n });\n }\n return res.status(400).json({\n error: 'unsupported_grant_type',\n error_description: 'Only authorization_code and refresh_token grants are supported',\n });\n });\n\n /**\n * OAuth Token Revocation (RFC 7009)\n * POST /oauth/revoke\n */\n router.post('/oauth/revoke', async (req: Request, res: Response) => {\n const { token, token_type_hint, client_id, client_secret } = req.body;\n\n if (!token) {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'token is required',\n });\n }\n\n if (client_id && client_secret) {\n const isValidClient = await dcrUtils.validateClient(store, client_id, client_secret);\n if (!isValidClient) {\n return res.status(401).json({\n error: 'invalid_client',\n error_description: 'Invalid client credentials',\n });\n }\n }\n\n if (token_type_hint === 'refresh_token') {\n await dcrUtils.deleteRefreshToken(store, token);\n } else if (token_type_hint === 'access_token') {\n await dcrUtils.deleteAccessToken(store, token);\n await dcrUtils.deleteProviderTokens(store, token);\n } else {\n // No hint - try both\n await dcrUtils.deleteRefreshToken(store, token);\n await dcrUtils.deleteAccessToken(store, token);\n await dcrUtils.deleteProviderTokens(store, token);\n }\n\n return res.status(200).send();\n });\n\n /**\n * Token Verification Endpoint\n * GET /oauth/verify\n *\n * Validates bearer tokens for Resource Server.\n * Returns AuthInfo with provider tokens for stateless DCR pattern.\n */\n router.get('/oauth/verify', async (req: Request, res: Response) => {\n const authHeader = req.headers.authorization;\n\n if (!authHeader || !authHeader.startsWith('Bearer ')) {\n return res.status(401).json({\n error: 'invalid_request',\n error_description: 'Missing or invalid Authorization header',\n });\n }\n\n const token = authHeader.substring(7);\n const tokenData = await dcrUtils.getAccessToken(store, token);\n\n if (!tokenData) {\n return res.status(401).json({\n error: 'invalid_token',\n error_description: 'Unknown or expired access token',\n });\n }\n\n const now = Date.now();\n const expiresAt = tokenData.created_at + tokenData.expires_in * 1000;\n\n if (now > expiresAt) {\n await dcrUtils.deleteAccessToken(store, token);\n await dcrUtils.deleteProviderTokens(store, token);\n return res.status(401).json({\n error: 'invalid_token',\n error_description: 'Access token has expired',\n });\n }\n\n const authInfo = {\n token,\n clientId: tokenData.client_id,\n scopes: tokenData.scope ? tokenData.scope.split(' ') : [],\n expiresAt,\n providerTokens: tokenData.providerTokens,\n };\n\n return res.json(authInfo);\n });\n\n /**\n * Debug endpoint to list registered clients (development only)\n */\n router.get('/debug/clients', async (_req: Request, res: Response) => {\n const clients = await dcrUtils.listClients(store);\n return res.json(clients);\n });\n\n return router;\n}\n"],"names":["createHash","randomUUID","express","DcrOAuthProvider","dcrUtils","createDcrRouter","config","router","Router","store","issuerUrl","baseUrl","scopesSupported","clientConfig","use","json","urlencoded","extended","get","_req","res","metadata","issuer","authorization_endpoint","token_endpoint","registration_endpoint","revocation_endpoint","scopes_supported","response_types_supported","grant_types_supported","token_endpoint_auth_methods_supported","code_challenge_methods_supported","service_documentation","resource","authorization_servers","bearer_methods_supported","post","req","registrationRequest","body","client","registerClient","status","error","error_description","Error","message","response_type","client_id","redirect_uri","scope","state","code_challenge","code_challenge_method","query","getClient","isValidRedirect","validateRedirectUri","googleState","dcrRequestState","undefined","created_at","Date","now","expires_at","set","googleAuthUrl","URL","searchParams","clientId","redirect","toString","code","googleCode","delete","tokenUrl","tokenParamsObj","grant_type","clientSecret","client_secret","tokenParams","URLSearchParams","tokenResponse","fetch","method","headers","ok","errorText","text","tokenData","providerTokens","accessToken","access_token","refresh_token","refreshToken","expiresAt","expires_in","dcrCode","authCode","setAuthCode","clientRedirectUrl","authHeader","authorization","startsWith","base64Credentials","substring","credentials","Buffer","from","id","secret","split","code_verifier","isValidClient","validateClient","getAuthCode","deleteAuthCode","computedChallenge","update","digest","refreshTokenValue","token_type","setAccessToken","setRefreshToken","setProviderTokens","getRefreshToken","refreshedProviderTokens","provider","verifyEndpoint","logger","info","console","log","warn","debug","refreshAccessToken","String","newAccessToken","newTokenData","token","token_type_hint","deleteRefreshToken","deleteAccessToken","deleteProviderTokens","send","getAccessToken","authInfo","scopes","clients","listClients"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-google/src/lib/dcr-utils.ts"],"sourcesContent":["/**\n * DCR Storage Utilities\n *\n * Keyv-based storage utilities for Dynamic Client Registration.\n * Follows @mcp-z/oauth pattern: single Keyv store with compound keys.\n *\n * Key Patterns:\n * - dcr:client:{clientId} -> RegisteredClient\n * - dcr:provider:{dcrToken} -> ProviderTokens\n * - dcr:authcode:{code} -> AuthorizationCode\n * - dcr:access:{token} -> AccessToken\n * - dcr:refresh:{token} -> AccessToken\n */\n\nimport type { DcrClientInformation, DcrClientMetadata, ProviderTokens } from '@mcp-z/oauth';\nimport { randomUUID } from 'crypto';\nimport type { Keyv } from 'keyv';\nimport type { AccessToken, AuthorizationCode, RegisteredClient } from '../types.ts';\n\n// ============================================================================\n// Client Operations\n// ============================================================================\n\n/**\n * Register a new OAuth client (RFC 7591 Section 3.1)\n *\n * @param store - Keyv store for all DCR data\n * @param metadata - Client registration metadata\n * @returns Registered client with credentials\n * @throws Error if validation fails\n */\nexport async function registerClient(store: Keyv, metadata: DcrClientMetadata): Promise<DcrClientInformation> {\n // Validate redirect URIs (required per RFC 7591)\n if (!metadata.redirect_uris || metadata.redirect_uris.length === 0) {\n throw new Error('redirect_uris is required');\n }\n\n // Generate client credentials\n const client_id = `dcr_${randomUUID()}`;\n const client_secret = randomUUID();\n\n // Default grant types and response types per RFC 7591 Section 2\n const grant_types = metadata.grant_types ?? ['authorization_code', 'refresh_token'];\n const response_types = metadata.response_types ?? ['code'];\n\n // Build registered client - only include optional fields if they have values\n const client: RegisteredClient = {\n client_id,\n client_secret,\n client_id_issued_at: Math.floor(Date.now() / 1000),\n client_secret_expires_at: 0, // Never expires\n redirect_uris: metadata.redirect_uris,\n token_endpoint_auth_method: metadata.token_endpoint_auth_method ?? 'client_secret_basic',\n grant_types,\n response_types,\n ...(metadata.client_name !== undefined && { client_name: metadata.client_name }),\n ...(metadata.client_uri !== undefined && { client_uri: metadata.client_uri }),\n ...(metadata.logo_uri !== undefined && { logo_uri: metadata.logo_uri }),\n ...(metadata.scope !== undefined && { scope: metadata.scope }),\n ...(metadata.contacts !== undefined && { contacts: metadata.contacts }),\n ...(metadata.tos_uri !== undefined && { tos_uri: metadata.tos_uri }),\n ...(metadata.policy_uri !== undefined && { policy_uri: metadata.policy_uri }),\n ...(metadata.jwks_uri !== undefined && { jwks_uri: metadata.jwks_uri }),\n ...(metadata.jwks !== undefined && { jwks: metadata.jwks }),\n ...(metadata.software_id !== undefined && { software_id: metadata.software_id }),\n ...(metadata.software_version !== undefined && { software_version: metadata.software_version }),\n created_at: Date.now(),\n };\n\n // Store client\n await store.set(`dcr:client:${client_id}`, client);\n\n // Return client information (excluding internal created_at)\n const { created_at, ...clientInfo } = client;\n return clientInfo;\n}\n\n/**\n * Get a registered client by ID\n *\n * @param store - Keyv store for all DCR data\n * @param clientId - Client identifier\n * @returns Registered client or undefined if not found\n */\nexport async function getClient(store: Keyv, clientId: string): Promise<RegisteredClient | undefined> {\n return await store.get(`dcr:client:${clientId}`);\n}\n\n/**\n * Validate client credentials\n *\n * @param store - Keyv store for all DCR data\n * @param clientId - Client identifier\n * @param clientSecret - Client secret\n * @returns True if credentials are valid\n */\nexport async function validateClient(store: Keyv, clientId: string, clientSecret: string): Promise<boolean> {\n const client = await getClient(store, clientId);\n if (!client) return false;\n return client.client_secret === clientSecret;\n}\n\n/**\n * Validate redirect URI for a client\n *\n * @param store - Keyv store for all DCR data\n * @param clientId - Client identifier\n * @param redirectUri - Redirect URI to validate\n * @returns True if redirect URI is registered\n */\nexport async function validateRedirectUri(store: Keyv, clientId: string, redirectUri: string): Promise<boolean> {\n const client = await getClient(store, clientId);\n if (!client || !client.redirect_uris) return false;\n return client.redirect_uris.includes(redirectUri);\n}\n\n/**\n * List all registered clients (for debugging)\n *\n * Note: This method uses Keyv's iterator which may not be available on all storage adapters.\n * For production use, consider maintaining a separate index of client IDs.\n *\n * @param store - Keyv store for all DCR data\n * @returns Array of all registered clients\n */\nexport async function listClients(store: Keyv): Promise<RegisteredClient[]> {\n const clients: RegisteredClient[] = [];\n\n // Check if iterator is available on the store\n if (store.iterator) {\n // Use iterator with namespace to iterate through dcr:client: keys\n const iterator = store.iterator('dcr:client:');\n for await (const [_key, value] of iterator) {\n if (value !== undefined) {\n clients.push(value as RegisteredClient);\n }\n }\n }\n\n return clients;\n}\n\n/**\n * Delete a registered client\n *\n * @param store - Keyv store for all DCR data\n * @param clientId - Client identifier\n */\nexport async function deleteClient(store: Keyv, clientId: string): Promise<void> {\n await store.delete(`dcr:client:${clientId}`);\n}\n\n// ============================================================================\n// Provider Token Operations\n// ============================================================================\n\n/**\n * Store provider tokens for a DCR access token\n *\n * @param store - Keyv store for all DCR data\n * @param dcrToken - DCR-issued access token (used as key)\n * @param tokens - Google provider tokens (access, refresh, expiry)\n */\nexport async function setProviderTokens(store: Keyv, dcrToken: string, tokens: ProviderTokens): Promise<void> {\n await store.set(`dcr:provider:${dcrToken}`, tokens);\n}\n\n/**\n * Retrieve provider tokens for a DCR access token\n *\n * @param store - Keyv store for all DCR data\n * @param dcrToken - DCR-issued access token\n * @returns Provider tokens or undefined if not found\n */\nexport async function getProviderTokens(store: Keyv, dcrToken: string): Promise<ProviderTokens | undefined> {\n return await store.get(`dcr:provider:${dcrToken}`);\n}\n\n/**\n * Delete provider tokens for a DCR access token\n *\n * @param store - Keyv store for all DCR data\n * @param dcrToken - DCR-issued access token\n */\nexport async function deleteProviderTokens(store: Keyv, dcrToken: string): Promise<void> {\n await store.delete(`dcr:provider:${dcrToken}`);\n}\n\n// ============================================================================\n// Authorization Code Operations\n// ============================================================================\n\n/**\n * Store an authorization code\n *\n * @param store - Keyv store for all DCR data\n * @param code - Authorization code\n * @param authCode - Authorization code data\n */\nexport async function setAuthCode(store: Keyv, code: string, authCode: AuthorizationCode): Promise<void> {\n await store.set(`dcr:authcode:${code}`, authCode);\n}\n\n/**\n * Get an authorization code\n *\n * @param store - Keyv store for all DCR data\n * @param code - Authorization code\n * @returns Authorization code data or undefined if not found\n */\nexport async function getAuthCode(store: Keyv, code: string): Promise<AuthorizationCode | undefined> {\n return await store.get(`dcr:authcode:${code}`);\n}\n\n/**\n * Delete an authorization code\n *\n * @param store - Keyv store for all DCR data\n * @param code - Authorization code\n */\nexport async function deleteAuthCode(store: Keyv, code: string): Promise<void> {\n await store.delete(`dcr:authcode:${code}`);\n}\n\n// ============================================================================\n// Access Token Operations\n// ============================================================================\n\n/**\n * Store an access token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Access token\n * @param tokenData - Access token data\n */\nexport async function setAccessToken(store: Keyv, token: string, tokenData: AccessToken): Promise<void> {\n await store.set(`dcr:access:${token}`, tokenData);\n}\n\n/**\n * Get an access token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Access token\n * @returns Access token data or undefined if not found\n */\nexport async function getAccessToken(store: Keyv, token: string): Promise<AccessToken | undefined> {\n return await store.get(`dcr:access:${token}`);\n}\n\n/**\n * Delete an access token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Access token\n */\nexport async function deleteAccessToken(store: Keyv, token: string): Promise<void> {\n await store.delete(`dcr:access:${token}`);\n}\n\n// ============================================================================\n// Refresh Token Operations\n// ============================================================================\n\n/**\n * Store a refresh token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Refresh token\n * @param tokenData - Access token data (contains refresh token context)\n */\nexport async function setRefreshToken(store: Keyv, token: string, tokenData: AccessToken): Promise<void> {\n await store.set(`dcr:refresh:${token}`, tokenData);\n}\n\n/**\n * Get a refresh token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Refresh token\n * @returns Access token data or undefined if not found\n */\nexport async function getRefreshToken(store: Keyv, token: string): Promise<AccessToken | undefined> {\n return await store.get(`dcr:refresh:${token}`);\n}\n\n/**\n * Delete a refresh token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Refresh token\n */\nexport async function deleteRefreshToken(store: Keyv, token: string): Promise<void> {\n await store.delete(`dcr:refresh:${token}`);\n}\n"],"names":["randomUUID","registerClient","store","metadata","redirect_uris","length","Error","client_id","client_secret","grant_types","response_types","client","client_id_issued_at","Math","floor","Date","now","client_secret_expires_at","token_endpoint_auth_method","client_name","undefined","client_uri","logo_uri","scope","contacts","tos_uri","policy_uri","jwks_uri","jwks","software_id","software_version","created_at","set","clientInfo","getClient","clientId","get","validateClient","clientSecret","validateRedirectUri","redirectUri","includes","listClients","clients","iterator","_key","value","push","deleteClient","delete","setProviderTokens","dcrToken","tokens","getProviderTokens","deleteProviderTokens","setAuthCode","code","authCode","getAuthCode","deleteAuthCode","setAccessToken","token","tokenData","getAccessToken","deleteAccessToken","setRefreshToken","getRefreshToken","deleteRefreshToken"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-google/src/lib/dcr-utils.ts"],"sourcesContent":["/**\n * DCR Storage Utilities\n *\n * Keyv-based storage utilities for Dynamic Client Registration.\n * Follows @mcp-z/oauth pattern: single Keyv store with compound keys.\n *\n * Key Patterns:\n * - dcr:client:{clientId} -> RegisteredClient\n * - dcr:provider:{dcrToken} -> ProviderTokens\n * - dcr:authcode:{code} -> AuthorizationCode\n * - dcr:access:{token} -> AccessToken\n * - dcr:refresh:{token} -> AccessToken\n */\n\nimport type { DcrClientInformation, DcrClientMetadata, ProviderTokens } from '@mcp-z/oauth';\nimport { randomUUID } from 'crypto';\nimport type { Keyv } from 'keyv';\nimport type { AccessToken, AuthorizationCode, RegisteredClient } from '../types.ts';\n\n// ============================================================================\n// Client Operations\n// ============================================================================\n\n/**\n * Register a new OAuth client (RFC 7591 Section 3.1)\n *\n * @param store - Keyv store for all DCR data\n * @param metadata - Client registration metadata\n * @returns Registered client with credentials\n * @throws Error if validation fails\n */\nexport async function registerClient(store: Keyv, metadata: DcrClientMetadata): Promise<DcrClientInformation> {\n // Validate redirect URIs (required per RFC 7591)\n if (!metadata.redirect_uris || metadata.redirect_uris.length === 0) {\n throw new Error('redirect_uris is required');\n }\n\n // Generate client credentials\n const client_id = `dcr_${randomUUID()}`;\n const client_secret = randomUUID();\n\n // Default grant types and response types per RFC 7591 Section 2\n const grant_types = metadata.grant_types ?? ['authorization_code', 'refresh_token'];\n const response_types = metadata.response_types ?? ['code'];\n\n // Build registered client - only include optional fields if they have values\n const client: RegisteredClient = {\n client_id,\n client_secret,\n client_id_issued_at: Math.floor(Date.now() / 1000),\n client_secret_expires_at: 0, // Never expires\n redirect_uris: metadata.redirect_uris,\n token_endpoint_auth_method: metadata.token_endpoint_auth_method ?? 'client_secret_basic',\n grant_types,\n response_types,\n ...(metadata.client_name !== undefined && { client_name: metadata.client_name }),\n ...(metadata.client_uri !== undefined && { client_uri: metadata.client_uri }),\n ...(metadata.logo_uri !== undefined && { logo_uri: metadata.logo_uri }),\n ...(metadata.scope !== undefined && { scope: metadata.scope }),\n ...(metadata.contacts !== undefined && { contacts: metadata.contacts }),\n ...(metadata.tos_uri !== undefined && { tos_uri: metadata.tos_uri }),\n ...(metadata.policy_uri !== undefined && { policy_uri: metadata.policy_uri }),\n ...(metadata.jwks_uri !== undefined && { jwks_uri: metadata.jwks_uri }),\n ...(metadata.jwks !== undefined && { jwks: metadata.jwks }),\n ...(metadata.software_id !== undefined && { software_id: metadata.software_id }),\n ...(metadata.software_version !== undefined && { software_version: metadata.software_version }),\n created_at: Date.now(),\n };\n\n // Store client\n await store.set(`dcr:client:${client_id}`, client);\n\n // Return client information (excluding internal created_at)\n const { created_at, ...clientInfo } = client;\n return clientInfo;\n}\n\n/**\n * Get a registered client by ID\n *\n * @param store - Keyv store for all DCR data\n * @param clientId - Client identifier\n * @returns Registered client or undefined if not found\n */\nexport async function getClient(store: Keyv, clientId: string): Promise<RegisteredClient | undefined> {\n return await store.get(`dcr:client:${clientId}`);\n}\n\n/**\n * Validate client credentials\n *\n * @param store - Keyv store for all DCR data\n * @param clientId - Client identifier\n * @param clientSecret - Client secret\n * @returns True if credentials are valid\n */\nexport async function validateClient(store: Keyv, clientId: string, clientSecret: string): Promise<boolean> {\n const client = await getClient(store, clientId);\n if (!client) return false;\n return client.client_secret === clientSecret;\n}\n\n/**\n * Validate redirect URI for a client\n *\n * @param store - Keyv store for all DCR data\n * @param clientId - Client identifier\n * @param redirectUri - Redirect URI to validate\n * @returns True if redirect URI is registered\n */\nexport async function validateRedirectUri(store: Keyv, clientId: string, redirectUri: string): Promise<boolean> {\n const client = await getClient(store, clientId);\n if (!client || !client.redirect_uris) return false;\n return client.redirect_uris.includes(redirectUri);\n}\n\n/**\n * List all registered clients (for debugging)\n *\n * Note: This method uses Keyv's iterator which may not be available on all storage adapters.\n * For production use, consider maintaining a separate index of client IDs.\n *\n * @param store - Keyv store for all DCR data\n * @returns Array of all registered clients\n */\nexport async function listClients(store: Keyv): Promise<RegisteredClient[]> {\n const clients: RegisteredClient[] = [];\n\n // Check if iterator is available on the store\n if (store.iterator) {\n // Use iterator with namespace to iterate through dcr:client: keys\n const iterator = store.iterator('dcr:client:');\n for await (const [_key, value] of iterator) {\n if (value !== undefined) {\n clients.push(value as RegisteredClient);\n }\n }\n }\n\n return clients;\n}\n\n/**\n * Delete a registered client\n *\n * @param store - Keyv store for all DCR data\n * @param clientId - Client identifier\n */\nexport async function deleteClient(store: Keyv, clientId: string): Promise<void> {\n await store.delete(`dcr:client:${clientId}`);\n}\n\n// ============================================================================\n// Provider Token Operations\n// ============================================================================\n\n/**\n * Store provider tokens for a DCR access token\n *\n * @param store - Keyv store for all DCR data\n * @param dcrToken - DCR-issued access token (used as key)\n * @param tokens - Google provider tokens (access, refresh, expiry)\n */\nexport async function setProviderTokens(store: Keyv, dcrToken: string, tokens: ProviderTokens): Promise<void> {\n await store.set(`dcr:provider:${dcrToken}`, tokens);\n}\n\n/**\n * Retrieve provider tokens for a DCR access token\n *\n * @param store - Keyv store for all DCR data\n * @param dcrToken - DCR-issued access token\n * @returns Provider tokens or undefined if not found\n */\nexport async function getProviderTokens(store: Keyv, dcrToken: string): Promise<ProviderTokens | undefined> {\n return await store.get(`dcr:provider:${dcrToken}`);\n}\n\n/**\n * Delete provider tokens for a DCR access token\n *\n * @param store - Keyv store for all DCR data\n * @param dcrToken - DCR-issued access token\n */\nexport async function deleteProviderTokens(store: Keyv, dcrToken: string): Promise<void> {\n await store.delete(`dcr:provider:${dcrToken}`);\n}\n\n// ============================================================================\n// Authorization Code Operations\n// ============================================================================\n\n/**\n * Store an authorization code\n *\n * @param store - Keyv store for all DCR data\n * @param code - Authorization code\n * @param authCode - Authorization code data\n */\nexport async function setAuthCode(store: Keyv, code: string, authCode: AuthorizationCode): Promise<void> {\n await store.set(`dcr:authcode:${code}`, authCode);\n}\n\n/**\n * Get an authorization code\n *\n * @param store - Keyv store for all DCR data\n * @param code - Authorization code\n * @returns Authorization code data or undefined if not found\n */\nexport async function getAuthCode(store: Keyv, code: string): Promise<AuthorizationCode | undefined> {\n return await store.get(`dcr:authcode:${code}`);\n}\n\n/**\n * Delete an authorization code\n *\n * @param store - Keyv store for all DCR data\n * @param code - Authorization code\n */\nexport async function deleteAuthCode(store: Keyv, code: string): Promise<void> {\n await store.delete(`dcr:authcode:${code}`);\n}\n\n// ============================================================================\n// Access Token Operations\n// ============================================================================\n\n/**\n * Store an access token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Access token\n * @param tokenData - Access token data\n */\nexport async function setAccessToken(store: Keyv, token: string, tokenData: AccessToken): Promise<void> {\n await store.set(`dcr:access:${token}`, tokenData);\n}\n\n/**\n * Get an access token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Access token\n * @returns Access token data or undefined if not found\n */\nexport async function getAccessToken(store: Keyv, token: string): Promise<AccessToken | undefined> {\n return await store.get(`dcr:access:${token}`);\n}\n\n/**\n * Delete an access token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Access token\n */\nexport async function deleteAccessToken(store: Keyv, token: string): Promise<void> {\n await store.delete(`dcr:access:${token}`);\n}\n\n// ============================================================================\n// Refresh Token Operations\n// ============================================================================\n\n/**\n * Store a refresh token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Refresh token\n * @param tokenData - Access token data (contains refresh token context)\n */\nexport async function setRefreshToken(store: Keyv, token: string, tokenData: AccessToken): Promise<void> {\n await store.set(`dcr:refresh:${token}`, tokenData);\n}\n\n/**\n * Get a refresh token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Refresh token\n * @returns Access token data or undefined if not found\n */\nexport async function getRefreshToken(store: Keyv, token: string): Promise<AccessToken | undefined> {\n return await store.get(`dcr:refresh:${token}`);\n}\n\n/**\n * Delete a refresh token\n *\n * @param store - Keyv store for all DCR data\n * @param token - Refresh token\n */\nexport async function deleteRefreshToken(store: Keyv, token: string): Promise<void> {\n await store.delete(`dcr:refresh:${token}`);\n}\n"],"names":["randomUUID","registerClient","store","metadata","redirect_uris","length","Error","client_id","client_secret","grant_types","response_types","client","client_id_issued_at","Math","floor","Date","now","client_secret_expires_at","token_endpoint_auth_method","client_name","undefined","client_uri","logo_uri","scope","contacts","tos_uri","policy_uri","jwks_uri","jwks","software_id","software_version","created_at","set","clientInfo","getClient","clientId","get","validateClient","clientSecret","validateRedirectUri","redirectUri","includes","listClients","clients","iterator","_key","value","push","deleteClient","delete","setProviderTokens","dcrToken","tokens","getProviderTokens","deleteProviderTokens","setAuthCode","code","authCode","getAuthCode","deleteAuthCode","setAccessToken","token","tokenData","getAccessToken","deleteAccessToken","setRefreshToken","getRefreshToken","deleteRefreshToken"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-google/src/lib/dcr-verify.ts"],"sourcesContent":["/**\n * DCR Token Verification Utilities\n *\n * Provides token verification for both self-hosted and external DCR modes:\n * - Self-hosted: Verifies tokens against local DCR router (/oauth/verify)\n * - External: Verifies tokens against Auth0/Stitch verification endpoint\n */\n\nimport type { ProviderTokens } from '@mcp-z/oauth';\nimport { fetchWithTimeout } from './fetch-with-timeout.ts';\n\n/**\n * Verification result from DCR authorization server\n */\nexport interface VerificationResult {\n /** Bearer token that was verified */\n token: string;\n /** Client ID associated with the token */\n clientId: string;\n /** OAuth scopes granted to the token */\n scopes: string[];\n /** Token expiration timestamp (milliseconds since epoch) */\n expiresAt: number;\n /** Provider tokens (Google access/refresh tokens) */\n providerTokens: ProviderTokens;\n}\n\n/**\n * Verify bearer token against DCR authorization server\n *\n * Supports both self-hosted and external DCR modes by calling the\n * /oauth/verify endpoint (or equivalent external URL).\n *\n * @param bearerToken - Bearer token to verify (without \"Bearer \" prefix)\n * @param verifyUrl - Verification endpoint URL (self-hosted or external)\n * @returns Verification result with provider tokens\n * @throws Error if verification fails\n *\n * @example Self-hosted mode\n * ```typescript\n * const result = await verifyBearerToken(\n * token,\n * 'http://localhost:3456/oauth/verify'\n * );\n * const auth = provider.toAuth(result.providerTokens);\n * ```\n *\n * @example External mode (Auth0/Stitch)\n * ```typescript\n * const result = await verifyBearerToken(\n * token,\n * 'https://auth.example.com/oauth/verify'\n * );\n * const auth = provider.toAuth(result.providerTokens);\n * ```\n */\nexport async function verifyBearerToken(bearerToken: string, verifyUrl: string): Promise<VerificationResult> {\n const response = await fetchWithTimeout(verifyUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token verification failed: ${response.status} ${errorText}`);\n }\n\n const result = (await response.json()) as VerificationResult;\n\n // Validate required fields\n if (!result.providerTokens || !result.providerTokens.accessToken) {\n throw new Error('Verification response missing required provider tokens');\n }\n\n return result;\n}\n"],"names":["fetchWithTimeout","verifyBearerToken","bearerToken","verifyUrl","response","method","headers","Authorization","ok","errorText","text","Error","status","result","json","providerTokens","accessToken"],"mappings":"AAAA;;;;;;CAMC,GAGD,SAASA,gBAAgB,QAAQ,0BAA0B;AAkB3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC,GACD,OAAO,eAAeC,kBAAkBC,WAAmB,EAAEC,SAAiB;IAC5E,MAAMC,WAAW,MAAMJ,iBAAiBG,WAAW;QACjDE,QAAQ;QACRC,SAAS;YACPC,eAAe,CAAC,OAAO,EAAEL,aAAa;QACxC;IACF;IAEA,IAAI,CAACE,SAASI,EAAE,EAAE;QAChB,MAAMC,YAAY,MAAML,SAASM,IAAI;QACrC,MAAM,IAAIC,MAAM,CAAC,2BAA2B,EAAEP,SAASQ,MAAM,CAAC,CAAC,EAAEH,WAAW;IAC9E;IAEA,MAAMI,SAAU,MAAMT,SAASU,IAAI;IAEnC,2BAA2B;IAC3B,IAAI,CAACD,OAAOE,cAAc,IAAI,CAACF,OAAOE,cAAc,CAACC,WAAW,EAAE;QAChE,MAAM,IAAIL,MAAM;IAClB;IAEA,OAAOE;AACT"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-google/src/lib/dcr-verify.ts"],"sourcesContent":["/**\n * DCR Token Verification Utilities\n *\n * Provides token verification for both self-hosted and external DCR modes:\n * - Self-hosted: Verifies tokens against local DCR router (/oauth/verify)\n * - External: Verifies tokens against Auth0/Stitch verification endpoint\n */\n\nimport type { ProviderTokens } from '@mcp-z/oauth';\nimport { fetchWithTimeout } from './fetch-with-timeout.ts';\n\n/**\n * Verification result from DCR authorization server\n */\nexport interface VerificationResult {\n /** Bearer token that was verified */\n token: string;\n /** Client ID associated with the token */\n clientId: string;\n /** OAuth scopes granted to the token */\n scopes: string[];\n /** Token expiration timestamp (milliseconds since epoch) */\n expiresAt: number;\n /** Provider tokens (Google access/refresh tokens) */\n providerTokens: ProviderTokens;\n}\n\n/**\n * Verify bearer token against DCR authorization server\n *\n * Supports both self-hosted and external DCR modes by calling the\n * /oauth/verify endpoint (or equivalent external URL).\n *\n * @param bearerToken - Bearer token to verify (without \"Bearer \" prefix)\n * @param verifyUrl - Verification endpoint URL (self-hosted or external)\n * @returns Verification result with provider tokens\n * @throws Error if verification fails\n *\n * @example Self-hosted mode\n * ```typescript\n * const result = await verifyBearerToken(\n * token,\n * 'http://localhost:3456/oauth/verify'\n * );\n * const auth = provider.toAuth(result.providerTokens);\n * ```\n *\n * @example External mode (Auth0/Stitch)\n * ```typescript\n * const result = await verifyBearerToken(\n * token,\n * 'https://auth.example.com/oauth/verify'\n * );\n * const auth = provider.toAuth(result.providerTokens);\n * ```\n */\nexport async function verifyBearerToken(bearerToken: string, verifyUrl: string): Promise<VerificationResult> {\n const response = await fetchWithTimeout(verifyUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token verification failed: ${response.status} ${errorText}`);\n }\n\n const result = (await response.json()) as VerificationResult;\n\n // Validate required fields\n if (!result.providerTokens || !result.providerTokens.accessToken) {\n throw new Error('Verification response missing required provider tokens');\n }\n\n return result;\n}\n"],"names":["fetchWithTimeout","verifyBearerToken","bearerToken","verifyUrl","response","method","headers","Authorization","ok","errorText","text","Error","status","result","json","providerTokens","accessToken"],"mappings":"AAAA;;;;;;CAMC,GAGD,SAASA,gBAAgB,QAAQ,0BAA0B;AAkB3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC,GACD,OAAO,eAAeC,kBAAkBC,WAAmB,EAAEC,SAAiB;IAC5E,MAAMC,WAAW,MAAMJ,iBAAiBG,WAAW;QACjDE,QAAQ;QACRC,SAAS;YACPC,eAAe,CAAC,OAAO,EAAEL,aAAa;QACxC;IACF;IAEA,IAAI,CAACE,SAASI,EAAE,EAAE;QAChB,MAAMC,YAAY,MAAML,SAASM,IAAI;QACrC,MAAM,IAAIC,MAAM,CAAC,2BAA2B,EAAEP,SAASQ,MAAM,CAAC,CAAC,EAAEH,WAAW;IAC9E;IAEA,MAAMI,SAAU,MAAMT,SAASU,IAAI;IAEnC,2BAA2B;IAC3B,IAAI,CAACD,OAAOE,cAAc,IAAI,CAACF,OAAOE,cAAc,CAACC,WAAW,EAAE;QAChE,MAAM,IAAIL,MAAM;IAClB;IAEA,OAAOE;AACT"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-google/src/lib/fetch-with-timeout.ts"],"sourcesContent":["/**\n * Fetch with timeout to prevent hanging requests\n *\n * This utility wraps the native fetch API with an AbortController to ensure\n * requests don't hang indefinitely. This is critical for OAuth token operations\n * where expired tokens might cause Google's servers to hang or respond slowly.\n *\n * @param url - URL to fetch\n * @param options - Fetch options\n * @param timeoutMs - Timeout in milliseconds (default: 30000)\n * @returns Fetch response\n * @throws Error if request times out\n */\nexport async function fetchWithTimeout(url: string, options?: RequestInit, timeoutMs = 30000): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n });\n clearTimeout(timeoutId);\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout after ${timeoutMs}ms`);\n }\n throw error;\n }\n}\n"],"names":["fetchWithTimeout","url","options","timeoutMs","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","signal","clearTimeout","error","Error","name"],"mappings":"AAAA;;;;;;;;;;;;CAYC,GACD,OAAO,eAAeA,iBAAiBC,GAAW,EAAEC,OAAqB,EAAEC,YAAY,KAAK;IAC1F,MAAMC,aAAa,IAAIC;IACvB,MAAMC,YAAYC,WAAW,IAAMH,WAAWI,KAAK,IAAIL;IAEvD,IAAI;QACF,MAAMM,WAAW,MAAMC,MAAMT,KAAK;YAChC,GAAGC,OAAO;YACVS,QAAQP,WAAWO,MAAM;QAC3B;QACAC,aAAaN;QACb,OAAOG;IACT,EAAE,OAAOI,OAAO;QACdD,aAAaN;QACb,IAAIO,iBAAiBC,SAASD,MAAME,IAAI,KAAK,cAAc;YACzD,MAAM,IAAID,MAAM,CAAC,sBAAsB,EAAEX,UAAU,EAAE,CAAC;QACxD;QACA,MAAMU;IACR;AACF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-google/src/lib/fetch-with-timeout.ts"],"sourcesContent":["/**\n * Fetch with timeout to prevent hanging requests\n *\n * This utility wraps the native fetch API with an AbortController to ensure\n * requests don't hang indefinitely. This is critical for OAuth token operations\n * where expired tokens might cause Google's servers to hang or respond slowly.\n *\n * @param url - URL to fetch\n * @param options - Fetch options\n * @param timeoutMs - Timeout in milliseconds (default: 30000)\n * @returns Fetch response\n * @throws Error if request times out\n */\nexport async function fetchWithTimeout(url: string, options?: RequestInit, timeoutMs = 30000): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n });\n clearTimeout(timeoutId);\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout after ${timeoutMs}ms`);\n }\n throw error;\n }\n}\n"],"names":["fetchWithTimeout","url","options","timeoutMs","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","signal","clearTimeout","error","Error","name"],"mappings":"AAAA;;;;;;;;;;;;CAYC,GACD,OAAO,eAAeA,iBAAiBC,GAAW,EAAEC,OAAqB,EAAEC,YAAY,KAAK;IAC1F,MAAMC,aAAa,IAAIC;IACvB,MAAMC,YAAYC,WAAW,IAAMH,WAAWI,KAAK,IAAIL;IAEvD,IAAI;QACF,MAAMM,WAAW,MAAMC,MAAMT,KAAK;YAChC,GAAGC,OAAO;YACVS,QAAQP,WAAWO,MAAM;QAC3B;QACAC,aAAaN;QACb,OAAOG;IACT,EAAE,OAAOI,OAAO;QACdD,aAAaN;QACb,IAAIO,iBAAiBC,SAASD,MAAME,IAAI,KAAK,cAAc;YACzD,MAAM,IAAID,MAAM,CAAC,sBAAsB,EAAEX,UAAU,EAAE,CAAC;QACxD;QACA,MAAMU;IACR;AACF"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Loopback OAuth callback router
3
+ *
4
+ * Handles GET /oauth/callback for persistent redirectUri deployments.
5
+ */
6
+ import express from 'express';
7
+ import type { LoopbackOAuthProvider } from '../providers/loopback-oauth.js';
8
+ export declare function createLoopbackCallbackRouter(provider: LoopbackOAuthProvider): express.Router;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Loopback OAuth callback router
3
+ *
4
+ * Handles GET /oauth/callback for persistent redirectUri deployments.
5
+ */ import { getErrorTemplate, getSuccessTemplate } from '@mcp-z/oauth';
6
+ import express from 'express';
7
+ export function createLoopbackCallbackRouter(provider) {
8
+ const router = express.Router();
9
+ router.get('/oauth/callback', async (req, res)=>{
10
+ const code = typeof req.query.code === 'string' ? req.query.code : undefined;
11
+ const state = typeof req.query.state === 'string' ? req.query.state : undefined;
12
+ const error = typeof req.query.error === 'string' ? req.query.error : undefined;
13
+ if (error) {
14
+ res.status(400).send(getErrorTemplate(error));
15
+ return;
16
+ }
17
+ if (!code) {
18
+ res.status(400).send(getErrorTemplate('No authorization code received'));
19
+ return;
20
+ }
21
+ try {
22
+ await provider.handleOAuthCallback({
23
+ code,
24
+ state
25
+ });
26
+ res.status(200).send(getSuccessTemplate());
27
+ } catch (callbackError) {
28
+ res.status(500).send(getErrorTemplate(callbackError instanceof Error ? callbackError.message : 'Token exchange failed'));
29
+ }
30
+ });
31
+ return router;
32
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-google/src/lib/loopback-router.ts"],"sourcesContent":["/**\n * Loopback OAuth callback router\n *\n * Handles GET /oauth/callback for persistent redirectUri deployments.\n */\n\nimport { getErrorTemplate, getSuccessTemplate } from '@mcp-z/oauth';\nimport type { Request, Response } from 'express';\nimport express from 'express';\nimport type { LoopbackOAuthProvider } from '../providers/loopback-oauth.ts';\n\nexport function createLoopbackCallbackRouter(provider: LoopbackOAuthProvider): express.Router {\n const router = express.Router();\n\n router.get('/oauth/callback', async (req: Request, res: Response) => {\n const code = typeof req.query.code === 'string' ? req.query.code : undefined;\n const state = typeof req.query.state === 'string' ? req.query.state : undefined;\n const error = typeof req.query.error === 'string' ? req.query.error : undefined;\n\n if (error) {\n res.status(400).send(getErrorTemplate(error));\n return;\n }\n\n if (!code) {\n res.status(400).send(getErrorTemplate('No authorization code received'));\n return;\n }\n\n try {\n await provider.handleOAuthCallback({ code, state });\n res.status(200).send(getSuccessTemplate());\n } catch (callbackError) {\n res.status(500).send(getErrorTemplate(callbackError instanceof Error ? callbackError.message : 'Token exchange failed'));\n }\n });\n\n return router;\n}\n"],"names":["getErrorTemplate","getSuccessTemplate","express","createLoopbackCallbackRouter","provider","router","Router","get","req","res","code","query","undefined","state","error","status","send","handleOAuthCallback","callbackError","Error","message"],"mappings":"AAAA;;;;CAIC,GAED,SAASA,gBAAgB,EAAEC,kBAAkB,QAAQ,eAAe;AAEpE,OAAOC,aAAa,UAAU;AAG9B,OAAO,SAASC,6BAA6BC,QAA+B;IAC1E,MAAMC,SAASH,QAAQI,MAAM;IAE7BD,OAAOE,GAAG,CAAC,mBAAmB,OAAOC,KAAcC;QACjD,MAAMC,OAAO,OAAOF,IAAIG,KAAK,CAACD,IAAI,KAAK,WAAWF,IAAIG,KAAK,CAACD,IAAI,GAAGE;QACnE,MAAMC,QAAQ,OAAOL,IAAIG,KAAK,CAACE,KAAK,KAAK,WAAWL,IAAIG,KAAK,CAACE,KAAK,GAAGD;QACtE,MAAME,QAAQ,OAAON,IAAIG,KAAK,CAACG,KAAK,KAAK,WAAWN,IAAIG,KAAK,CAACG,KAAK,GAAGF;QAEtE,IAAIE,OAAO;YACTL,IAAIM,MAAM,CAAC,KAAKC,IAAI,CAAChB,iBAAiBc;YACtC;QACF;QAEA,IAAI,CAACJ,MAAM;YACTD,IAAIM,MAAM,CAAC,KAAKC,IAAI,CAAChB,iBAAiB;YACtC;QACF;QAEA,IAAI;YACF,MAAMI,SAASa,mBAAmB,CAAC;gBAAEP;gBAAMG;YAAM;YACjDJ,IAAIM,MAAM,CAAC,KAAKC,IAAI,CAACf;QACvB,EAAE,OAAOiB,eAAe;YACtBT,IAAIM,MAAM,CAAC,KAAKC,IAAI,CAAChB,iBAAiBkB,yBAAyBC,QAAQD,cAAcE,OAAO,GAAG;QACjG;IACF;IAEA,OAAOf;AACT"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-google/src/lib/token-verifier.ts"],"sourcesContent":["/**\n * DCR Token Verifier\n *\n * Validates bearer tokens by calling Authorization Server's verification endpoint.\n * Implements proper AS/RS separation - Resource Server doesn't access token storage.\n */\n\nimport type { ProviderTokens } from '@mcp-z/oauth';\n\n/**\n * Authentication information from token verification\n */\nexport interface AuthInfo {\n /** Bearer access token */\n token: string;\n\n /** Client ID that owns the token */\n clientId: string;\n\n /** Granted scopes */\n scopes: string[];\n\n /** Token expiration timestamp (milliseconds since epoch) */\n expiresAt: number;\n\n /** Google provider tokens (if available) */\n providerTokens?: ProviderTokens;\n}\n\n/**\n * DCR Token Verifier validates access tokens via Authorization Server\n *\n * This implements proper OAuth 2.0 architecture where the Resource Server\n * (MCP server) validates tokens by calling the Authorization Server's\n * verification endpoint rather than accessing token storage directly.\n */\nexport class DcrTokenVerifier {\n private verifyUrl: string;\n\n /**\n * @param verifyUrl - Authorization Server's /oauth/verify endpoint URL\n */\n constructor(verifyUrl: string) {\n this.verifyUrl = verifyUrl;\n }\n\n /**\n * Verify an access token by calling the Authorization Server\n *\n * @param token - Bearer access token to validate\n * @returns AuthInfo with token metadata and provider tokens\n * @throws Error if token is invalid or verification fails\n */\n async verifyAccessToken(token: string): Promise<AuthInfo> {\n try {\n const response = await fetch(this.verifyUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n let errorMessage = 'Unknown error';\n try {\n const error = await response.json();\n errorMessage = (error as { error_description?: string; error?: string }).error_description ?? (error as { error?: string }).error ?? errorMessage;\n } catch {\n // Failed to parse error JSON, use status text\n errorMessage = response.statusText;\n }\n throw new Error(`Token verification failed: ${errorMessage}`);\n }\n\n const authInfo = (await response.json()) as AuthInfo;\n return authInfo;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Token verification failed: ${String(error)}`);\n }\n }\n}\n"],"names":["DcrTokenVerifier","verifyAccessToken","token","response","fetch","verifyUrl","method","headers","Authorization","ok","errorMessage","error","json","error_description","statusText","Error","authInfo","String"],"mappings":"AAAA;;;;;CAKC,GAwBD;;;;;;CAMC,GACD,OAAO,MAAMA;IAUX;;;;;;GAMC,GACD,MAAMC,kBAAkBC,KAAa,EAAqB;QACxD,IAAI;YACF,MAAMC,WAAW,MAAMC,MAAM,IAAI,CAACC,SAAS,EAAE;gBAC3CC,QAAQ;gBACRC,SAAS;oBACPC,eAAe,CAAC,OAAO,EAAEN,OAAO;gBAClC;YACF;YAEA,IAAI,CAACC,SAASM,EAAE,EAAE;gBAChB,IAAIC,eAAe;gBACnB,IAAI;wBAEa,MAAA;oBADf,MAAMC,QAAQ,MAAMR,SAASS,IAAI;oBACjCF,gBAAe,QAAA,2BAAA,AAACC,MAAyDE,iBAAiB,cAA3E,sCAAA,2BAA+E,AAACF,MAA6BA,KAAK,cAAlH,kBAAA,OAAsHD;gBACvI,EAAE,OAAM;oBACN,8CAA8C;oBAC9CA,eAAeP,SAASW,UAAU;gBACpC;gBACA,MAAM,IAAIC,MAAM,CAAC,2BAA2B,EAAEL,cAAc;YAC9D;YAEA,MAAMM,WAAY,MAAMb,SAASS,IAAI;YACrC,OAAOI;QACT,EAAE,OAAOL,OAAO;YACd,IAAIA,iBAAiBI,OAAO;gBAC1B,MAAMJ;YACR;YACA,MAAM,IAAII,MAAM,CAAC,2BAA2B,EAAEE,OAAON,QAAQ;QAC/D;IACF;IA3CA;;GAEC,GACD,YAAYN,SAAiB,CAAE;QAC7B,IAAI,CAACA,SAAS,GAAGA;IACnB;AAuCF"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-google/src/lib/token-verifier.ts"],"sourcesContent":["/**\n * DCR Token Verifier\n *\n * Validates bearer tokens by calling Authorization Server's verification endpoint.\n * Implements proper AS/RS separation - Resource Server doesn't access token storage.\n */\n\nimport type { ProviderTokens } from '@mcp-z/oauth';\n\n/**\n * Authentication information from token verification\n */\nexport interface AuthInfo {\n /** Bearer access token */\n token: string;\n\n /** Client ID that owns the token */\n clientId: string;\n\n /** Granted scopes */\n scopes: string[];\n\n /** Token expiration timestamp (milliseconds since epoch) */\n expiresAt: number;\n\n /** Google provider tokens (if available) */\n providerTokens?: ProviderTokens;\n}\n\n/**\n * DCR Token Verifier validates access tokens via Authorization Server\n *\n * This implements proper OAuth 2.0 architecture where the Resource Server\n * (MCP server) validates tokens by calling the Authorization Server's\n * verification endpoint rather than accessing token storage directly.\n */\nexport class DcrTokenVerifier {\n private verifyUrl: string;\n\n /**\n * @param verifyUrl - Authorization Server's /oauth/verify endpoint URL\n */\n constructor(verifyUrl: string) {\n this.verifyUrl = verifyUrl;\n }\n\n /**\n * Verify an access token by calling the Authorization Server\n *\n * @param token - Bearer access token to validate\n * @returns AuthInfo with token metadata and provider tokens\n * @throws Error if token is invalid or verification fails\n */\n async verifyAccessToken(token: string): Promise<AuthInfo> {\n try {\n const response = await fetch(this.verifyUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n let errorMessage = 'Unknown error';\n try {\n const error = await response.json();\n errorMessage = (error as { error_description?: string; error?: string }).error_description ?? (error as { error?: string }).error ?? errorMessage;\n } catch {\n // Failed to parse error JSON, use status text\n errorMessage = response.statusText;\n }\n throw new Error(`Token verification failed: ${errorMessage}`);\n }\n\n const authInfo = (await response.json()) as AuthInfo;\n return authInfo;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Token verification failed: ${String(error)}`);\n }\n }\n}\n"],"names":["DcrTokenVerifier","verifyAccessToken","token","response","fetch","verifyUrl","method","headers","Authorization","ok","errorMessage","error","json","error_description","statusText","Error","authInfo","String"],"mappings":"AAAA;;;;;CAKC,GAwBD;;;;;;CAMC,GACD,OAAO,MAAMA;IAUX;;;;;;GAMC,GACD,MAAMC,kBAAkBC,KAAa,EAAqB;QACxD,IAAI;YACF,MAAMC,WAAW,MAAMC,MAAM,IAAI,CAACC,SAAS,EAAE;gBAC3CC,QAAQ;gBACRC,SAAS;oBACPC,eAAe,CAAC,OAAO,EAAEN,OAAO;gBAClC;YACF;YAEA,IAAI,CAACC,SAASM,EAAE,EAAE;gBAChB,IAAIC,eAAe;gBACnB,IAAI;wBAEa,MAAA;oBADf,MAAMC,QAAQ,MAAMR,SAASS,IAAI;oBACjCF,gBAAe,QAAA,2BAAA,AAACC,MAAyDE,iBAAiB,cAA3E,sCAAA,2BAA+E,AAACF,MAA6BA,KAAK,cAAlH,kBAAA,OAAsHD;gBACvI,EAAE,OAAM;oBACN,8CAA8C;oBAC9CA,eAAeP,SAASW,UAAU;gBACpC;gBACA,MAAM,IAAIC,MAAM,CAAC,2BAA2B,EAAEL,cAAc;YAC9D;YAEA,MAAMM,WAAY,MAAMb,SAASS,IAAI;YACrC,OAAOI;QACT,EAAE,OAAOL,OAAO;YACd,IAAIA,iBAAiBI,OAAO;gBAC1B,MAAMJ;YACR;YACA,MAAM,IAAII,MAAM,CAAC,2BAA2B,EAAEE,OAAON,QAAQ;QAC/D;IACF;IA3CA;;GAEC,GACD,YAAYN,SAAiB,CAAE;QAC7B,IAAI,CAACA,SAAS,GAAGA;IACnB;AAuCF"}
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-google/src/providers/dcr.ts"],"sourcesContent":["/**\n * DCR Provider - Stateless Dynamic Client Registration Provider\n *\n * Implements stateless provider pattern where provider tokens are received from\n * token verification context rather than managed by the provider itself.\n *\n * Use case: MCP HTTP servers with DCR authentication where client manages tokens\n * and provider only handles Google API calls with provided credentials.\n */\n\nimport type { ProviderTokens } from '@mcp-z/oauth';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { OAuth2Client } from 'google-auth-library';\nimport type { AuthContext, EnrichedExtra, Logger } from '../types.ts';\n\n/**\n * DCR Provider configuration\n */\nexport interface DcrOAuthProviderConfig {\n /** Google application client ID */\n clientId: string;\n\n /** Google application client secret (optional for public clients) */\n clientSecret?: string;\n\n /** OAuth scopes */\n scope: string;\n\n /** DCR token verification endpoint URL (e.g., http://localhost:3000/oauth/verify) */\n verifyEndpoint: string;\n\n /** Logger for auth operations */\n logger: Logger;\n}\n\n/**\n * Google TokenResponse\n */\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * DCR Provider - Stateless OAuth provider for Dynamic Client Registration\n *\n * Unlike LoopbackOAuthProvider which manages token storage, DcrOAuthProvider is stateless:\n * - Receives provider tokens from verification context (HTTP bearer auth)\n * - Creates auth providers on-demand from tokens\n * - Handles token refresh using Google OAuth 2.0\n * - No token storage dependency\n *\n * Pattern:\n * ```typescript\n * const provider = new DcrOAuthProvider(config);\n * const auth = provider.toAuth(providerTokens);\n * const accessToken = await getAccessToken(auth);\n * ```\n */\nexport class DcrOAuthProvider {\n private config: DcrOAuthProviderConfig;\n private emailCache = new Map<string, { email: string; expiresAt: number }>();\n\n constructor(config: DcrOAuthProviderConfig) {\n this.config = config;\n }\n\n /**\n * Create Google OAuth2Client from provider tokens\n *\n * This is the core stateless pattern - provider receives tokens from context\n * (token verification, HTTP request) and creates OAuth2Client on-demand.\n *\n * @param tokens - Provider tokens (Google access/refresh tokens)\n * @returns Google OAuth2Client configured with credentials\n */\n toAuth(tokens: ProviderTokens): OAuth2Client {\n const { clientId, clientSecret } = this.config;\n\n // Create OAuth2Client with credentials\n const client = new OAuth2Client({\n clientId,\n ...(clientSecret && { clientSecret }),\n });\n\n // Set initial credentials (convert undefined to null for Google's Credentials type)\n client.credentials = {\n access_token: tokens.accessToken,\n refresh_token: tokens.refreshToken ?? null,\n expiry_date: tokens.expiresAt ?? null,\n token_type: 'Bearer',\n };\n\n // Override getRequestMetadataAsync to handle token refresh\n // @ts-expect-error - Access protected method for token refresh\n const originalGetMetadata = client.getRequestMetadataAsync.bind(client);\n\n // @ts-expect-error - Override protected method for token refresh\n client.getRequestMetadataAsync = async (url?: string) => {\n // Check if token needs refresh\n if (this.needsRefresh(client.credentials.expiry_date)) {\n try {\n // Use built-in refresh mechanism\n const refreshedTokens = await client.refreshAccessToken();\n client.credentials = refreshedTokens.credentials;\n } catch (error) {\n throw new Error(`Token refresh failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n return originalGetMetadata(url);\n };\n\n return client;\n }\n\n /**\n * Check if token needs refresh (with 1 minute buffer)\n */\n private needsRefresh(expiryDate: number | null | undefined): boolean {\n if (!expiryDate) return false; // No expiry = no refresh needed\n return Date.now() >= expiryDate - 60000; // 1 minute buffer\n }\n\n /**\n * Refresh Google access token using refresh token\n *\n * @param refreshToken - Google refresh token\n * @returns New provider tokens\n */\n async refreshAccessToken(refreshToken: string): Promise<ProviderTokens> {\n const { clientId, clientSecret } = this.config;\n\n const tokenUrl = 'https://oauth2.googleapis.com/token';\n const params: Record<string, string> = {\n refresh_token: refreshToken,\n client_id: clientId,\n grant_type: 'refresh_token',\n };\n\n // Only include client_secret for confidential clients\n if (clientSecret) {\n params.client_secret = clientSecret;\n }\n\n const body = new URLSearchParams(params);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed: ${response.status} ${errorText}`);\n }\n\n const tokenResponse = (await response.json()) as TokenResponse;\n\n const result: ProviderTokens = {\n accessToken: tokenResponse.access_token,\n refreshToken: refreshToken, // Keep original refresh token\n };\n\n // Only add optional fields if they have values\n if (tokenResponse.expires_in !== undefined) {\n result.expiresAt = Date.now() + tokenResponse.expires_in * 1000;\n }\n if (tokenResponse.scope !== undefined) {\n result.scope = tokenResponse.scope;\n }\n\n return result;\n }\n\n /**\n * Get user email from Google userinfo API (with caching)\n *\n * @param tokens - Provider tokens to use for API call\n * @returns User's email address\n */\n async getUserEmail(tokens: ProviderTokens): Promise<string> {\n const cacheKey = tokens.accessToken;\n const cached = this.emailCache.get(cacheKey);\n\n // Check cache (with same expiry as access token)\n if (cached && Date.now() < cached.expiresAt) {\n return cached.email;\n }\n\n const auth = this.toAuth(tokens);\n\n // Use OAuth2Client to make authenticated request\n const response = await auth.request({\n url: 'https://www.googleapis.com/oauth2/v2/userinfo',\n method: 'GET',\n });\n\n const userInfo = response.data as { email: string };\n const email = userInfo.email;\n\n // Cache with token expiration (default 1 hour if not specified)\n this.emailCache.set(cacheKey, {\n email,\n expiresAt: tokens.expiresAt ?? Date.now() + 3600000,\n });\n\n return email;\n }\n\n /**\n * Auth middleware for HTTP servers with DCR bearer auth\n * Validates bearer tokens and enriches extra with provider tokens\n *\n * Pattern:\n * ```typescript\n * const provider = new DcrOAuthProvider({ ..., verifyEndpoint: 'http://localhost:3000/oauth/verify' });\n * const authMiddleware = provider.authMiddleware();\n * const tools = toolFactories.map(f => f()).map(authMiddleware.withToolAuth);\n * const resources = resourceFactories.map(f => f()).map(authMiddleware.withResourceAuth);\n * const prompts = promptFactories.map(f => f()).map(authMiddleware.withPromptAuth);\n * ```\n */\n authMiddleware() {\n // Shared wrapper logic - extracts extra parameter from specified position\n // Generic T captures the actual module type; handler is cast from unknown to callable\n const wrapAtPosition = <T extends { name: string; handler: unknown; [key: string]: unknown }>(module: T, extraPosition: number): T => {\n const originalHandler = module.handler as (...args: unknown[]) => Promise<unknown>;\n\n const wrappedHandler = async (...allArgs: unknown[]) => {\n // Extract extra from the correct position\n const extra = allArgs[extraPosition] as EnrichedExtra;\n\n // Extract DCR bearer token from SDK's authInfo (if present) or request headers\n let bearerToken: string | undefined;\n\n // Option 1: Token already verified by SDK's bearerAuth middleware\n if (extra.authInfo && typeof extra.authInfo === 'object') {\n // authInfo contains the validated token - extract it\n // The SDK's bearerAuth middleware already validated it, but we need the raw token for /oauth/verify\n // Check if authInfo has the token directly, otherwise extract from headers\n const authInfo = extra.authInfo as unknown as Record<string, unknown>;\n bearerToken = (typeof authInfo.accessToken === 'string' ? authInfo.accessToken : undefined) ?? (typeof authInfo.token === 'string' ? authInfo.token : undefined);\n }\n\n // Option 2: Extract from Authorization header\n if (!bearerToken && extra.requestInfo?.headers) {\n const authHeader = extra.requestInfo.headers.authorization || extra.requestInfo.headers.Authorization;\n if (authHeader) {\n // Handle both string and string[] types\n const headerValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;\n if (headerValue) {\n const match = /^Bearer\\s+(.+)$/i.exec(headerValue);\n if (match) {\n bearerToken = match[1];\n }\n }\n }\n }\n\n if (!bearerToken) {\n throw new McpError(ErrorCode.InvalidRequest, 'Missing Authorization header. DCR mode requires bearer token.');\n }\n\n // Call /oauth/verify to validate DCR token and get provider tokens\n const verifyResponse = await fetch(this.config.verifyEndpoint, {\n headers: { Authorization: `Bearer ${bearerToken}` },\n });\n\n if (!verifyResponse.ok) {\n throw new McpError(ErrorCode.InvalidRequest, `Token verification failed: ${verifyResponse.status}`);\n }\n\n const verifyData = (await verifyResponse.json()) as {\n providerTokens: ProviderTokens;\n };\n\n // Fetch user email to use as accountId (with caching)\n let accountId: string;\n try {\n accountId = await this.getUserEmail(verifyData.providerTokens);\n } catch (error) {\n throw new McpError(ErrorCode.InternalError, `Failed to get user email for DCR authentication: ${error instanceof Error ? error.message : String(error)}`);\n }\n\n // Create auth client from provider tokens\n const auth = this.toAuth(verifyData.providerTokens);\n\n // Inject authContext and logger into extra\n (extra as { authContext?: AuthContext }).authContext = {\n auth,\n accountId, // User's email address\n };\n (extra as { logger?: unknown }).logger = this.config.logger;\n\n // Call original handler with all args\n return await originalHandler(...allArgs);\n };\n\n return {\n ...module,\n handler: wrappedHandler,\n } as T;\n };\n\n return {\n // Use structural constraints to avoid contravariance check on handler type.\n // wrapAtPosition is now generic and returns T directly.\n withToolAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 1),\n withResourceAuth: <T extends { name: string; template?: unknown; config?: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 2),\n withPromptAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 0),\n };\n }\n}\n"],"names":["ErrorCode","McpError","OAuth2Client","DcrOAuthProvider","toAuth","tokens","clientId","clientSecret","config","client","credentials","access_token","accessToken","refresh_token","refreshToken","expiry_date","expiresAt","token_type","originalGetMetadata","getRequestMetadataAsync","bind","url","needsRefresh","refreshedTokens","refreshAccessToken","error","Error","message","String","expiryDate","Date","now","tokenUrl","params","client_id","grant_type","client_secret","body","URLSearchParams","response","fetch","method","headers","toString","ok","errorText","text","status","tokenResponse","json","result","expires_in","undefined","scope","getUserEmail","cacheKey","cached","emailCache","get","email","auth","request","userInfo","data","set","authMiddleware","wrapAtPosition","module","extraPosition","originalHandler","handler","wrappedHandler","allArgs","extra","bearerToken","authInfo","token","requestInfo","authHeader","authorization","Authorization","headerValue","Array","isArray","match","exec","InvalidRequest","verifyResponse","verifyEndpoint","verifyData","accountId","providerTokens","InternalError","authContext","logger","withToolAuth","withResourceAuth","withPromptAuth","Map"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-google/src/providers/dcr.ts"],"sourcesContent":["/**\n * DCR Provider - Stateless Dynamic Client Registration Provider\n *\n * Implements stateless provider pattern where provider tokens are received from\n * token verification context rather than managed by the provider itself.\n *\n * Use case: MCP HTTP servers with DCR authentication where client manages tokens\n * and provider only handles Google API calls with provided credentials.\n */\n\nimport type { ProviderTokens } from '@mcp-z/oauth';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { OAuth2Client } from 'google-auth-library';\nimport type { AuthContext, EnrichedExtra, Logger } from '../types.ts';\n\n/**\n * DCR Provider configuration\n */\nexport interface DcrOAuthProviderConfig {\n /** Google application client ID */\n clientId: string;\n\n /** Google application client secret (optional for public clients) */\n clientSecret?: string;\n\n /** OAuth scopes */\n scope: string;\n\n /** DCR token verification endpoint URL (e.g., http://localhost:3000/oauth/verify) */\n verifyEndpoint: string;\n\n /** Logger for auth operations */\n logger: Logger;\n}\n\n/**\n * Google TokenResponse\n */\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * DCR Provider - Stateless OAuth provider for Dynamic Client Registration\n *\n * Unlike LoopbackOAuthProvider which manages token storage, DcrOAuthProvider is stateless:\n * - Receives provider tokens from verification context (HTTP bearer auth)\n * - Creates auth providers on-demand from tokens\n * - Handles token refresh using Google OAuth 2.0\n * - No token storage dependency\n *\n * Pattern:\n * ```typescript\n * const provider = new DcrOAuthProvider(config);\n * const auth = provider.toAuth(providerTokens);\n * const accessToken = await getAccessToken(auth);\n * ```\n */\nexport class DcrOAuthProvider {\n private config: DcrOAuthProviderConfig;\n private emailCache = new Map<string, { email: string; expiresAt: number }>();\n\n constructor(config: DcrOAuthProviderConfig) {\n this.config = config;\n }\n\n /**\n * Create Google OAuth2Client from provider tokens\n *\n * This is the core stateless pattern - provider receives tokens from context\n * (token verification, HTTP request) and creates OAuth2Client on-demand.\n *\n * @param tokens - Provider tokens (Google access/refresh tokens)\n * @returns Google OAuth2Client configured with credentials\n */\n toAuth(tokens: ProviderTokens): OAuth2Client {\n const { clientId, clientSecret } = this.config;\n\n // Create OAuth2Client with credentials\n const client = new OAuth2Client({\n clientId,\n ...(clientSecret && { clientSecret }),\n });\n\n // Set initial credentials (convert undefined to null for Google's Credentials type)\n client.credentials = {\n access_token: tokens.accessToken,\n refresh_token: tokens.refreshToken ?? null,\n expiry_date: tokens.expiresAt ?? null,\n token_type: 'Bearer',\n };\n\n // Override getRequestMetadataAsync to handle token refresh\n // @ts-expect-error - Access protected method for token refresh\n const originalGetMetadata = client.getRequestMetadataAsync.bind(client);\n\n // @ts-expect-error - Override protected method for token refresh\n client.getRequestMetadataAsync = async (url?: string) => {\n // Check if token needs refresh\n if (this.needsRefresh(client.credentials.expiry_date)) {\n try {\n // Use built-in refresh mechanism\n const refreshedTokens = await client.refreshAccessToken();\n client.credentials = refreshedTokens.credentials;\n } catch (error) {\n throw new Error(`Token refresh failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n return originalGetMetadata(url);\n };\n\n return client;\n }\n\n /**\n * Check if token needs refresh (with 1 minute buffer)\n */\n private needsRefresh(expiryDate: number | null | undefined): boolean {\n if (!expiryDate) return false; // No expiry = no refresh needed\n return Date.now() >= expiryDate - 60000; // 1 minute buffer\n }\n\n /**\n * Refresh Google access token using refresh token\n *\n * @param refreshToken - Google refresh token\n * @returns New provider tokens\n */\n async refreshAccessToken(refreshToken: string): Promise<ProviderTokens> {\n const { clientId, clientSecret } = this.config;\n\n const tokenUrl = 'https://oauth2.googleapis.com/token';\n const params: Record<string, string> = {\n refresh_token: refreshToken,\n client_id: clientId,\n grant_type: 'refresh_token',\n };\n\n // Only include client_secret for confidential clients\n if (clientSecret) {\n params.client_secret = clientSecret;\n }\n\n const body = new URLSearchParams(params);\n\n const response = await fetch(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed: ${response.status} ${errorText}`);\n }\n\n const tokenResponse = (await response.json()) as TokenResponse;\n\n const result: ProviderTokens = {\n accessToken: tokenResponse.access_token,\n refreshToken: refreshToken, // Keep original refresh token\n };\n\n // Only add optional fields if they have values\n if (tokenResponse.expires_in !== undefined) {\n result.expiresAt = Date.now() + tokenResponse.expires_in * 1000;\n }\n if (tokenResponse.scope !== undefined) {\n result.scope = tokenResponse.scope;\n }\n\n return result;\n }\n\n /**\n * Get user email from Google userinfo API (with caching)\n *\n * @param tokens - Provider tokens to use for API call\n * @returns User's email address\n */\n async getUserEmail(tokens: ProviderTokens): Promise<string> {\n const cacheKey = tokens.accessToken;\n const cached = this.emailCache.get(cacheKey);\n\n // Check cache (with same expiry as access token)\n if (cached && Date.now() < cached.expiresAt) {\n return cached.email;\n }\n\n const auth = this.toAuth(tokens);\n\n // Use OAuth2Client to make authenticated request\n const response = await auth.request({\n url: 'https://www.googleapis.com/oauth2/v2/userinfo',\n method: 'GET',\n });\n\n const userInfo = response.data as { email: string };\n const email = userInfo.email;\n\n // Cache with token expiration (default 1 hour if not specified)\n this.emailCache.set(cacheKey, {\n email,\n expiresAt: tokens.expiresAt ?? Date.now() + 3600000,\n });\n\n return email;\n }\n\n /**\n * Auth middleware for HTTP servers with DCR bearer auth\n * Validates bearer tokens and enriches extra with provider tokens\n *\n * Pattern:\n * ```typescript\n * const provider = new DcrOAuthProvider({ ..., verifyEndpoint: 'http://localhost:3000/oauth/verify' });\n * const authMiddleware = provider.authMiddleware();\n * const tools = toolFactories.map(f => f()).map(authMiddleware.withToolAuth);\n * const resources = resourceFactories.map(f => f()).map(authMiddleware.withResourceAuth);\n * const prompts = promptFactories.map(f => f()).map(authMiddleware.withPromptAuth);\n * ```\n */\n authMiddleware() {\n // Shared wrapper logic - extracts extra parameter from specified position\n // Generic T captures the actual module type; handler is cast from unknown to callable\n const wrapAtPosition = <T extends { name: string; handler: unknown; [key: string]: unknown }>(module: T, extraPosition: number): T => {\n const originalHandler = module.handler as (...args: unknown[]) => Promise<unknown>;\n\n const wrappedHandler = async (...allArgs: unknown[]) => {\n // Extract extra from the correct position\n const extra = allArgs[extraPosition] as EnrichedExtra;\n\n // Extract DCR bearer token from SDK's authInfo (if present) or request headers\n let bearerToken: string | undefined;\n\n // Option 1: Token already verified by SDK's bearerAuth middleware\n if (extra.authInfo && typeof extra.authInfo === 'object') {\n // authInfo contains the validated token - extract it\n // The SDK's bearerAuth middleware already validated it, but we need the raw token for /oauth/verify\n // Check if authInfo has the token directly, otherwise extract from headers\n const authInfo = extra.authInfo as unknown as Record<string, unknown>;\n bearerToken = (typeof authInfo.accessToken === 'string' ? authInfo.accessToken : undefined) ?? (typeof authInfo.token === 'string' ? authInfo.token : undefined);\n }\n\n // Option 2: Extract from Authorization header\n if (!bearerToken && extra.requestInfo?.headers) {\n const authHeader = extra.requestInfo.headers.authorization || extra.requestInfo.headers.Authorization;\n if (authHeader) {\n // Handle both string and string[] types\n const headerValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;\n if (headerValue) {\n const match = /^Bearer\\s+(.+)$/i.exec(headerValue);\n if (match) {\n bearerToken = match[1];\n }\n }\n }\n }\n\n if (!bearerToken) {\n throw new McpError(ErrorCode.InvalidRequest, 'Missing Authorization header. DCR mode requires bearer token.');\n }\n\n // Call /oauth/verify to validate DCR token and get provider tokens\n const verifyResponse = await fetch(this.config.verifyEndpoint, {\n headers: { Authorization: `Bearer ${bearerToken}` },\n });\n\n if (!verifyResponse.ok) {\n throw new McpError(ErrorCode.InvalidRequest, `Token verification failed: ${verifyResponse.status}`);\n }\n\n const verifyData = (await verifyResponse.json()) as {\n providerTokens: ProviderTokens;\n };\n\n // Fetch user email to use as accountId (with caching)\n let accountId: string;\n try {\n accountId = await this.getUserEmail(verifyData.providerTokens);\n } catch (error) {\n throw new McpError(ErrorCode.InternalError, `Failed to get user email for DCR authentication: ${error instanceof Error ? error.message : String(error)}`);\n }\n\n // Create auth client from provider tokens\n const auth = this.toAuth(verifyData.providerTokens);\n\n // Inject authContext and logger into extra\n (extra as { authContext?: AuthContext }).authContext = {\n auth,\n accountId, // User's email address\n };\n (extra as { logger?: unknown }).logger = this.config.logger;\n\n // Call original handler with all args\n return await originalHandler(...allArgs);\n };\n\n return {\n ...module,\n handler: wrappedHandler,\n } as T;\n };\n\n return {\n // Use structural constraints to avoid contravariance check on handler type.\n // wrapAtPosition is now generic and returns T directly.\n withToolAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 1),\n withResourceAuth: <T extends { name: string; template?: unknown; config?: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 2),\n withPromptAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 0),\n };\n }\n}\n"],"names":["ErrorCode","McpError","OAuth2Client","DcrOAuthProvider","toAuth","tokens","clientId","clientSecret","config","client","credentials","access_token","accessToken","refresh_token","refreshToken","expiry_date","expiresAt","token_type","originalGetMetadata","getRequestMetadataAsync","bind","url","needsRefresh","refreshedTokens","refreshAccessToken","error","Error","message","String","expiryDate","Date","now","tokenUrl","params","client_id","grant_type","client_secret","body","URLSearchParams","response","fetch","method","headers","toString","ok","errorText","text","status","tokenResponse","json","result","expires_in","undefined","scope","getUserEmail","cacheKey","cached","emailCache","get","email","auth","request","userInfo","data","set","authMiddleware","wrapAtPosition","module","extraPosition","originalHandler","handler","wrappedHandler","allArgs","extra","bearerToken","authInfo","token","requestInfo","authHeader","authorization","Authorization","headerValue","Array","isArray","match","exec","InvalidRequest","verifyResponse","verifyEndpoint","verifyData","accountId","providerTokens","InternalError","authContext","logger","withToolAuth","withResourceAuth","withPromptAuth","Map"],"mappings":"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"}
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { type OAuth2TokenStorageProvider } from '@mcp-z/oauth';
8
8
  import { OAuth2Client } from 'google-auth-library';
9
- import { type LoopbackOAuthConfig } from '../types.js';
9
+ import { type CachedToken, type LoopbackOAuthConfig } from '../types.js';
10
10
  /**
11
11
  * Loopback OAuth Client (RFC 8252 Section 7.3)
12
12
  *
@@ -33,14 +33,6 @@ export declare class LoopbackOAuthProvider implements OAuth2TokenStorageProvider
33
33
  * @returns OAuth2Client configured for the specified account
34
34
  */
35
35
  toAuth(accountId?: string): OAuth2Client;
36
- /**
37
- * Authenticate new account with OAuth flow
38
- * Triggers account selection, stores token, registers account
39
- *
40
- * @returns Email address of newly authenticated account
41
- * @throws Error in headless mode (cannot open browser for OAuth)
42
- */
43
- authenticateNewAccount(): Promise<string>;
44
36
  /**
45
37
  * Get user email from Google's userinfo endpoint (pure query)
46
38
  * Used to query email for existing authenticated account
@@ -49,14 +41,6 @@ export declare class LoopbackOAuthProvider implements OAuth2TokenStorageProvider
49
41
  * @returns User's email address
50
42
  */
51
43
  getUserEmail(accountId?: string): Promise<string>;
52
- /**
53
- * Check for existing accounts in token storage (incremental OAuth detection)
54
- *
55
- * Uses key-utils helper for forward compatibility with key format changes.
56
- *
57
- * @returns Array of account IDs that have tokens for this service
58
- */
59
- private getExistingAccounts;
60
44
  private isTokenValid;
61
45
  /**
62
46
  * Fetch user email from Google OAuth2 userinfo endpoint
@@ -69,6 +53,20 @@ export declare class LoopbackOAuthProvider implements OAuth2TokenStorageProvider
69
53
  private performEphemeralOAuthFlow;
70
54
  private exchangeCodeForToken;
71
55
  private refreshAccessToken;
56
+ /**
57
+ * Handle OAuth callback from persistent endpoint.
58
+ * Used by HTTP servers with configured redirectUri.
59
+ *
60
+ * @param params - OAuth callback parameters
61
+ * @returns Email and cached token
62
+ */
63
+ handleOAuthCallback(params: {
64
+ code: string;
65
+ state?: string;
66
+ }): Promise<{
67
+ email: string;
68
+ token: CachedToken;
69
+ }>;
72
70
  /**
73
71
  * Create authentication middleware for MCP tools, resources, and prompts
74
72
  *