@mcp-z/oauth-google 1.0.4 → 1.0.6
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.
- package/dist/cjs/lib/dcr-router.js +5 -3
- package/dist/cjs/lib/dcr-router.js.map +1 -1
- package/dist/cjs/lib/dcr-utils.js +7 -4
- package/dist/cjs/lib/dcr-utils.js.map +1 -1
- package/dist/esm/lib/dcr-router.js +4 -2
- package/dist/esm/lib/dcr-router.js.map +1 -1
- package/dist/esm/lib/dcr-utils.js +7 -4
- package/dist/esm/lib/dcr-utils.js.map +1 -1
- package/package.json +14 -15
|
@@ -442,7 +442,7 @@ function createDcrRouter(config) {
|
|
|
442
442
|
* Initiates Google OAuth flow, then generates DCR authorization code
|
|
443
443
|
*/ router.get('/oauth/authorize', function(req, res) {
|
|
444
444
|
return _async_to_generator(function() {
|
|
445
|
-
var _req_query, response_type, client_id, redirect_uri, _req_query_scope, scope, _req_query_state, state, code_challenge, code_challenge_method, client, isValidRedirect, googleState, dcrRequestState, googleAuthUrl;
|
|
445
|
+
var _req_query, response_type, client_id, redirect_uri, _req_query_scope, scope, _req_query_state, state, code_challenge, code_challenge_method, client, isValidRedirect, effectiveScope, googleState, dcrRequestState, googleAuthUrl;
|
|
446
446
|
return _ts_generator(this, function(_state) {
|
|
447
447
|
switch(_state.label){
|
|
448
448
|
case 0:
|
|
@@ -504,11 +504,13 @@ function createDcrRouter(config) {
|
|
|
504
504
|
})
|
|
505
505
|
];
|
|
506
506
|
}
|
|
507
|
+
// Use client-provided scope or fall back to server's supported scopes
|
|
508
|
+
effectiveScope = typeof scope === 'string' && scope.trim() !== '' ? scope : scopesSupported.join(' ');
|
|
507
509
|
googleState = (0, _crypto.randomUUID)();
|
|
508
510
|
dcrRequestState = {
|
|
509
511
|
client_id: client_id,
|
|
510
512
|
redirect_uri: redirect_uri,
|
|
511
|
-
scope:
|
|
513
|
+
scope: effectiveScope,
|
|
512
514
|
state: typeof state === 'string' ? state : undefined,
|
|
513
515
|
code_challenge: typeof code_challenge === 'string' ? code_challenge : undefined,
|
|
514
516
|
code_challenge_method: typeof code_challenge_method === 'string' ? code_challenge_method : undefined,
|
|
@@ -525,7 +527,7 @@ function createDcrRouter(config) {
|
|
|
525
527
|
googleAuthUrl.searchParams.set('client_id', clientConfig.clientId);
|
|
526
528
|
googleAuthUrl.searchParams.set('redirect_uri', "".concat(baseUrl, "/oauth/callback"));
|
|
527
529
|
googleAuthUrl.searchParams.set('response_type', 'code');
|
|
528
|
-
googleAuthUrl.searchParams.set('scope',
|
|
530
|
+
googleAuthUrl.searchParams.set('scope', effectiveScope);
|
|
529
531
|
googleAuthUrl.searchParams.set('state', googleState);
|
|
530
532
|
googleAuthUrl.searchParams.set('access_type', 'offline');
|
|
531
533
|
googleAuthUrl.searchParams.set('prompt', 'consent');
|
|
@@ -1 +1 @@
|
|
|
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('/mcp', (req: Request, res: Response, next) => {\n const authHeader = req.headers.authorization || req.headers.Authorization;\n const headerValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;\n\n if (!headerValue || !headerValue.toLowerCase().startsWith('bearer ')) {\n return res\n .status(401)\n .set('WWW-Authenticate', `Bearer resource_metadata=\"${baseUrl}/.well-known/oauth-protected-resource\"`)\n .json({\n jsonrpc: '2.0',\n error: {\n code: -32600,\n message: 'Missing Authorization header. DCR mode requires bearer token.',\n },\n id: null,\n });\n }\n\n return next();\n });\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":["createDcrRouter","config","router","express","Router","store","issuerUrl","baseUrl","scopesSupported","clientConfig","use","req","res","next","authHeader","headers","authorization","Authorization","headerValue","Array","isArray","toLowerCase","startsWith","status","set","json","jsonrpc","error","code","message","id","urlencoded","extended","get","_req","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","registrationRequest","client","body","dcrUtils","registerClient","error_description","Error","response_type","client_id","redirect_uri","scope","state","code_challenge","code_challenge_method","isValidRedirect","googleState","dcrRequestState","googleAuthUrl","query","getClient","validateRedirectUri","randomUUID","undefined","created_at","Date","now","expires_at","URL","searchParams","clientId","redirect","toString","googleCode","tokenUrl","tokenParamsObj","tokenParams","tokenResponse","errorText","tokenData","providerTokens","dcrCode","authCode","clientRedirectUrl","delete","grant_type","clientSecret","client_secret","URLSearchParams","fetch","method","ok","text","accessToken","access_token","refresh_token","refreshToken","expiresAt","expires_in","setAuthCode","base64Credentials","credentials","secret","code_verifier","isValidClient","computedChallenge","refreshTokenValue","refreshedProviderTokens","provider","newAccessToken","newTokenData","substring","Buffer","from","split","validateClient","getAuthCode","deleteAuthCode","createHash","update","digest","token_type","setAccessToken","setRefreshToken","setProviderTokens","getRefreshToken","DcrOAuthProvider","verifyEndpoint","logger","info","console","log","warn","debug","refreshAccessToken","String","token","token_type_hint","deleteRefreshToken","deleteAccessToken","deleteProviderTokens","send","authInfo","getAccessToken","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('/mcp', (req: Request, res: Response, next) => {\n const authHeader = req.headers.authorization || req.headers.Authorization;\n const headerValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;\n\n if (!headerValue || !headerValue.toLowerCase().startsWith('bearer ')) {\n return res\n .status(401)\n .set('WWW-Authenticate', `Bearer resource_metadata=\"${baseUrl}/.well-known/oauth-protected-resource\"`)\n .json({\n jsonrpc: '2.0',\n error: {\n code: -32600,\n message: 'Missing Authorization header. DCR mode requires bearer token.',\n },\n id: null,\n });\n }\n\n return next();\n });\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 // Use client-provided scope or fall back to server's supported scopes\n const effectiveScope = typeof scope === 'string' && scope.trim() !== '' ? scope : scopesSupported.join(' ');\n\n const googleState = randomUUID();\n const dcrRequestState = {\n client_id,\n redirect_uri,\n scope: effectiveScope,\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', effectiveScope);\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\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":["createDcrRouter","config","router","express","Router","store","issuerUrl","baseUrl","scopesSupported","clientConfig","use","req","res","next","authHeader","headers","authorization","Authorization","headerValue","Array","isArray","toLowerCase","startsWith","status","set","json","jsonrpc","error","code","message","id","urlencoded","extended","get","_req","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","registrationRequest","client","body","dcrUtils","registerClient","error_description","Error","response_type","client_id","redirect_uri","scope","state","code_challenge","code_challenge_method","isValidRedirect","effectiveScope","googleState","dcrRequestState","googleAuthUrl","query","getClient","validateRedirectUri","trim","join","randomUUID","undefined","created_at","Date","now","expires_at","URL","searchParams","clientId","redirect","toString","googleCode","tokenUrl","tokenParamsObj","tokenParams","tokenResponse","errorText","tokenData","providerTokens","dcrCode","authCode","clientRedirectUrl","delete","grant_type","clientSecret","client_secret","URLSearchParams","fetch","method","ok","text","accessToken","access_token","refresh_token","refreshToken","expiresAt","expires_in","setAuthCode","base64Credentials","credentials","secret","code_verifier","isValidClient","computedChallenge","refreshTokenValue","refreshedProviderTokens","provider","newAccessToken","newTokenData","substring","Buffer","from","split","validateClient","getAuthCode","deleteAuthCode","createHash","update","digest","token_type","setAccessToken","setRefreshToken","setProviderTokens","getRefreshToken","DcrOAuthProvider","verifyEndpoint","logger","info","console","log","warn","debug","refreshAccessToken","String","token","token_type_hint","deleteRefreshToken","deleteAccessToken","deleteProviderTokens","send","authInfo","getAccessToken","scopes","clients","listClients"],"mappings":"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"}
|
|
@@ -380,6 +380,9 @@ function _ts_generator(thisArg, body) {
|
|
|
380
380
|
};
|
|
381
381
|
}
|
|
382
382
|
}
|
|
383
|
+
var TEN_MINUTES_MS = 10 * 60 * 1000;
|
|
384
|
+
var ONE_HOUR_MS = 60 * 60 * 1000;
|
|
385
|
+
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
|
|
383
386
|
function registerClient(store, metadata) {
|
|
384
387
|
return _async_to_generator(function() {
|
|
385
388
|
var _metadata_grant_types, _metadata_response_types, _metadata_token_endpoint_auth_method, client_id, client_secret, grant_types, response_types, client, created_at, clientInfo;
|
|
@@ -648,7 +651,7 @@ function setProviderTokens(store, dcrToken, tokens) {
|
|
|
648
651
|
case 0:
|
|
649
652
|
return [
|
|
650
653
|
4,
|
|
651
|
-
store.set("dcr:provider:".concat(dcrToken), tokens)
|
|
654
|
+
store.set("dcr:provider:".concat(dcrToken), tokens, ONE_HOUR_MS)
|
|
652
655
|
];
|
|
653
656
|
case 1:
|
|
654
657
|
_state.sent();
|
|
@@ -702,7 +705,7 @@ function setAuthCode(store, code, authCode) {
|
|
|
702
705
|
case 0:
|
|
703
706
|
return [
|
|
704
707
|
4,
|
|
705
|
-
store.set("dcr:authcode:".concat(code), authCode)
|
|
708
|
+
store.set("dcr:authcode:".concat(code), authCode, TEN_MINUTES_MS)
|
|
706
709
|
];
|
|
707
710
|
case 1:
|
|
708
711
|
_state.sent();
|
|
@@ -756,7 +759,7 @@ function setAccessToken(store, token, tokenData) {
|
|
|
756
759
|
case 0:
|
|
757
760
|
return [
|
|
758
761
|
4,
|
|
759
|
-
store.set("dcr:access:".concat(token), tokenData)
|
|
762
|
+
store.set("dcr:access:".concat(token), tokenData, ONE_HOUR_MS)
|
|
760
763
|
];
|
|
761
764
|
case 1:
|
|
762
765
|
_state.sent();
|
|
@@ -810,7 +813,7 @@ function setRefreshToken(store, token, tokenData) {
|
|
|
810
813
|
case 0:
|
|
811
814
|
return [
|
|
812
815
|
4,
|
|
813
|
-
store.set("dcr:refresh:".concat(token), tokenData)
|
|
816
|
+
store.set("dcr:refresh:".concat(token), tokenData, THIRTY_DAYS_MS)
|
|
814
817
|
];
|
|
815
818
|
case 1:
|
|
816
819
|
_state.sent();
|
|
@@ -1 +1 @@
|
|
|
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":["deleteAccessToken","deleteAuthCode","deleteClient","deleteProviderTokens","deleteRefreshToken","getAccessToken","getAuthCode","getClient","getProviderTokens","getRefreshToken","listClients","registerClient","setAccessToken","setAuthCode","setProviderTokens","setRefreshToken","validateClient","validateRedirectUri","store","metadata","client_id","client_secret","grant_types","response_types","client","created_at","clientInfo","redirect_uris","length","Error","randomUUID","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","set","clientId","get","clientSecret","redirectUri","includes","clients","iterator","_key","value","push","delete","dcrToken","tokens","code","authCode","token","tokenData"],"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\nconst TEN_MINUTES_MS = 10 * 60 * 1000;\nconst ONE_HOUR_MS = 60 * 60 * 1000;\nconst THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;\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, ONE_HOUR_MS);\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, TEN_MINUTES_MS);\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, ONE_HOUR_MS);\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, THIRTY_DAYS_MS);\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":["deleteAccessToken","deleteAuthCode","deleteClient","deleteProviderTokens","deleteRefreshToken","getAccessToken","getAuthCode","getClient","getProviderTokens","getRefreshToken","listClients","registerClient","setAccessToken","setAuthCode","setProviderTokens","setRefreshToken","validateClient","validateRedirectUri","TEN_MINUTES_MS","ONE_HOUR_MS","THIRTY_DAYS_MS","store","metadata","client_id","client_secret","grant_types","response_types","client","created_at","clientInfo","redirect_uris","length","Error","randomUUID","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","set","clientId","get","clientSecret","redirectUri","includes","clients","iterator","_key","value","push","delete","dcrToken","tokens","code","authCode","token","tokenData"],"mappings":"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"}
|
|
@@ -163,11 +163,13 @@ import * as dcrUtils from './dcr-utils.js';
|
|
|
163
163
|
error_description: 'Invalid redirect_uri'
|
|
164
164
|
});
|
|
165
165
|
}
|
|
166
|
+
// Use client-provided scope or fall back to server's supported scopes
|
|
167
|
+
const effectiveScope = typeof scope === 'string' && scope.trim() !== '' ? scope : scopesSupported.join(' ');
|
|
166
168
|
const googleState = randomUUID();
|
|
167
169
|
const dcrRequestState = {
|
|
168
170
|
client_id,
|
|
169
171
|
redirect_uri,
|
|
170
|
-
scope:
|
|
172
|
+
scope: effectiveScope,
|
|
171
173
|
state: typeof state === 'string' ? state : undefined,
|
|
172
174
|
code_challenge: typeof code_challenge === 'string' ? code_challenge : undefined,
|
|
173
175
|
code_challenge_method: typeof code_challenge_method === 'string' ? code_challenge_method : undefined,
|
|
@@ -179,7 +181,7 @@ import * as dcrUtils from './dcr-utils.js';
|
|
|
179
181
|
googleAuthUrl.searchParams.set('client_id', clientConfig.clientId);
|
|
180
182
|
googleAuthUrl.searchParams.set('redirect_uri', `${baseUrl}/oauth/callback`);
|
|
181
183
|
googleAuthUrl.searchParams.set('response_type', 'code');
|
|
182
|
-
googleAuthUrl.searchParams.set('scope',
|
|
184
|
+
googleAuthUrl.searchParams.set('scope', effectiveScope);
|
|
183
185
|
googleAuthUrl.searchParams.set('state', googleState);
|
|
184
186
|
googleAuthUrl.searchParams.set('access_type', 'offline');
|
|
185
187
|
googleAuthUrl.searchParams.set('prompt', 'consent');
|
|
@@ -1 +1 @@
|
|
|
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('/mcp', (req: Request, res: Response, next) => {\n const authHeader = req.headers.authorization || req.headers.Authorization;\n const headerValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;\n\n if (!headerValue || !headerValue.toLowerCase().startsWith('bearer ')) {\n return res\n .status(401)\n .set('WWW-Authenticate', `Bearer resource_metadata=\"${baseUrl}/.well-known/oauth-protected-resource\"`)\n .json({\n jsonrpc: '2.0',\n error: {\n code: -32600,\n message: 'Missing Authorization header. DCR mode requires bearer token.',\n },\n id: null,\n });\n }\n\n return next();\n });\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","req","res","next","authHeader","headers","authorization","Authorization","headerValue","Array","isArray","toLowerCase","startsWith","status","set","json","jsonrpc","error","code","message","id","urlencoded","extended","get","_req","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","registrationRequest","body","client","registerClient","error_description","Error","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","googleAuthUrl","URL","searchParams","clientId","redirect","toString","googleCode","delete","tokenUrl","tokenParamsObj","grant_type","clientSecret","client_secret","tokenParams","URLSearchParams","tokenResponse","fetch","method","ok","errorText","text","tokenData","providerTokens","accessToken","access_token","refresh_token","refreshToken","expiresAt","expires_in","dcrCode","authCode","setAuthCode","clientRedirectUrl","base64Credentials","substring","credentials","Buffer","from","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('/mcp', (req: Request, res: Response, next) => {\n const authHeader = req.headers.authorization || req.headers.Authorization;\n const headerValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;\n\n if (!headerValue || !headerValue.toLowerCase().startsWith('bearer ')) {\n return res\n .status(401)\n .set('WWW-Authenticate', `Bearer resource_metadata=\"${baseUrl}/.well-known/oauth-protected-resource\"`)\n .json({\n jsonrpc: '2.0',\n error: {\n code: -32600,\n message: 'Missing Authorization header. DCR mode requires bearer token.',\n },\n id: null,\n });\n }\n\n return next();\n });\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 // Use client-provided scope or fall back to server's supported scopes\n const effectiveScope = typeof scope === 'string' && scope.trim() !== '' ? scope : scopesSupported.join(' ');\n\n const googleState = randomUUID();\n const dcrRequestState = {\n client_id,\n redirect_uri,\n scope: effectiveScope,\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', effectiveScope);\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\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","req","res","next","authHeader","headers","authorization","Authorization","headerValue","Array","isArray","toLowerCase","startsWith","status","set","json","jsonrpc","error","code","message","id","urlencoded","extended","get","_req","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","registrationRequest","body","client","registerClient","error_description","Error","response_type","client_id","redirect_uri","scope","state","code_challenge","code_challenge_method","query","getClient","isValidRedirect","validateRedirectUri","effectiveScope","trim","join","googleState","dcrRequestState","undefined","created_at","Date","now","expires_at","googleAuthUrl","URL","searchParams","clientId","redirect","toString","googleCode","delete","tokenUrl","tokenParamsObj","grant_type","clientSecret","client_secret","tokenParams","URLSearchParams","tokenResponse","fetch","method","ok","errorText","text","tokenData","providerTokens","accessToken","access_token","refresh_token","refreshToken","expiresAt","expires_in","dcrCode","authCode","setAuthCode","clientRedirectUrl","base64Credentials","substring","credentials","Buffer","from","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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"}
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
* - dcr:access:{token} -> AccessToken
|
|
12
12
|
* - dcr:refresh:{token} -> AccessToken
|
|
13
13
|
*/ import { randomUUID } from 'crypto';
|
|
14
|
+
const TEN_MINUTES_MS = 10 * 60 * 1000;
|
|
15
|
+
const ONE_HOUR_MS = 60 * 60 * 1000;
|
|
16
|
+
const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
|
|
14
17
|
// ============================================================================
|
|
15
18
|
// Client Operations
|
|
16
19
|
// ============================================================================
|
|
@@ -162,7 +165,7 @@
|
|
|
162
165
|
* @param dcrToken - DCR-issued access token (used as key)
|
|
163
166
|
* @param tokens - Google provider tokens (access, refresh, expiry)
|
|
164
167
|
*/ export async function setProviderTokens(store, dcrToken, tokens) {
|
|
165
|
-
await store.set(`dcr:provider:${dcrToken}`, tokens);
|
|
168
|
+
await store.set(`dcr:provider:${dcrToken}`, tokens, ONE_HOUR_MS);
|
|
166
169
|
}
|
|
167
170
|
/**
|
|
168
171
|
* Retrieve provider tokens for a DCR access token
|
|
@@ -191,7 +194,7 @@
|
|
|
191
194
|
* @param code - Authorization code
|
|
192
195
|
* @param authCode - Authorization code data
|
|
193
196
|
*/ export async function setAuthCode(store, code, authCode) {
|
|
194
|
-
await store.set(`dcr:authcode:${code}`, authCode);
|
|
197
|
+
await store.set(`dcr:authcode:${code}`, authCode, TEN_MINUTES_MS);
|
|
195
198
|
}
|
|
196
199
|
/**
|
|
197
200
|
* Get an authorization code
|
|
@@ -220,7 +223,7 @@
|
|
|
220
223
|
* @param token - Access token
|
|
221
224
|
* @param tokenData - Access token data
|
|
222
225
|
*/ export async function setAccessToken(store, token, tokenData) {
|
|
223
|
-
await store.set(`dcr:access:${token}`, tokenData);
|
|
226
|
+
await store.set(`dcr:access:${token}`, tokenData, ONE_HOUR_MS);
|
|
224
227
|
}
|
|
225
228
|
/**
|
|
226
229
|
* Get an access token
|
|
@@ -249,7 +252,7 @@
|
|
|
249
252
|
* @param token - Refresh token
|
|
250
253
|
* @param tokenData - Access token data (contains refresh token context)
|
|
251
254
|
*/ export async function setRefreshToken(store, token, tokenData) {
|
|
252
|
-
await store.set(`dcr:refresh:${token}`, tokenData);
|
|
255
|
+
await store.set(`dcr:refresh:${token}`, tokenData, THIRTY_DAYS_MS);
|
|
253
256
|
}
|
|
254
257
|
/**
|
|
255
258
|
* Get a refresh token
|
|
@@ -1 +1 @@
|
|
|
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
|
+
{"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\nconst TEN_MINUTES_MS = 10 * 60 * 1000;\nconst ONE_HOUR_MS = 60 * 60 * 1000;\nconst THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;\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, ONE_HOUR_MS);\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, TEN_MINUTES_MS);\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, ONE_HOUR_MS);\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, THIRTY_DAYS_MS);\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","TEN_MINUTES_MS","ONE_HOUR_MS","THIRTY_DAYS_MS","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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-z/oauth-google",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "OAuth 2.0 client for Google APIs with multi-account support, PKCE security, and swappable storage backends",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"oauth2",
|
|
@@ -52,33 +52,32 @@
|
|
|
52
52
|
"version": "tsds version"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@mcp-z/oauth": "^1.0.
|
|
56
|
-
"express": "^5.
|
|
57
|
-
"google-auth-library": "^10.
|
|
58
|
-
"jose": "^6.
|
|
55
|
+
"@mcp-z/oauth": "^1.0.0",
|
|
56
|
+
"express": "^5.0.0",
|
|
57
|
+
"google-auth-library": "^10.0.0",
|
|
58
|
+
"jose": "^6.0.0",
|
|
59
59
|
"open": "^11.0.0",
|
|
60
|
-
"zod": "^4.
|
|
60
|
+
"zod": "^4.0.0"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@mcp-z/client": "^1.0.
|
|
64
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
63
|
+
"@mcp-z/client": "^1.0.5",
|
|
64
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
65
65
|
"@types/cors": "^2.8.19",
|
|
66
66
|
"@types/express": "^5.0.6",
|
|
67
67
|
"@types/mocha": "^10.0.10",
|
|
68
68
|
"@types/node": "^25.0.3",
|
|
69
|
-
"cors": "^2.
|
|
69
|
+
"cors": "^2.0.0",
|
|
70
70
|
"dotenv": "^17.2.3",
|
|
71
71
|
"get-port": "^7.1.0",
|
|
72
|
-
"googleapis": "^
|
|
73
|
-
"keyv": "^5.
|
|
74
|
-
"keyv-file": "^5.
|
|
72
|
+
"googleapis": "^170.0.0",
|
|
73
|
+
"keyv": "^5.0.0",
|
|
74
|
+
"keyv-file": "^5.0.0",
|
|
75
75
|
"node-version-use": "^2.4.7",
|
|
76
76
|
"ts-dev-stack": "^1.22.1",
|
|
77
|
-
"tsds-config": "^1.0.4"
|
|
78
|
-
"typescript": "^5.9.3"
|
|
77
|
+
"tsds-config": "^1.0.4"
|
|
79
78
|
},
|
|
80
79
|
"peerDependencies": {
|
|
81
|
-
"keyv": "^5.
|
|
80
|
+
"keyv": "^5.0.0"
|
|
82
81
|
},
|
|
83
82
|
"engines": {
|
|
84
83
|
"node": ">=24"
|