@mcp-z/oauth-microsoft 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/dist/cjs/index.d.cts +16 -0
  4. package/dist/cjs/index.d.ts +16 -0
  5. package/dist/cjs/index.js +112 -0
  6. package/dist/cjs/index.js.map +1 -0
  7. package/dist/cjs/lib/dcr-router.d.cts +44 -0
  8. package/dist/cjs/lib/dcr-router.d.ts +44 -0
  9. package/dist/cjs/lib/dcr-router.js +1227 -0
  10. package/dist/cjs/lib/dcr-router.js.map +1 -0
  11. package/dist/cjs/lib/dcr-utils.d.cts +160 -0
  12. package/dist/cjs/lib/dcr-utils.d.ts +160 -0
  13. package/dist/cjs/lib/dcr-utils.js +860 -0
  14. package/dist/cjs/lib/dcr-utils.js.map +1 -0
  15. package/dist/cjs/lib/dcr-verify.d.cts +53 -0
  16. package/dist/cjs/lib/dcr-verify.d.ts +53 -0
  17. package/dist/cjs/lib/dcr-verify.js +193 -0
  18. package/dist/cjs/lib/dcr-verify.js.map +1 -0
  19. package/dist/cjs/lib/fetch-with-timeout.d.cts +14 -0
  20. package/dist/cjs/lib/fetch-with-timeout.d.ts +14 -0
  21. package/dist/cjs/lib/fetch-with-timeout.js +257 -0
  22. package/dist/cjs/lib/fetch-with-timeout.js.map +1 -0
  23. package/dist/cjs/lib/token-verifier.d.cts +44 -0
  24. package/dist/cjs/lib/token-verifier.d.ts +44 -0
  25. package/dist/cjs/lib/token-verifier.js +253 -0
  26. package/dist/cjs/lib/token-verifier.js.map +1 -0
  27. package/dist/cjs/package.json +1 -0
  28. package/dist/cjs/providers/dcr.d.cts +110 -0
  29. package/dist/cjs/providers/dcr.d.ts +110 -0
  30. package/dist/cjs/providers/dcr.js +600 -0
  31. package/dist/cjs/providers/dcr.js.map +1 -0
  32. package/dist/cjs/providers/device-code.d.cts +179 -0
  33. package/dist/cjs/providers/device-code.d.ts +179 -0
  34. package/dist/cjs/providers/device-code.js +896 -0
  35. package/dist/cjs/providers/device-code.js.map +1 -0
  36. package/dist/cjs/providers/loopback-oauth.d.cts +125 -0
  37. package/dist/cjs/providers/loopback-oauth.d.ts +125 -0
  38. package/dist/cjs/providers/loopback-oauth.js +1325 -0
  39. package/dist/cjs/providers/loopback-oauth.js.map +1 -0
  40. package/dist/cjs/schemas/index.d.cts +20 -0
  41. package/dist/cjs/schemas/index.d.ts +20 -0
  42. package/dist/cjs/schemas/index.js +37 -0
  43. package/dist/cjs/schemas/index.js.map +1 -0
  44. package/dist/cjs/setup/config.d.cts +113 -0
  45. package/dist/cjs/setup/config.d.ts +113 -0
  46. package/dist/cjs/setup/config.js +246 -0
  47. package/dist/cjs/setup/config.js.map +1 -0
  48. package/dist/cjs/types.d.cts +188 -0
  49. package/dist/cjs/types.d.ts +188 -0
  50. package/dist/cjs/types.js +18 -0
  51. package/dist/cjs/types.js.map +1 -0
  52. package/dist/esm/index.d.ts +16 -0
  53. package/dist/esm/index.js +16 -0
  54. package/dist/esm/index.js.map +1 -0
  55. package/dist/esm/lib/dcr-router.d.ts +44 -0
  56. package/dist/esm/lib/dcr-router.js +556 -0
  57. package/dist/esm/lib/dcr-router.js.map +1 -0
  58. package/dist/esm/lib/dcr-utils.d.ts +160 -0
  59. package/dist/esm/lib/dcr-utils.js +270 -0
  60. package/dist/esm/lib/dcr-utils.js.map +1 -0
  61. package/dist/esm/lib/dcr-verify.d.ts +53 -0
  62. package/dist/esm/lib/dcr-verify.js +53 -0
  63. package/dist/esm/lib/dcr-verify.js.map +1 -0
  64. package/dist/esm/lib/fetch-with-timeout.d.ts +14 -0
  65. package/dist/esm/lib/fetch-with-timeout.js +30 -0
  66. package/dist/esm/lib/fetch-with-timeout.js.map +1 -0
  67. package/dist/esm/lib/token-verifier.d.ts +44 -0
  68. package/dist/esm/lib/token-verifier.js +53 -0
  69. package/dist/esm/lib/token-verifier.js.map +1 -0
  70. package/dist/esm/package.json +1 -0
  71. package/dist/esm/providers/dcr.d.ts +110 -0
  72. package/dist/esm/providers/dcr.js +235 -0
  73. package/dist/esm/providers/dcr.js.map +1 -0
  74. package/dist/esm/providers/device-code.d.ts +179 -0
  75. package/dist/esm/providers/device-code.js +417 -0
  76. package/dist/esm/providers/device-code.js.map +1 -0
  77. package/dist/esm/providers/loopback-oauth.d.ts +125 -0
  78. package/dist/esm/providers/loopback-oauth.js +643 -0
  79. package/dist/esm/providers/loopback-oauth.js.map +1 -0
  80. package/dist/esm/schemas/index.d.ts +20 -0
  81. package/dist/esm/schemas/index.js +18 -0
  82. package/dist/esm/schemas/index.js.map +1 -0
  83. package/dist/esm/setup/config.d.ts +113 -0
  84. package/dist/esm/setup/config.js +268 -0
  85. package/dist/esm/setup/config.js.map +1 -0
  86. package/dist/esm/types.d.ts +188 -0
  87. package/dist/esm/types.js +8 -0
  88. package/dist/esm/types.js.map +1 -0
  89. package/package.json +87 -0
