@mcp-z/oauth-microsoft 1.0.0 → 1.0.2
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/README.md +8 -0
- package/dist/cjs/index.d.cts +2 -1
- package/dist/cjs/index.d.ts +2 -1
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lib/dcr-router.js.map +1 -1
- package/dist/cjs/lib/dcr-utils.js.map +1 -1
- package/dist/cjs/lib/dcr-verify.js.map +1 -1
- package/dist/cjs/lib/fetch-with-timeout.js.map +1 -1
- package/dist/cjs/lib/loopback-router.d.cts +8 -0
- package/dist/cjs/lib/loopback-router.d.ts +8 -0
- package/dist/cjs/lib/loopback-router.js +219 -0
- package/dist/cjs/lib/loopback-router.js.map +1 -0
- package/dist/cjs/lib/token-verifier.js.map +1 -1
- package/dist/cjs/providers/dcr.js.map +1 -1
- package/dist/cjs/providers/device-code.js.map +1 -1
- package/dist/cjs/providers/loopback-oauth.d.cts +93 -18
- package/dist/cjs/providers/loopback-oauth.d.ts +93 -18
- package/dist/cjs/providers/loopback-oauth.js +877 -491
- package/dist/cjs/providers/loopback-oauth.js.map +1 -1
- package/dist/cjs/schemas/index.js +1 -1
- package/dist/cjs/schemas/index.js.map +1 -1
- package/dist/cjs/setup/config.d.cts +4 -1
- package/dist/cjs/setup/config.d.ts +4 -1
- package/dist/cjs/setup/config.js +7 -4
- package/dist/cjs/setup/config.js.map +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/dcr-router.js.map +1 -1
- package/dist/esm/lib/dcr-utils.js.map +1 -1
- package/dist/esm/lib/dcr-verify.js.map +1 -1
- package/dist/esm/lib/fetch-with-timeout.js.map +1 -1
- package/dist/esm/lib/loopback-router.d.ts +8 -0
- package/dist/esm/lib/loopback-router.js +32 -0
- package/dist/esm/lib/loopback-router.js.map +1 -0
- package/dist/esm/lib/token-verifier.js.map +1 -1
- package/dist/esm/providers/dcr.js.map +1 -1
- package/dist/esm/providers/device-code.js +2 -2
- package/dist/esm/providers/device-code.js.map +1 -1
- package/dist/esm/providers/loopback-oauth.d.ts +93 -18
- package/dist/esm/providers/loopback-oauth.js +470 -289
- package/dist/esm/providers/loopback-oauth.js.map +1 -1
- package/dist/esm/schemas/index.js +1 -1
- package/dist/esm/schemas/index.js.map +1 -1
- package/dist/esm/setup/config.d.ts +4 -1
- package/dist/esm/setup/config.js +7 -4
- package/dist/esm/setup/config.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,9 +23,17 @@ npm install @mcp-z/oauth-microsoft keyv
|
|
|
23
23
|
3. Click New registration.
|
|
24
24
|
4. Choose a name and select a supported account type.
|
|
25
25
|
5. Copy the Application (client) ID and Directory (tenant) ID.
|
|
26
|
+
6. Select your MCP transport (stdio for local and http for remote) and platform
|
|
27
|
+
- For stdio, choose "Authentication", + Add Redirect URI, "Mobile and desktop applications" platform
|
|
28
|
+
- For http, choose "Authentication", + Add Redirect URI, "Web" platform, add your URL (default is http://localhost:3000/oauth/callback based on the --port or PORT)
|
|
29
|
+
- For local hosting, add "http://localhost" for [Ephemeral redirect URL](https://en.wikipedia.org/wiki/Ephemeral_port)
|
|
26
30
|
|
|
27
31
|
## OAuth modes
|
|
28
32
|
|
|
33
|
+
### Redirect URI modes (loopback)
|
|
34
|
+
- No REDIRECT_URI: ephemeral loopback (random port), works for stdio and http.
|
|
35
|
+
- REDIRECT_URI set: persistent callback /oauth/callback (HTTP only).
|
|
36
|
+
|
|
29
37
|
### Loopback OAuth (interactive)
|
|
30
38
|
|
|
31
39
|
```ts
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { createDcrRouter, type DcrRouterConfig } from './lib/dcr-router.js';
|
|
9
9
|
export { type VerificationResult, verifyBearerToken } from './lib/dcr-verify.js';
|
|
10
|
+
export { createLoopbackCallbackRouter } from './lib/loopback-router.js';
|
|
10
11
|
export { type AuthInfo, DcrTokenVerifier } from './lib/token-verifier.js';
|
|
11
12
|
export { DcrOAuthProvider, type DcrOAuthProviderConfig } from './providers/dcr.js';
|
|
12
13
|
export { type DeviceCodeConfig, DeviceCodeProvider } from './providers/device-code.js';
|
|
13
14
|
export { LoopbackOAuthProvider } from './providers/loopback-oauth.js';
|
|
14
15
|
export * as schemas from './schemas/index.js';
|
|
15
|
-
export { createConfig, parseConfig, parseDcrConfig } from './setup/config.js';
|
|
16
|
+
export { createConfig, parseConfig, parseDcrConfig, type TransportType } from './setup/config.js';
|
|
16
17
|
export * from './types.js';
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { createDcrRouter, type DcrRouterConfig } from './lib/dcr-router.js';
|
|
9
9
|
export { type VerificationResult, verifyBearerToken } from './lib/dcr-verify.js';
|
|
10
|
+
export { createLoopbackCallbackRouter } from './lib/loopback-router.js';
|
|
10
11
|
export { type AuthInfo, DcrTokenVerifier } from './lib/token-verifier.js';
|
|
11
12
|
export { DcrOAuthProvider, type DcrOAuthProviderConfig } from './providers/dcr.js';
|
|
12
13
|
export { type DeviceCodeConfig, DeviceCodeProvider } from './providers/device-code.js';
|
|
13
14
|
export { LoopbackOAuthProvider } from './providers/loopback-oauth.js';
|
|
14
15
|
export * as schemas from './schemas/index.js';
|
|
15
|
-
export { createConfig, parseConfig, parseDcrConfig } from './setup/config.js';
|
|
16
|
+
export { createConfig, parseConfig, parseDcrConfig, type TransportType } from './setup/config.js';
|
|
16
17
|
export * from './types.js';
|
package/dist/cjs/index.js
CHANGED
|
@@ -33,6 +33,9 @@ _export(exports, {
|
|
|
33
33
|
get createDcrRouter () {
|
|
34
34
|
return _dcrrouterts.createDcrRouter;
|
|
35
35
|
},
|
|
36
|
+
get createLoopbackCallbackRouter () {
|
|
37
|
+
return _loopbackrouterts.createLoopbackCallbackRouter;
|
|
38
|
+
},
|
|
36
39
|
get parseConfig () {
|
|
37
40
|
return _configts.parseConfig;
|
|
38
41
|
},
|
|
@@ -48,6 +51,7 @@ _export(exports, {
|
|
|
48
51
|
});
|
|
49
52
|
var _dcrrouterts = require("./lib/dcr-router.js");
|
|
50
53
|
var _dcrverifyts = require("./lib/dcr-verify.js");
|
|
54
|
+
var _loopbackrouterts = require("./lib/loopback-router.js");
|
|
51
55
|
var _tokenverifierts = require("./lib/token-verifier.js");
|
|
52
56
|
var _dcrts = require("./providers/dcr.js");
|
|
53
57
|
var _devicecodets = require("./providers/device-code.js");
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-microsoft/src/index.ts"],"sourcesContent":["/**\n * @mcp-z/oauth-microsoft - Shared Microsoft OAuth implementation\n *\n * Provides OAuth authentication:\n * - Loopback OAuth (RFC 8252) - Server-managed, file-based tokens\n * - Device Code flow (RFC 8628) - For headless/limited-input scenarios\n */\n\nexport { createDcrRouter, type DcrRouterConfig } from './lib/dcr-router.ts';\nexport { type VerificationResult, verifyBearerToken } from './lib/dcr-verify.ts';\nexport { createLoopbackCallbackRouter } from './lib/loopback-router.ts';\nexport { type AuthInfo, DcrTokenVerifier } from './lib/token-verifier.ts';\nexport { DcrOAuthProvider, type DcrOAuthProviderConfig } from './providers/dcr.ts';\nexport { type DeviceCodeConfig, DeviceCodeProvider } from './providers/device-code.ts';\nexport { LoopbackOAuthProvider } from './providers/loopback-oauth.ts';\nexport * as schemas from './schemas/index.ts';\nexport { createConfig, parseConfig, parseDcrConfig, type TransportType } from './setup/config.ts';\nexport * from './types.ts';\n"],"names":["DcrOAuthProvider","DcrTokenVerifier","DeviceCodeProvider","LoopbackOAuthProvider","createConfig","createDcrRouter","createLoopbackCallbackRouter","parseConfig","parseDcrConfig","schemas","verifyBearerToken"],"mappings":"AAAA;;;;;;CAMC;;;;;;;;;;;QAMQA;eAAAA,uBAAgB;;QADDC;eAAAA,iCAAgB;;QAERC;eAAAA,gCAAkB;;QACzCC;eAAAA,sCAAqB;;QAErBC;eAAAA,sBAAY;;QARZC;eAAAA,4BAAe;;QAEfC;eAAAA,8CAA4B;;QAMdC;eAAAA,qBAAW;;QAAEC;eAAAA,wBAAc;;QADtCC;;;QANsBC;eAAAA,8BAAiB;;;2BADG;2BACK;gCACd;+BACG;qBACc;4BACJ;+BACpB;+DACb;wBACqD;qBAChE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-microsoft/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 // Apply required middleware for OAuth 2.0 endpoints (RFC 6749)\n router.use(express.json()); // For /oauth/register (application/json)\n router.use(express.urlencoded({ extended: true })); // For /oauth/token (application/x-www-form-urlencoded)\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\n // Register the client\n const client = await dcrUtils.registerClient(store, registrationRequest);\n\n // Return client information (RFC 7591 Section 3.2.1)\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 Microsoft 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 // Validate required parameters\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 // Validate client\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 // Validate redirect_uri\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 // Store DCR request state for Microsoft OAuth callback\n const msState = 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:ms-state:${msState}`, dcrRequestState, 600000); // 10 min TTL\n\n // Build Microsoft authorization URL\n const msAuthUrl = new URL(`https://login.microsoftonline.com/${clientConfig.tenantId || 'common'}/oauth2/v2.0/authorize`);\n msAuthUrl.searchParams.set('client_id', clientConfig.clientId);\n msAuthUrl.searchParams.set('response_type', 'code');\n msAuthUrl.searchParams.set('redirect_uri', `${baseUrl}/oauth/callback`);\n msAuthUrl.searchParams.set('scope', typeof scope === 'string' ? scope : '');\n msAuthUrl.searchParams.set('state', msState);\n msAuthUrl.searchParams.set('response_mode', 'query');\n\n // Redirect user to Microsoft for authorization\n return res.redirect(msAuthUrl.toString());\n });\n\n /**\n * OAuth Callback Handler\n * GET /oauth/callback\n *\n * Handles callback from Microsoft after user authorization\n */\n router.get('/oauth/callback', async (req: Request, res: Response) => {\n const { code: msCode, state: msState, error, error_description } = req.query;\n\n // Handle Microsoft OAuth errors\n if (error) {\n return res.status(400).json({\n error,\n error_description: error_description || 'Microsoft OAuth authorization failed',\n });\n }\n\n if (!msCode || typeof msCode !== 'string') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Authorization code is required',\n });\n }\n\n if (!msState || typeof msState !== 'string') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'State parameter is required',\n });\n }\n\n // Retrieve original DCR request state\n const dcrRequestState = await store.get(`dcr:ms-state:${msState}`);\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 // Delete state (one-time use)\n await store.delete(`dcr:ms-state:${msState}`);\n\n // Exchange Microsoft authorization code for tokens\n try {\n const tokenUrl = `https://login.microsoftonline.com/${clientConfig.tenantId || 'common'}/oauth2/v2.0/token`;\n const tokenParams = new URLSearchParams({\n grant_type: 'authorization_code',\n code: msCode,\n client_id: clientConfig.clientId,\n redirect_uri: `${baseUrl}/oauth/callback`,\n scope: dcrRequestState.scope,\n });\n\n // Add client_secret if available (confidential client)\n if (clientConfig.clientSecret) {\n tokenParams.set('client_secret', clientConfig.clientSecret);\n }\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 errorData = (await tokenResponse.json()) as { error?: string; error_description?: string };\n throw new Error(`Microsoft token exchange failed: ${errorData.error_description || errorData.error}`);\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 Microsoft 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 // Generate DCR authorization code with real provider tokens\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 // Redirect back to MCP client with DCR authorization code\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 } catch (error) {\n return res.status(500).json({\n error: 'server_error',\n error_description: error instanceof Error ? error.message : 'Failed to exchange authorization code',\n });\n }\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 // Extract client credentials from either body or Basic Auth header\n let client_id = req.body.client_id;\n let client_secret = req.body.client_secret;\n\n // Support client_secret_basic authentication (RFC 6749 Section 2.3.1)\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 // Validate grant_type\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 // Authorization Code Grant\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 // Get authorization code\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 // Validate authorization code\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 // Validate PKCE if used\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 // Validate code_verifier against code_challenge\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 // Delete authorization code (one-time use)\n await dcrUtils.deleteAuthCode(store, code);\n\n // Generate DCR access token\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\n // Store provider tokens indexed by DCR access token\n await dcrUtils.setProviderTokens(store, accessToken, authCode.providerTokens);\n\n // Return token response\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 // Refresh Token Grant\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 // 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 // Get refresh token\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 // Refresh provider tokens if available\n let refreshedProviderTokens = tokenData.providerTokens;\n if (tokenData.providerTokens.refreshToken) {\n try {\n // Create DcrOAuthProvider instance to refresh Microsoft tokens\n const provider = new DcrOAuthProvider({\n clientId: clientConfig.clientId,\n ...(clientConfig.clientSecret && { clientSecret: clientConfig.clientSecret }),\n tenantId: clientConfig.tenantId ?? 'common',\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 Microsoft 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 // Generate new DCR access token\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\n // Store refreshed provider tokens indexed by new DCR access token\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 // Validate client if credentials provided\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 // Revoke the token\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 // RFC 7009: Return 200 even if token not found\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 // Extract bearer token from Authorization header\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); // Remove 'Bearer ' prefix\n\n // Validate token exists in access tokens store\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 // Check if token is expired\n const now = Date.now();\n const expiresAt = tokenData.created_at + tokenData.expires_in * 1000;\n\n if (now > expiresAt) {\n // Remove expired token\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 // Return AuthInfo with provider tokens for stateless DCR\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 res.json(clients);\n });\n\n return router;\n}\n"],"names":["createDcrRouter","config","router","express","Router","store","issuerUrl","baseUrl","scopesSupported","clientConfig","use","json","urlencoded","extended","get","_req","res","metadata","issuer","authorization_endpoint","token_endpoint","registration_endpoint","revocation_endpoint","scopes_supported","response_types_supported","grant_types_supported","token_endpoint_auth_methods_supported","code_challenge_methods_supported","service_documentation","resource","authorization_servers","bearer_methods_supported","post","req","registrationRequest","client","error","body","dcrUtils","registerClient","status","error_description","Error","message","response_type","client_id","redirect_uri","scope","state","code_challenge","code_challenge_method","isValidRedirect","msState","dcrRequestState","msAuthUrl","query","getClient","validateRedirectUri","randomUUID","undefined","created_at","Date","now","expires_at","set","URL","tenantId","searchParams","clientId","redirect","toString","msCode","tokenUrl","tokenParams","tokenResponse","errorData","tokenData","providerTokens","dcrCode","authCode","clientRedirectUrl","code","delete","URLSearchParams","grant_type","clientSecret","fetch","method","headers","ok","accessToken","access_token","refresh_token","refreshToken","expiresAt","expires_in","setAuthCode","client_secret","authHeader","base64Credentials","credentials","id","secret","code_verifier","isValidClient","computedChallenge","refreshTokenValue","refreshedProviderTokens","provider","newAccessToken","newTokenData","authorization","startsWith","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-microsoft/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 // Apply required middleware for OAuth 2.0 endpoints (RFC 6749)\n router.use(express.json()); // For /oauth/register (application/json)\n router.use(express.urlencoded({ extended: true })); // For /oauth/token (application/x-www-form-urlencoded)\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\n // Register the client\n const client = await dcrUtils.registerClient(store, registrationRequest);\n\n // Return client information (RFC 7591 Section 3.2.1)\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 Microsoft 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 // Validate required parameters\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 // Validate client\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 // Validate redirect_uri\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 // Store DCR request state for Microsoft OAuth callback\n const msState = 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:ms-state:${msState}`, dcrRequestState, 600000); // 10 min TTL\n\n // Build Microsoft authorization URL\n const msAuthUrl = new URL(`https://login.microsoftonline.com/${clientConfig.tenantId || 'common'}/oauth2/v2.0/authorize`);\n msAuthUrl.searchParams.set('client_id', clientConfig.clientId);\n msAuthUrl.searchParams.set('response_type', 'code');\n msAuthUrl.searchParams.set('redirect_uri', `${baseUrl}/oauth/callback`);\n msAuthUrl.searchParams.set('scope', typeof scope === 'string' ? scope : '');\n msAuthUrl.searchParams.set('state', msState);\n msAuthUrl.searchParams.set('response_mode', 'query');\n\n // Redirect user to Microsoft for authorization\n return res.redirect(msAuthUrl.toString());\n });\n\n /**\n * OAuth Callback Handler\n * GET /oauth/callback\n *\n * Handles callback from Microsoft after user authorization\n */\n router.get('/oauth/callback', async (req: Request, res: Response) => {\n const { code: msCode, state: msState, error, error_description } = req.query;\n\n // Handle Microsoft OAuth errors\n if (error) {\n return res.status(400).json({\n error,\n error_description: error_description || 'Microsoft OAuth authorization failed',\n });\n }\n\n if (!msCode || typeof msCode !== 'string') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'Authorization code is required',\n });\n }\n\n if (!msState || typeof msState !== 'string') {\n return res.status(400).json({\n error: 'invalid_request',\n error_description: 'State parameter is required',\n });\n }\n\n // Retrieve original DCR request state\n const dcrRequestState = await store.get(`dcr:ms-state:${msState}`);\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 // Delete state (one-time use)\n await store.delete(`dcr:ms-state:${msState}`);\n\n // Exchange Microsoft authorization code for tokens\n try {\n const tokenUrl = `https://login.microsoftonline.com/${clientConfig.tenantId || 'common'}/oauth2/v2.0/token`;\n const tokenParams = new URLSearchParams({\n grant_type: 'authorization_code',\n code: msCode,\n client_id: clientConfig.clientId,\n redirect_uri: `${baseUrl}/oauth/callback`,\n scope: dcrRequestState.scope,\n });\n\n // Add client_secret if available (confidential client)\n if (clientConfig.clientSecret) {\n tokenParams.set('client_secret', clientConfig.clientSecret);\n }\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 errorData = (await tokenResponse.json()) as { error?: string; error_description?: string };\n throw new Error(`Microsoft token exchange failed: ${errorData.error_description || errorData.error}`);\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 Microsoft 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 // Generate DCR authorization code with real provider tokens\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 // Redirect back to MCP client with DCR authorization code\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 } catch (error) {\n return res.status(500).json({\n error: 'server_error',\n error_description: error instanceof Error ? error.message : 'Failed to exchange authorization code',\n });\n }\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 // Extract client credentials from either body or Basic Auth header\n let client_id = req.body.client_id;\n let client_secret = req.body.client_secret;\n\n // Support client_secret_basic authentication (RFC 6749 Section 2.3.1)\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 // Validate grant_type\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 // Authorization Code Grant\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 // Get authorization code\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 // Validate authorization code\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 // Validate PKCE if used\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 // Validate code_verifier against code_challenge\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 // Delete authorization code (one-time use)\n await dcrUtils.deleteAuthCode(store, code);\n\n // Generate DCR access token\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\n // Store provider tokens indexed by DCR access token\n await dcrUtils.setProviderTokens(store, accessToken, authCode.providerTokens);\n\n // Return token response\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 // Refresh Token Grant\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 // 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 // Get refresh token\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 // Refresh provider tokens if available\n let refreshedProviderTokens = tokenData.providerTokens;\n if (tokenData.providerTokens.refreshToken) {\n try {\n // Create DcrOAuthProvider instance to refresh Microsoft tokens\n const provider = new DcrOAuthProvider({\n clientId: clientConfig.clientId,\n ...(clientConfig.clientSecret && { clientSecret: clientConfig.clientSecret }),\n tenantId: clientConfig.tenantId ?? 'common',\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 Microsoft 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 // Generate new DCR access token\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\n // Store refreshed provider tokens indexed by new DCR access token\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 // Validate client if credentials provided\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 // Revoke the token\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 // RFC 7009: Return 200 even if token not found\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 // Extract bearer token from Authorization header\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); // Remove 'Bearer ' prefix\n\n // Validate token exists in access tokens store\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 // Check if token is expired\n const now = Date.now();\n const expiresAt = tokenData.created_at + tokenData.expires_in * 1000;\n\n if (now > expiresAt) {\n // Remove expired token\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 // Return AuthInfo with provider tokens for stateless DCR\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 res.json(clients);\n });\n\n return router;\n}\n"],"names":["createDcrRouter","config","router","express","Router","store","issuerUrl","baseUrl","scopesSupported","clientConfig","use","json","urlencoded","extended","get","_req","res","metadata","issuer","authorization_endpoint","token_endpoint","registration_endpoint","revocation_endpoint","scopes_supported","response_types_supported","grant_types_supported","token_endpoint_auth_methods_supported","code_challenge_methods_supported","service_documentation","resource","authorization_servers","bearer_methods_supported","post","req","registrationRequest","client","error","body","dcrUtils","registerClient","status","error_description","Error","message","response_type","client_id","redirect_uri","scope","state","code_challenge","code_challenge_method","isValidRedirect","msState","dcrRequestState","msAuthUrl","query","getClient","validateRedirectUri","randomUUID","undefined","created_at","Date","now","expires_at","set","URL","tenantId","searchParams","clientId","redirect","toString","msCode","tokenUrl","tokenParams","tokenResponse","errorData","tokenData","providerTokens","dcrCode","authCode","clientRedirectUrl","code","delete","URLSearchParams","grant_type","clientSecret","fetch","method","headers","ok","accessToken","access_token","refresh_token","refreshToken","expiresAt","expires_in","setAuthCode","client_secret","authHeader","base64Credentials","credentials","id","secret","code_verifier","isValidClient","computedChallenge","refreshTokenValue","refreshedProviderTokens","provider","newAccessToken","newTokenData","authorization","startsWith","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 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-microsoft/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 - Microsoft 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-microsoft/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 - Microsoft 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 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-microsoft/src/lib/dcr-verify.ts"],"sourcesContent":["/**\n * DCR Token Verification Utilities\n *\n * Provides token verification for both self-hosted and external DCR modes:\n * - Self-hosted: Verifies tokens against local DCR router (/oauth/verify)\n * - External: Verifies tokens against Auth0/Stitch verification endpoint\n */\n\nimport type { ProviderTokens } from '@mcp-z/oauth';\nimport { fetchWithTimeout } from './fetch-with-timeout.ts';\n\n/**\n * Verification result from DCR authorization server\n */\nexport interface VerificationResult {\n /** Bearer token that was verified */\n token: string;\n /** Client ID associated with the token */\n clientId: string;\n /** OAuth scopes granted to the token */\n scopes: string[];\n /** Token expiration timestamp (milliseconds since epoch) */\n expiresAt: number;\n /** Provider tokens (Microsoft access/refresh tokens) */\n providerTokens: ProviderTokens;\n}\n\n/**\n * Verify bearer token against DCR authorization server\n *\n * Supports both self-hosted and external DCR modes by calling the\n * /oauth/verify endpoint (or equivalent external URL).\n *\n * @param bearerToken - Bearer token to verify (without \"Bearer \" prefix)\n * @param verifyUrl - Verification endpoint URL (self-hosted or external)\n * @returns Verification result with provider tokens\n * @throws Error if verification fails\n *\n * @example Self-hosted mode\n * ```typescript\n * const result = await verifyBearerToken(\n * token,\n * 'http://localhost:3456/oauth/verify'\n * );\n * const auth = provider.toAuthProvider(result.providerTokens);\n * ```\n *\n * @example External mode (Auth0/Stitch)\n * ```typescript\n * const result = await verifyBearerToken(\n * token,\n * 'https://auth.example.com/oauth/verify'\n * );\n * const auth = provider.toAuthProvider(result.providerTokens);\n * ```\n */\nexport async function verifyBearerToken(bearerToken: string, verifyUrl: string): Promise<VerificationResult> {\n const response = await fetchWithTimeout(verifyUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token verification failed: ${response.status} ${errorText}`);\n }\n\n const result = (await response.json()) as VerificationResult;\n\n // Validate required fields\n if (!result.providerTokens || !result.providerTokens.accessToken) {\n throw new Error('Verification response missing required provider tokens');\n }\n\n return result;\n}\n"],"names":["verifyBearerToken","bearerToken","verifyUrl","response","errorText","result","fetchWithTimeout","method","headers","Authorization","ok","text","Error","status","json","providerTokens","accessToken"],"mappings":"AAAA;;;;;;CAMC;;;;+BAkDqBA;;;eAAAA;;;kCA/CW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+C1B,SAAeA,kBAAkBC,WAAmB,EAAEC,SAAiB;;YACtEC,UAQEC,WAIFC;;;;oBAZW;;wBAAMC,IAAAA,oCAAgB,EAACJ,WAAW;4BACjDK,QAAQ;4BACRC,SAAS;gCACPC,eAAe,AAAC,UAAqB,OAAZR;4BAC3B;wBACF;;;oBALME,WAAW;yBAOb,CAACA,SAASO,EAAE,EAAZ;;;;oBACgB;;wBAAMP,SAASQ,IAAI;;;oBAA/BP,YAAY;oBAClB,MAAM,IAAIQ,MAAM,AAAC,8BAAgDR,OAAnBD,SAASU,MAAM,EAAC,KAAa,OAAVT;;oBAGnD;;wBAAMD,SAASW,IAAI;;;oBAA7BT,SAAU;oBAEhB,2BAA2B;oBAC3B,IAAI,CAACA,OAAOU,cAAc,IAAI,CAACV,OAAOU,cAAc,CAACC,WAAW,EAAE;wBAChE,MAAM,IAAIJ,MAAM;oBAClB;oBAEA;;wBAAOP;;;;IACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/oauth-microsoft/src/lib/fetch-with-timeout.ts"],"sourcesContent":["/**\n * Fetch with timeout to prevent hanging requests\n *\n * This utility wraps the native fetch API with an AbortController to ensure\n * requests don't hang indefinitely. This is critical for OAuth token operations\n * where expired tokens might cause Microsoft's servers to hang or respond slowly.\n *\n * @param url - URL to fetch\n * @param options - Fetch options\n * @param timeoutMs - Timeout in milliseconds (default: 30000)\n * @returns Fetch response\n * @throws Error if request times out\n */\nexport async function fetchWithTimeout(url: string, options?: RequestInit, timeoutMs = 30000): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n });\n clearTimeout(timeoutId);\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout after ${timeoutMs}ms`);\n }\n throw error;\n }\n}\n"],"names":["fetchWithTimeout","url","options","timeoutMs","controller","timeoutId","response","error","AbortController","setTimeout","abort","fetch","signal","clearTimeout","Error","name"],"mappings":"AAAA;;;;;;;;;;;;CAYC;;;;+BACqBA;;;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAf,SAAeA,iBAAiBC,GAAW,EAAEC,OAAqB;QAAEC,YAAAA,iEAAY;;YAC/EC,YACAC,WAGEC,UAMCC;;;;oBAVHH,aAAa,IAAII;oBACjBH,YAAYI,WAAW;+BAAML,WAAWM,KAAK;uBAAIP;;;;;;;;;oBAGpC;;wBAAMQ,MAAMV,KAAK,wCAC7BC;4BACHU,QAAQR,WAAWQ,MAAM;;;;oBAFrBN,WAAW;oBAIjBO,aAAaR;oBACb;;wBAAOC;;;oBACAC;oBACPM,aAAaR;oBACb,IAAIE,AAAK,YAALA,OAAiBO,UAASP,MAAMQ,IAAI,KAAK,cAAc;wBACzD,MAAM,IAAID,MAAM,AAAC,yBAAkC,OAAVX,WAAU;oBACrD;oBACA,MAAMI;;;;;;;IAEV"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loopback OAuth callback router
|
|
3
|
+
*
|
|
4
|
+
* Handles GET /oauth/callback for persistent redirectUri deployments.
|
|
5
|
+
*/
|
|
6
|
+
import express from 'express';
|
|
7
|
+
import type { LoopbackOAuthProvider } from '../providers/loopback-oauth.js';
|
|
8
|
+
export declare function createLoopbackCallbackRouter(provider: LoopbackOAuthProvider): express.Router;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loopback OAuth callback router
|
|
3
|
+
*
|
|
4
|
+
* Handles GET /oauth/callback for persistent redirectUri deployments.
|
|
5
|
+
*/
|
|
6
|
+
import express from 'express';
|
|
7
|
+
import type { LoopbackOAuthProvider } from '../providers/loopback-oauth.js';
|
|
8
|
+
export declare function createLoopbackCallbackRouter(provider: LoopbackOAuthProvider): express.Router;
|