@@ -0,0 +1,53 @@
1
+ /**
2
+ * DCR Token Verification Utilities
3
+ *
4
+ * Provides token verification for both self-hosted and external DCR modes:
5
+ * - Self-hosted: Verifies tokens against local DCR router (/oauth/verify)
6
+ * - External: Verifies tokens against Auth0/Stitch verification endpoint
7
+ */ import { fetchWithTimeout } from './fetch-with-timeout.js';
8
+ /**
9
+ * Verify bearer token against DCR authorization server
10
+ *
11
+ * Supports both self-hosted and external DCR modes by calling the
12
+ * /oauth/verify endpoint (or equivalent external URL).
13
+ *
14
+ * @param bearerToken - Bearer token to verify (without "Bearer " prefix)
15
+ * @param verifyUrl - Verification endpoint URL (self-hosted or external)
16
+ * @returns Verification result with provider tokens
17
+ * @throws Error if verification fails
18
+ *
19
+ * @example Self-hosted mode
20
+ * ```typescript
21
+ * const result = await verifyBearerToken(
22
+ * token,
23
+ * 'http://localhost:3456/oauth/verify'
24
+ * );
25
+ * const auth = provider.toAuthProvider(result.providerTokens);
26
+ * ```
27
+ *
28
+ * @example External mode (Auth0/Stitch)
29
+ * ```typescript
30
+ * const result = await verifyBearerToken(
31
+ * token,
32
+ * 'https://auth.example.com/oauth/verify'
33
+ * );
34
+ * const auth = provider.toAuthProvider(result.providerTokens);
35
+ * ```
36
+ */ export async function verifyBearerToken(bearerToken, verifyUrl) {
37
+ const response = await fetchWithTimeout(verifyUrl, {
38
+ method: 'GET',
39
+ headers: {
40
+ Authorization: `Bearer ${bearerToken}`
41
+ }
42
+ });
43
+ if (!response.ok) {
44
+ const errorText = await response.text();
45
+ throw new Error(`Token verification failed: ${response.status} ${errorText}`);
46
+ }
47
+ const result = await response.json();
48
+ // Validate required fields
49
+ if (!result.providerTokens || !result.providerTokens.accessToken) {
50
+ throw new Error('Verification response missing required provider tokens');
51
+ }
52
+ return result;
53
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/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":["fetchWithTimeout","verifyBearerToken","bearerToken","verifyUrl","response","method","headers","Authorization","ok","errorText","text","Error","status","result","json","providerTokens","accessToken"],"mappings":"AAAA;;;;;;CAMC,GAGD,SAASA,gBAAgB,QAAQ,0BAA0B;AAkB3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC,GACD,OAAO,eAAeC,kBAAkBC,WAAmB,EAAEC,SAAiB;IAC5E,MAAMC,WAAW,MAAMJ,iBAAiBG,WAAW;QACjDE,QAAQ;QACRC,SAAS;YACPC,eAAe,CAAC,OAAO,EAAEL,aAAa;QACxC;IACF;IAEA,IAAI,CAACE,SAASI,EAAE,EAAE;QAChB,MAAMC,YAAY,MAAML,SAASM,IAAI;QACrC,MAAM,IAAIC,MAAM,CAAC,2BAA2B,EAAEP,SAASQ,MAAM,CAAC,CAAC,EAAEH,WAAW;IAC9E;IAEA,MAAMI,SAAU,MAAMT,SAASU,IAAI;IAEnC,2BAA2B;IAC3B,IAAI,CAACD,OAAOE,cAAc,IAAI,CAACF,OAAOE,cAAc,CAACC,WAAW,EAAE;QAChE,MAAM,IAAIL,MAAM;IAClB;IAEA,OAAOE;AACT"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Fetch with timeout to prevent hanging requests
3
+ *
4
+ * This utility wraps the native fetch API with an AbortController to ensure
5
+ * requests don't hang indefinitely. This is critical for OAuth token operations
6
+ * where expired tokens might cause Microsoft's servers to hang or respond slowly.
7
+ *
8
+ * @param url - URL to fetch
9
+ * @param options - Fetch options
10
+ * @param timeoutMs - Timeout in milliseconds (default: 30000)
11
+ * @returns Fetch response
12
+ * @throws Error if request times out
13
+ */
14
+ export declare function fetchWithTimeout(url: string, options?: RequestInit, timeoutMs?: number): Promise<Response>;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Fetch with timeout to prevent hanging requests
3
+ *
4
+ * This utility wraps the native fetch API with an AbortController to ensure
5
+ * requests don't hang indefinitely. This is critical for OAuth token operations
6
+ * where expired tokens might cause Microsoft's servers to hang or respond slowly.
7
+ *
8
+ * @param url - URL to fetch
9
+ * @param options - Fetch options
10
+ * @param timeoutMs - Timeout in milliseconds (default: 30000)
11
+ * @returns Fetch response
12
+ * @throws Error if request times out
13
+ */ export async function fetchWithTimeout(url, options, timeoutMs = 30000) {
14
+ const controller = new AbortController();
15
+ const timeoutId = setTimeout(()=>controller.abort(), timeoutMs);
16
+ try {
17
+ const response = await fetch(url, {
18
+ ...options,
19
+ signal: controller.signal
20
+ });
21
+ clearTimeout(timeoutId);
22
+ return response;
23
+ } catch (error) {
24
+ clearTimeout(timeoutId);
25
+ if (error instanceof Error && error.name === 'AbortError') {
26
+ throw new Error(`Request timeout after ${timeoutMs}ms`);
27
+ }
28
+ throw error;
29
+ }
30
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/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","AbortController","timeoutId","setTimeout","abort","response","fetch","signal","clearTimeout","error","Error","name"],"mappings":"AAAA;;;;;;;;;;;;CAYC,GACD,OAAO,eAAeA,iBAAiBC,GAAW,EAAEC,OAAqB,EAAEC,YAAY,KAAK;IAC1F,MAAMC,aAAa,IAAIC;IACvB,MAAMC,YAAYC,WAAW,IAAMH,WAAWI,KAAK,IAAIL;IAEvD,IAAI;QACF,MAAMM,WAAW,MAAMC,MAAMT,KAAK;YAChC,GAAGC,OAAO;YACVS,QAAQP,WAAWO,MAAM;QAC3B;QACAC,aAAaN;QACb,OAAOG;IACT,EAAE,OAAOI,OAAO;QACdD,aAAaN;QACb,IAAIO,iBAAiBC,SAASD,MAAME,IAAI,KAAK,cAAc;YACzD,MAAM,IAAID,MAAM,CAAC,sBAAsB,EAAEX,UAAU,EAAE,CAAC;QACxD;QACA,MAAMU;IACR;AACF"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * DCR Token Verifier
3
+ *
4
+ * Validates bearer tokens by calling Authorization Server's verification endpoint.
5
+ * Implements proper AS/RS separation - Resource Server doesn't access token storage.
6
+ */
7
+ import type { ProviderTokens } from '@mcp-z/oauth';
8
+ /**
9
+ * Authentication information from token verification
10
+ */
11
+ export interface AuthInfo {
12
+ /** Bearer access token */
13
+ token: string;
14
+ /** Client ID that owns the token */
15
+ clientId: string;
16
+ /** Granted scopes */
17
+ scopes: string[];
18
+ /** Token expiration timestamp (milliseconds since epoch) */
19
+ expiresAt: number;
20
+ /** Microsoft provider tokens (if available) */
21
+ providerTokens?: ProviderTokens;
22
+ }
23
+ /**
24
+ * DCR Token Verifier validates access tokens via Authorization Server
25
+ *
26
+ * This implements proper OAuth 2.0 architecture where the Resource Server
27
+ * (MCP server) validates tokens by calling the Authorization Server's
28
+ * verification endpoint rather than accessing token storage directly.
29
+ */
30
+ export declare class DcrTokenVerifier {
31
+ private verifyUrl;
32
+ /**
33
+ * @param verifyUrl - Authorization Server's /oauth/verify endpoint URL
34
+ */
35
+ constructor(verifyUrl: string);
36
+ /**
37
+ * Verify an access token by calling the Authorization Server
38
+ *
39
+ * @param token - Bearer access token to validate
40
+ * @returns AuthInfo with token metadata and provider tokens
41
+ * @throws Error if token is invalid or verification fails
42
+ */
43
+ verifyAccessToken(token: string): Promise<AuthInfo>;
44
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * DCR Token Verifier
3
+ *
4
+ * Validates bearer tokens by calling Authorization Server's verification endpoint.
5
+ * Implements proper AS/RS separation - Resource Server doesn't access token storage.
6
+ */ /**
7
+ * DCR Token Verifier validates access tokens via Authorization Server
8
+ *
9
+ * This implements proper OAuth 2.0 architecture where the Resource Server
10
+ * (MCP server) validates tokens by calling the Authorization Server's
11
+ * verification endpoint rather than accessing token storage directly.
12
+ */ export class DcrTokenVerifier {
13
+ /**
14
+ * Verify an access token by calling the Authorization Server
15
+ *
16
+ * @param token - Bearer access token to validate
17
+ * @returns AuthInfo with token metadata and provider tokens
18
+ * @throws Error if token is invalid or verification fails
19
+ */ async verifyAccessToken(token) {
20
+ try {
21
+ const response = await fetch(this.verifyUrl, {
22
+ method: 'GET',
23
+ headers: {
24
+ Authorization: `Bearer ${token}`
25
+ }
26
+ });
27
+ if (!response.ok) {
28
+ let errorMessage = 'Unknown error';
29
+ try {
30
+ var _ref, _error_error_description;
31
+ const error = await response.json();
32
+ errorMessage = (_ref = (_error_error_description = error.error_description) !== null && _error_error_description !== void 0 ? _error_error_description : error.error) !== null && _ref !== void 0 ? _ref : errorMessage;
33
+ } catch {
34
+ // Failed to parse error JSON, use status text
35
+ errorMessage = response.statusText;
36
+ }
37
+ throw new Error(`Token verification failed: ${errorMessage}`);
38
+ }
39
+ const authInfo = await response.json();
40
+ return authInfo;
41
+ } catch (error) {
42
+ if (error instanceof Error) {
43
+ throw error;
44
+ }
45
+ throw new Error(`Token verification failed: ${String(error)}`);
46
+ }
47
+ }
48
+ /**
49
+ * @param verifyUrl - Authorization Server's /oauth/verify endpoint URL
50
+ */ constructor(verifyUrl){
51
+ this.verifyUrl = verifyUrl;
52
+ }
53
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-microsoft/src/lib/token-verifier.ts"],"sourcesContent":["/**\n * DCR Token Verifier\n *\n * Validates bearer tokens by calling Authorization Server's verification endpoint.\n * Implements proper AS/RS separation - Resource Server doesn't access token storage.\n */\n\nimport type { ProviderTokens } from '@mcp-z/oauth';\n\n/**\n * Authentication information from token verification\n */\nexport interface AuthInfo {\n /** Bearer access token */\n token: string;\n\n /** Client ID that owns the token */\n clientId: string;\n\n /** Granted scopes */\n scopes: string[];\n\n /** Token expiration timestamp (milliseconds since epoch) */\n expiresAt: number;\n\n /** Microsoft provider tokens (if available) */\n providerTokens?: ProviderTokens;\n}\n\n/**\n * DCR Token Verifier validates access tokens via Authorization Server\n *\n * This implements proper OAuth 2.0 architecture where the Resource Server\n * (MCP server) validates tokens by calling the Authorization Server's\n * verification endpoint rather than accessing token storage directly.\n */\nexport class DcrTokenVerifier {\n private verifyUrl: string;\n\n /**\n * @param verifyUrl - Authorization Server's /oauth/verify endpoint URL\n */\n constructor(verifyUrl: string) {\n this.verifyUrl = verifyUrl;\n }\n\n /**\n * Verify an access token by calling the Authorization Server\n *\n * @param token - Bearer access token to validate\n * @returns AuthInfo with token metadata and provider tokens\n * @throws Error if token is invalid or verification fails\n */\n async verifyAccessToken(token: string): Promise<AuthInfo> {\n try {\n const response = await fetch(this.verifyUrl, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) {\n let errorMessage = 'Unknown error';\n try {\n const error = await response.json();\n errorMessage = (error as { error_description?: string; error?: string }).error_description ?? (error as { error?: string }).error ?? errorMessage;\n } catch {\n // Failed to parse error JSON, use status text\n errorMessage = response.statusText;\n }\n throw new Error(`Token verification failed: ${errorMessage}`);\n }\n\n const authInfo = (await response.json()) as AuthInfo;\n return authInfo;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Token verification failed: ${String(error)}`);\n }\n }\n}\n"],"names":["DcrTokenVerifier","verifyAccessToken","token","response","fetch","verifyUrl","method","headers","Authorization","ok","errorMessage","error","json","error_description","statusText","Error","authInfo","String"],"mappings":"AAAA;;;;;CAKC,GAwBD;;;;;;CAMC,GACD,OAAO,MAAMA;IAUX;;;;;;GAMC,GACD,MAAMC,kBAAkBC,KAAa,EAAqB;QACxD,IAAI;YACF,MAAMC,WAAW,MAAMC,MAAM,IAAI,CAACC,SAAS,EAAE;gBAC3CC,QAAQ;gBACRC,SAAS;oBACPC,eAAe,CAAC,OAAO,EAAEN,OAAO;gBAClC;YACF;YAEA,IAAI,CAACC,SAASM,EAAE,EAAE;gBAChB,IAAIC,eAAe;gBACnB,IAAI;wBAEa,MAAA;oBADf,MAAMC,QAAQ,MAAMR,SAASS,IAAI;oBACjCF,gBAAe,QAAA,2BAAA,AAACC,MAAyDE,iBAAiB,cAA3E,sCAAA,2BAA+E,AAACF,MAA6BA,KAAK,cAAlH,kBAAA,OAAsHD;gBACvI,EAAE,OAAM;oBACN,8CAA8C;oBAC9CA,eAAeP,SAASW,UAAU;gBACpC;gBACA,MAAM,IAAIC,MAAM,CAAC,2BAA2B,EAAEL,cAAc;YAC9D;YAEA,MAAMM,WAAY,MAAMb,SAASS,IAAI;YACrC,OAAOI;QACT,EAAE,OAAOL,OAAO;YACd,IAAIA,iBAAiBI,OAAO;gBAC1B,MAAMJ;YACR;YACA,MAAM,IAAII,MAAM,CAAC,2BAA2B,EAAEE,OAAON,QAAQ;QAC/D;IACF;IA3CA;;GAEC,GACD,YAAYN,SAAiB,CAAE;QAC7B,IAAI,CAACA,SAAS,GAAGA;IACnB;AAuCF"}
@@ -0,0 +1 @@
1
+ { "type": "module" }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * DCR Provider - Stateless Dynamic Client Registration Provider
3
+ *
4
+ * Implements stateless provider pattern where provider tokens are received from
5
+ * token verification context rather than managed by the provider itself.
6
+ *
7
+ * Use case: MCP HTTP servers with DCR authentication where client manages tokens
8
+ * and provider only handles Microsoft Graph API calls with provided credentials.
9
+ */
10
+ import type { ProviderTokens } from '@mcp-z/oauth';
11
+ import type { Logger, MicrosoftAuthProvider } from '../types.js';
12
+ /**
13
+ * DCR Provider configuration
14
+ */
15
+ export interface DcrOAuthProviderConfig {
16
+ /** Microsoft application client ID */
17
+ clientId: string;
18
+ /** Microsoft application client secret (optional for public clients) */
19
+ clientSecret?: string;
20
+ /** Azure AD tenant ID */
21
+ tenantId: string;
22
+ /** OAuth scopes */
23
+ scope: string;
24
+ /** Custom token endpoint URL (for testing, defaults to Microsoft OAuth endpoint) */
25
+ tokenUrl?: string;
26
+ /** DCR token verification endpoint URL (e.g., http://localhost:3000/oauth/verify) */
27
+ verifyEndpoint: string;
28
+ /** Logger for auth operations */
29
+ logger: Logger;
30
+ }
31
+ /**
32
+ * DCR Provider - Stateless OAuth provider for Dynamic Client Registration
33
+ *
34
+ * Unlike LoopbackOAuthProvider which manages token storage, DcrOAuthProvider is stateless:
35
+ * - Receives provider tokens from verification context (HTTP bearer auth)
36
+ * - Creates auth providers on-demand from tokens
37
+ * - Handles token refresh using Microsoft OAuth
38
+ * - No token storage dependency
39
+ *
40
+ * Pattern:
41
+ * ```typescript
42
+ * const provider = new DcrOAuthProvider(config);
43
+ * const auth = provider.toAuthProvider(providerTokens);
44
+ * const accessToken = await auth.getAccessToken();
45
+ * ```
46
+ */
47
+ export declare class DcrOAuthProvider {
48
+ private config;
49
+ private emailCache;
50
+ constructor(config: DcrOAuthProviderConfig);
51
+ /**
52
+ * Create Microsoft Graph auth provider from provider tokens
53
+ *
54
+ * This is the core stateless pattern - provider receives tokens from context
55
+ * (token verification, HTTP request) and creates auth provider on-demand.
56
+ *
57
+ * @param tokens - Provider tokens (Microsoft access/refresh tokens)
58
+ * @returns Microsoft Graph-compatible auth provider
59
+ */
60
+ toAuthProvider(tokens: ProviderTokens): MicrosoftAuthProvider;
61
+ /**
62
+ * Check if token is still valid (with 1 minute buffer)
63
+ */
64
+ private isTokenValid;
65
+ /**
66
+ * Refresh Microsoft access token using refresh token
67
+ *
68
+ * @param refreshToken - Microsoft refresh token
69
+ * @returns New provider tokens
70
+ */
71
+ refreshAccessToken(refreshToken: string): Promise<ProviderTokens>;
72
+ /**
73
+ * Get user email from Microsoft Graph API (with caching)
74
+ *
75
+ * @param tokens - Provider tokens to use for API call
76
+ * @returns User's email address
77
+ */
78
+ getUserEmail(tokens: ProviderTokens): Promise<string>;
79
+ /**
80
+ * Auth middleware for HTTP servers with DCR bearer auth
81
+ * Validates bearer tokens and enriches extra with provider tokens
82
+ *
83
+ * Pattern:
84
+ * ```typescript
85
+ * const provider = new DcrOAuthProvider({ ..., verifyEndpoint: 'http://localhost:3000/oauth/verify' });
86
+ * const middleware = provider.authMiddleware();
87
+ * const tools = toolFactories.map(f => f()).map(middleware.withToolAuth);
88
+ * const resources = resourceFactories.map(f => f()).map(middleware.withResourceAuth);
89
+ * const prompts = promptFactories.map(f => f()).map(middleware.withPromptAuth);
90
+ * ```
91
+ */
92
+ authMiddleware(): {
93
+ withToolAuth: <T extends {
94
+ name: string;
95
+ config: unknown;
96
+ handler: unknown;
97
+ }>(module: T) => T;
98
+ withResourceAuth: <T extends {
99
+ name: string;
100
+ template?: unknown;
101
+ config?: unknown;
102
+ handler: unknown;
103
+ }>(module: T) => T;
104
+ withPromptAuth: <T extends {
105
+ name: string;
106
+ config: unknown;
107
+ handler: unknown;
108
+ }>(module: T) => T;
109
+ };
110
+ }
@@ -0,0 +1,235 @@
1
+ /**
2
+ * DCR Provider - Stateless Dynamic Client Registration Provider
3
+ *
4
+ * Implements stateless provider pattern where provider tokens are received from
5
+ * token verification context rather than managed by the provider itself.
6
+ *
7
+ * Use case: MCP HTTP servers with DCR authentication where client manages tokens
8
+ * and provider only handles Microsoft Graph API calls with provided credentials.
9
+ */ import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
10
+ import { fetchWithTimeout } from '../lib/fetch-with-timeout.js';
11
+ /**
12
+ * DCR Provider - Stateless OAuth provider for Dynamic Client Registration
13
+ *
14
+ * Unlike LoopbackOAuthProvider which manages token storage, DcrOAuthProvider is stateless:
15
+ * - Receives provider tokens from verification context (HTTP bearer auth)
16
+ * - Creates auth providers on-demand from tokens
17
+ * - Handles token refresh using Microsoft OAuth
18
+ * - No token storage dependency
19
+ *
20
+ * Pattern:
21
+ * ```typescript
22
+ * const provider = new DcrOAuthProvider(config);
23
+ * const auth = provider.toAuthProvider(providerTokens);
24
+ * const accessToken = await auth.getAccessToken();
25
+ * ```
26
+ */ export class DcrOAuthProvider {
27
+ /**
28
+ * Create Microsoft Graph auth provider from provider tokens
29
+ *
30
+ * This is the core stateless pattern - provider receives tokens from context
31
+ * (token verification, HTTP request) and creates auth provider on-demand.
32
+ *
33
+ * @param tokens - Provider tokens (Microsoft access/refresh tokens)
34
+ * @returns Microsoft Graph-compatible auth provider
35
+ */ toAuthProvider(tokens) {
36
+ // Capture tokens in closure for auth provider
37
+ let currentTokens = {
38
+ ...tokens
39
+ };
40
+ return {
41
+ getAccessToken: async ()=>{
42
+ // Check if token is still valid
43
+ if (this.isTokenValid(currentTokens)) {
44
+ return currentTokens.accessToken;
45
+ }
46
+ // Token expired - try refresh if available
47
+ if (currentTokens.refreshToken) {
48
+ try {
49
+ const refreshedTokens = await this.refreshAccessToken(currentTokens.refreshToken);
50
+ currentTokens = refreshedTokens;
51
+ return currentTokens.accessToken;
52
+ } catch (error) {
53
+ throw new Error(`Token refresh failed: ${error instanceof Error ? error.message : String(error)}`);
54
+ }
55
+ }
56
+ // No refresh token - token expired and cannot refresh
57
+ throw new Error('Access token expired and no refresh token available');
58
+ }
59
+ };
60
+ }
61
+ /**
62
+ * Check if token is still valid (with 1 minute buffer)
63
+ */ isTokenValid(tokens) {
64
+ if (!tokens.expiresAt) return true; // No expiry = assume valid
65
+ return Date.now() < tokens.expiresAt - 60000; // 1 minute buffer
66
+ }
67
+ /**
68
+ * Refresh Microsoft access token using refresh token
69
+ *
70
+ * @param refreshToken - Microsoft refresh token
71
+ * @returns New provider tokens
72
+ */ async refreshAccessToken(refreshToken) {
73
+ const { clientId, clientSecret, tenantId, scope, tokenUrl: customTokenUrl } = this.config;
74
+ const tokenUrl = customTokenUrl !== null && customTokenUrl !== void 0 ? customTokenUrl : `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
75
+ const params = {
76
+ refresh_token: refreshToken,
77
+ client_id: clientId,
78
+ grant_type: 'refresh_token',
79
+ scope
80
+ };
81
+ // Only include client_secret for confidential clients
82
+ if (clientSecret) {
83
+ params.client_secret = clientSecret;
84
+ }
85
+ const body = new URLSearchParams(params);
86
+ const response = await fetchWithTimeout(tokenUrl, {
87
+ method: 'POST',
88
+ headers: {
89
+ 'Content-Type': 'application/x-www-form-urlencoded'
90
+ },
91
+ body: body.toString()
92
+ });
93
+ if (!response.ok) {
94
+ const errorText = await response.text();
95
+ throw new Error(`Token refresh failed: ${response.status} ${errorText}`);
96
+ }
97
+ const tokenResponse = await response.json();
98
+ return {
99
+ accessToken: tokenResponse.access_token,
100
+ refreshToken: refreshToken,
101
+ ...tokenResponse.expires_in !== undefined && {
102
+ expiresAt: Date.now() + tokenResponse.expires_in * 1000
103
+ },
104
+ ...tokenResponse.scope !== undefined && {
105
+ scope: tokenResponse.scope
106
+ }
107
+ };
108
+ }
109
+ /**
110
+ * Get user email from Microsoft Graph API (with caching)
111
+ *
112
+ * @param tokens - Provider tokens to use for API call
113
+ * @returns User's email address
114
+ */ async getUserEmail(tokens) {
115
+ var _userInfo_mail, _tokens_expiresAt;
116
+ const cacheKey = tokens.accessToken;
117
+ const cached = this.emailCache.get(cacheKey);
118
+ // Check cache (with same expiry as access token)
119
+ if (cached && Date.now() < cached.expiresAt) {
120
+ return cached.email;
121
+ }
122
+ const auth = this.toAuthProvider(tokens);
123
+ const accessToken = await auth.getAccessToken();
124
+ const response = await fetchWithTimeout('https://graph.microsoft.com/v1.0/me', {
125
+ headers: {
126
+ Authorization: `Bearer ${accessToken}`
127
+ }
128
+ });
129
+ if (!response.ok) {
130
+ throw new Error(`Failed to get user info: ${response.status} ${await response.text()}`);
131
+ }
132
+ const userInfo = await response.json();
133
+ const email = (_userInfo_mail = userInfo.mail) !== null && _userInfo_mail !== void 0 ? _userInfo_mail : userInfo.userPrincipalName;
134
+ // Cache with token expiration (default 1 hour if not specified)
135
+ this.emailCache.set(cacheKey, {
136
+ email,
137
+ expiresAt: (_tokens_expiresAt = tokens.expiresAt) !== null && _tokens_expiresAt !== void 0 ? _tokens_expiresAt : Date.now() + 3600000
138
+ });
139
+ return email;
140
+ }
141
+ /**
142
+ * Auth middleware for HTTP servers with DCR bearer auth
143
+ * Validates bearer tokens and enriches extra with provider tokens
144
+ *
145
+ * Pattern:
146
+ * ```typescript
147
+ * const provider = new DcrOAuthProvider({ ..., verifyEndpoint: 'http://localhost:3000/oauth/verify' });
148
+ * const middleware = provider.authMiddleware();
149
+ * const tools = toolFactories.map(f => f()).map(middleware.withToolAuth);
150
+ * const resources = resourceFactories.map(f => f()).map(middleware.withResourceAuth);
151
+ * const prompts = promptFactories.map(f => f()).map(middleware.withPromptAuth);
152
+ * ```
153
+ */ authMiddleware() {
154
+ // Shared wrapper logic - extracts extra parameter from specified position
155
+ // Generic T captures the actual module type; handler is cast from unknown to callable
156
+ const wrapAtPosition = (module, extraPosition)=>{
157
+ const originalHandler = module.handler;
158
+ const wrappedHandler = async (...allArgs)=>{
159
+ var _extra_requestInfo;
160
+ // Extract extra from the correct position
161
+ const extra = allArgs[extraPosition];
162
+ // Extract DCR bearer token from SDK's authInfo (if present) or request headers
163
+ let bearerToken;
164
+ // Option 1: Token already verified by SDK's bearerAuth middleware
165
+ if (extra.authInfo && typeof extra.authInfo === 'object') {
166
+ var _ref;
167
+ // authInfo contains the validated token - extract it
168
+ // The SDK's bearerAuth middleware already validated it, but we need the raw token for /oauth/verify
169
+ // Check if authInfo has the token directly, otherwise extract from headers
170
+ const authInfo = extra.authInfo;
171
+ bearerToken = (_ref = typeof authInfo.accessToken === 'string' ? authInfo.accessToken : undefined) !== null && _ref !== void 0 ? _ref : typeof authInfo.token === 'string' ? authInfo.token : undefined;
172
+ }
173
+ // Option 2: Extract from Authorization header
174
+ if (!bearerToken && ((_extra_requestInfo = extra.requestInfo) === null || _extra_requestInfo === void 0 ? void 0 : _extra_requestInfo.headers)) {
175
+ const authHeader = extra.requestInfo.headers.authorization || extra.requestInfo.headers.Authorization;
176
+ if (authHeader) {
177
+ // Handle both string and string[] types
178
+ const headerValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;
179
+ if (headerValue) {
180
+ const match = /^Bearer\s+(.+)$/i.exec(headerValue);
181
+ if (match) {
182
+ bearerToken = match[1];
183
+ }
184
+ }
185
+ }
186
+ }
187
+ if (!bearerToken) {
188
+ throw new McpError(ErrorCode.InvalidRequest, 'Missing Authorization header. DCR mode requires bearer token.');
189
+ }
190
+ // Call /oauth/verify to validate DCR token and get provider tokens
191
+ const verifyResponse = await fetchWithTimeout(this.config.verifyEndpoint, {
192
+ headers: {
193
+ Authorization: `Bearer ${bearerToken}`
194
+ }
195
+ });
196
+ if (!verifyResponse.ok) {
197
+ throw new McpError(ErrorCode.InvalidRequest, `Token verification failed: ${verifyResponse.status}`);
198
+ }
199
+ const verifyData = await verifyResponse.json();
200
+ // Fetch user email to use as accountId (with caching)
201
+ let accountId;
202
+ try {
203
+ accountId = await this.getUserEmail(verifyData.providerTokens);
204
+ } catch (error) {
205
+ throw new McpError(ErrorCode.InternalError, `Failed to get user email for DCR authentication: ${error instanceof Error ? error.message : String(error)}`);
206
+ }
207
+ // Create auth provider from provider tokens
208
+ const auth = this.toAuthProvider(verifyData.providerTokens);
209
+ // Inject authContext and logger into extra
210
+ extra.authContext = {
211
+ auth,
212
+ accountId
213
+ };
214
+ extra.logger = this.config.logger;
215
+ // Call original handler with all args
216
+ return await originalHandler(...allArgs);
217
+ };
218
+ return {
219
+ ...module,
220
+ handler: wrappedHandler
221
+ };
222
+ };
223
+ return {
224
+ // Use structural constraints to avoid contravariance check on handler type.
225
+ // wrapAtPosition is now generic and returns T directly.
226
+ withToolAuth: (module)=>wrapAtPosition(module, 1),
227
+ withResourceAuth: (module)=>wrapAtPosition(module, 2),
228
+ withPromptAuth: (module)=>wrapAtPosition(module, 0)
229
+ };
230
+ }
231
+ constructor(config){
232
+ this.emailCache = new Map();
233
+ this.config = config;
234
+ }
235
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/kevin/Dev/Projects/ai/mcp-z/oauth/oauth-microsoft/src/providers/dcr.ts"],"sourcesContent":["/**\n * DCR Provider - Stateless Dynamic Client Registration Provider\n *\n * Implements stateless provider pattern where provider tokens are received from\n * token verification context rather than managed by the provider itself.\n *\n * Use case: MCP HTTP servers with DCR authentication where client manages tokens\n * and provider only handles Microsoft Graph API calls with provided credentials.\n */\n\nimport type { ProviderTokens } from '@mcp-z/oauth';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { fetchWithTimeout } from '../lib/fetch-with-timeout.ts';\nimport type { AuthContext, EnrichedExtra, Logger, MicrosoftAuthProvider } from '../types.ts';\n\n/**\n * DCR Provider configuration\n */\nexport interface DcrOAuthProviderConfig {\n /** Microsoft application client ID */\n clientId: string;\n\n /** Microsoft application client secret (optional for public clients) */\n clientSecret?: string;\n\n /** Azure AD tenant ID */\n tenantId: string;\n\n /** OAuth scopes */\n scope: string;\n\n /** Custom token endpoint URL (for testing, defaults to Microsoft OAuth endpoint) */\n tokenUrl?: string;\n\n /** DCR token verification endpoint URL (e.g., http://localhost:3000/oauth/verify) */\n verifyEndpoint: string;\n\n /** Logger for auth operations */\n logger: Logger;\n}\n\n/**\n * Microsoft Graph TokenResponse\n */\ninterface TokenResponse {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n scope?: string;\n token_type?: string;\n}\n\n/**\n * DCR Provider - Stateless OAuth provider for Dynamic Client Registration\n *\n * Unlike LoopbackOAuthProvider which manages token storage, DcrOAuthProvider is stateless:\n * - Receives provider tokens from verification context (HTTP bearer auth)\n * - Creates auth providers on-demand from tokens\n * - Handles token refresh using Microsoft OAuth\n * - No token storage dependency\n *\n * Pattern:\n * ```typescript\n * const provider = new DcrOAuthProvider(config);\n * const auth = provider.toAuthProvider(providerTokens);\n * const accessToken = await auth.getAccessToken();\n * ```\n */\nexport class DcrOAuthProvider {\n private config: DcrOAuthProviderConfig;\n private emailCache = new Map<string, { email: string; expiresAt: number }>();\n\n constructor(config: DcrOAuthProviderConfig) {\n this.config = config;\n }\n\n /**\n * Create Microsoft Graph auth provider from provider tokens\n *\n * This is the core stateless pattern - provider receives tokens from context\n * (token verification, HTTP request) and creates auth provider on-demand.\n *\n * @param tokens - Provider tokens (Microsoft access/refresh tokens)\n * @returns Microsoft Graph-compatible auth provider\n */\n toAuthProvider(tokens: ProviderTokens): MicrosoftAuthProvider {\n // Capture tokens in closure for auth provider\n let currentTokens = { ...tokens };\n\n return {\n getAccessToken: async (): Promise<string> => {\n // Check if token is still valid\n if (this.isTokenValid(currentTokens)) {\n return currentTokens.accessToken;\n }\n\n // Token expired - try refresh if available\n if (currentTokens.refreshToken) {\n try {\n const refreshedTokens = await this.refreshAccessToken(currentTokens.refreshToken);\n currentTokens = refreshedTokens;\n return currentTokens.accessToken;\n } catch (error) {\n throw new Error(`Token refresh failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n // No refresh token - token expired and cannot refresh\n throw new Error('Access token expired and no refresh token available');\n },\n };\n }\n\n /**\n * Check if token is still valid (with 1 minute buffer)\n */\n private isTokenValid(tokens: ProviderTokens): boolean {\n if (!tokens.expiresAt) return true; // No expiry = assume valid\n return Date.now() < tokens.expiresAt - 60000; // 1 minute buffer\n }\n\n /**\n * Refresh Microsoft access token using refresh token\n *\n * @param refreshToken - Microsoft refresh token\n * @returns New provider tokens\n */\n async refreshAccessToken(refreshToken: string): Promise<ProviderTokens> {\n const { clientId, clientSecret, tenantId, scope, tokenUrl: customTokenUrl } = this.config;\n\n const tokenUrl = customTokenUrl ?? `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;\n const params: Record<string, string> = {\n refresh_token: refreshToken,\n client_id: clientId,\n grant_type: 'refresh_token',\n scope,\n };\n\n // Only include client_secret for confidential clients\n if (clientSecret) {\n params.client_secret = clientSecret;\n }\n\n const body = new URLSearchParams(params);\n\n const response = await fetchWithTimeout(tokenUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: body.toString(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Token refresh failed: ${response.status} ${errorText}`);\n }\n\n const tokenResponse = (await response.json()) as TokenResponse;\n\n return {\n accessToken: tokenResponse.access_token,\n refreshToken: refreshToken, // Keep original refresh token\n ...(tokenResponse.expires_in !== undefined && { expiresAt: Date.now() + tokenResponse.expires_in * 1000 }),\n ...(tokenResponse.scope !== undefined && { scope: tokenResponse.scope }),\n };\n }\n\n /**\n * Get user email from Microsoft Graph API (with caching)\n *\n * @param tokens - Provider tokens to use for API call\n * @returns User's email address\n */\n async getUserEmail(tokens: ProviderTokens): Promise<string> {\n const cacheKey = tokens.accessToken;\n const cached = this.emailCache.get(cacheKey);\n\n // Check cache (with same expiry as access token)\n if (cached && Date.now() < cached.expiresAt) {\n return cached.email;\n }\n\n const auth = this.toAuthProvider(tokens);\n const accessToken = await auth.getAccessToken();\n\n const response = await fetchWithTimeout('https://graph.microsoft.com/v1.0/me', {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!response.ok) {\n throw new Error(`Failed to get user info: ${response.status} ${await response.text()}`);\n }\n\n const userInfo = (await response.json()) as { mail?: string; userPrincipalName: string };\n const email = userInfo.mail ?? userInfo.userPrincipalName;\n\n // Cache with token expiration (default 1 hour if not specified)\n this.emailCache.set(cacheKey, {\n email,\n expiresAt: tokens.expiresAt ?? Date.now() + 3600000,\n });\n\n return email;\n }\n\n /**\n * Auth middleware for HTTP servers with DCR bearer auth\n * Validates bearer tokens and enriches extra with provider tokens\n *\n * Pattern:\n * ```typescript\n * const provider = new DcrOAuthProvider({ ..., verifyEndpoint: 'http://localhost:3000/oauth/verify' });\n * const middleware = provider.authMiddleware();\n * const tools = toolFactories.map(f => f()).map(middleware.withToolAuth);\n * const resources = resourceFactories.map(f => f()).map(middleware.withResourceAuth);\n * const prompts = promptFactories.map(f => f()).map(middleware.withPromptAuth);\n * ```\n */\n authMiddleware() {\n // Shared wrapper logic - extracts extra parameter from specified position\n // Generic T captures the actual module type; handler is cast from unknown to callable\n const wrapAtPosition = <T extends { name: string; handler: unknown; [key: string]: unknown }>(module: T, extraPosition: number): T => {\n const originalHandler = module.handler as (...args: unknown[]) => Promise<unknown>;\n\n const wrappedHandler = async (...allArgs: unknown[]) => {\n // Extract extra from the correct position\n const extra = allArgs[extraPosition] as EnrichedExtra;\n\n // Extract DCR bearer token from SDK's authInfo (if present) or request headers\n let bearerToken: string | undefined;\n\n // Option 1: Token already verified by SDK's bearerAuth middleware\n if (extra.authInfo && typeof extra.authInfo === 'object') {\n // authInfo contains the validated token - extract it\n // The SDK's bearerAuth middleware already validated it, but we need the raw token for /oauth/verify\n // Check if authInfo has the token directly, otherwise extract from headers\n const authInfo = extra.authInfo as unknown as Record<string, unknown>;\n bearerToken = (typeof authInfo.accessToken === 'string' ? authInfo.accessToken : undefined) ?? (typeof authInfo.token === 'string' ? authInfo.token : undefined);\n }\n\n // Option 2: Extract from Authorization header\n if (!bearerToken && extra.requestInfo?.headers) {\n const authHeader = extra.requestInfo.headers.authorization || extra.requestInfo.headers.Authorization;\n if (authHeader) {\n // Handle both string and string[] types\n const headerValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;\n if (headerValue) {\n const match = /^Bearer\\s+(.+)$/i.exec(headerValue);\n if (match) {\n bearerToken = match[1];\n }\n }\n }\n }\n\n if (!bearerToken) {\n throw new McpError(ErrorCode.InvalidRequest, 'Missing Authorization header. DCR mode requires bearer token.');\n }\n\n // Call /oauth/verify to validate DCR token and get provider tokens\n const verifyResponse = await fetchWithTimeout(this.config.verifyEndpoint, {\n headers: { Authorization: `Bearer ${bearerToken}` },\n });\n\n if (!verifyResponse.ok) {\n throw new McpError(ErrorCode.InvalidRequest, `Token verification failed: ${verifyResponse.status}`);\n }\n\n const verifyData = (await verifyResponse.json()) as {\n providerTokens: ProviderTokens;\n };\n\n // Fetch user email to use as accountId (with caching)\n let accountId: string;\n try {\n accountId = await this.getUserEmail(verifyData.providerTokens);\n } catch (error) {\n throw new McpError(ErrorCode.InternalError, `Failed to get user email for DCR authentication: ${error instanceof Error ? error.message : String(error)}`);\n }\n\n // Create auth provider from provider tokens\n const auth = this.toAuthProvider(verifyData.providerTokens);\n\n // Inject authContext and logger into extra\n (extra as { authContext?: AuthContext }).authContext = {\n auth,\n accountId, // User's email address\n };\n (extra as { logger?: unknown }).logger = this.config.logger;\n\n // Call original handler with all args\n return await originalHandler(...allArgs);\n };\n\n return {\n ...module,\n handler: wrappedHandler,\n } as T;\n };\n\n return {\n // Use structural constraints to avoid contravariance check on handler type.\n // wrapAtPosition is now generic and returns T directly.\n withToolAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 1),\n withResourceAuth: <T extends { name: string; template?: unknown; config?: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 2),\n withPromptAuth: <T extends { name: string; config: unknown; handler: unknown }>(module: T) => wrapAtPosition(module, 0),\n };\n }\n}\n"],"names":["ErrorCode","McpError","fetchWithTimeout","DcrOAuthProvider","toAuthProvider","tokens","currentTokens","getAccessToken","isTokenValid","accessToken","refreshToken","refreshedTokens","refreshAccessToken","error","Error","message","String","expiresAt","Date","now","clientId","clientSecret","tenantId","scope","tokenUrl","customTokenUrl","config","params","refresh_token","client_id","grant_type","client_secret","body","URLSearchParams","response","method","headers","toString","ok","errorText","text","status","tokenResponse","json","access_token","expires_in","undefined","getUserEmail","userInfo","cacheKey","cached","emailCache","get","email","auth","Authorization","mail","userPrincipalName","set","authMiddleware","wrapAtPosition","module","extraPosition","originalHandler","handler","wrappedHandler","allArgs","extra","bearerToken","authInfo","token","requestInfo","authHeader","authorization","headerValue","Array","isArray","match","exec","InvalidRequest","verifyResponse","verifyEndpoint","verifyData","accountId","providerTokens","InternalError","authContext","logger","withToolAuth","withResourceAuth","withPromptAuth","Map"],"mappings":"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"}