@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,179 @@
1
+ /**
2
+ * Device Code OAuth Implementation for Microsoft
3
+ *
4
+ * Implements OAuth 2.0 Device Authorization Grant (RFC 8628) for headless/limited-input devices.
5
+ * Designed for scenarios where interactive browser flows are impractical (SSH sessions, CI/CD, etc.).
6
+ *
7
+ * Flow:
8
+ * 1. Request device code from Microsoft endpoint
9
+ * 2. Display user_code and verification_uri to user
10
+ * 3. Poll token endpoint until user completes authentication
11
+ * 4. Cache access token + refresh token to storage
12
+ * 5. Refresh tokens when expired
13
+ *
14
+ * Similar to service accounts in usage pattern: single static identity, minimal account management.
15
+ */
16
+ import { type OAuth2TokenStorageProvider } from '@mcp-z/oauth';
17
+ import type { Keyv } from 'keyv';
18
+ import type { Logger, MicrosoftService } from '../types.js';
19
+ /**
20
+ * Device Code Provider Configuration
21
+ */
22
+ export interface DeviceCodeConfig {
23
+ /** Microsoft service type (e.g., 'outlook') */
24
+ service: MicrosoftService;
25
+ /** Azure AD client ID */
26
+ clientId: string;
27
+ /** Azure AD tenant ID */
28
+ tenantId: string;
29
+ /** OAuth scopes to request (space-separated string or array) */
30
+ scope: string;
31
+ /** Logger instance */
32
+ logger: Logger;
33
+ /** Token storage for caching */
34
+ tokenStore: Keyv<unknown>;
35
+ /** Headless mode - print device code instead of opening browser */
36
+ headless: boolean;
37
+ }
38
+ /**
39
+ * DeviceCodeProvider implements OAuth2TokenStorageProvider using Microsoft Device Code Flow
40
+ *
41
+ * This provider:
42
+ * - Initiates device code flow with Microsoft endpoint
43
+ * - Displays user_code and verification_uri for manual authentication
44
+ * - Polls token endpoint until user completes auth
45
+ * - Stores access tokens + refresh tokens in Keyv storage
46
+ * - Refreshes tokens when expired
47
+ * - Provides single static identity (minimal account management like service accounts)
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const provider = new DeviceCodeProvider({
52
+ * service: 'outlook',
53
+ * clientId: 'your-client-id',
54
+ * tenantId: 'common',
55
+ * scope: 'https://graph.microsoft.com/Mail.Read',
56
+ * logger: console,
57
+ * tokenStore: new Keyv(),
58
+ * headless: true,
59
+ * });
60
+ *
61
+ * // Get authenticated Microsoft Graph client
62
+ * const token = await provider.getAccessToken('default');
63
+ * ```
64
+ */
65
+ export declare class DeviceCodeProvider implements OAuth2TokenStorageProvider {
66
+ private config;
67
+ constructor(config: DeviceCodeConfig);
68
+ /**
69
+ * Start device code flow and poll for token
70
+ *
71
+ * 1. POST to /devicecode endpoint to get device_code and user_code
72
+ * 2. Display verification instructions to user
73
+ * 3. Poll /token endpoint every interval seconds
74
+ * 4. Handle authorization_pending, slow_down, expired_token errors
75
+ * 5. Return token when user completes authentication
76
+ */
77
+ private startDeviceCodeFlow;
78
+ /**
79
+ * Poll Microsoft token endpoint until user completes authentication
80
+ *
81
+ * Handles Microsoft-specific error codes:
82
+ * - authorization_pending: User hasn't completed auth yet, keep polling
83
+ * - slow_down: Increase polling interval by 5 seconds
84
+ * - authorization_declined: User denied authorization
85
+ * - expired_token: Device code expired (typically after 15 minutes)
86
+ */
87
+ private pollForToken;
88
+ /**
89
+ * Refresh expired access token using refresh token
90
+ *
91
+ * @param refreshToken - Refresh token from previous authentication
92
+ * @returns New cached token with fresh access token
93
+ */
94
+ private refreshAccessToken;
95
+ /**
96
+ * Check if token is still valid (not expired)
97
+ */
98
+ private isTokenValid;
99
+ /**
100
+ * Get access token for Microsoft Graph API
101
+ *
102
+ * Flow:
103
+ * 1. Check token storage
104
+ * 2. If valid token exists, return it
105
+ * 3. If expired but has refresh token, try refresh
106
+ * 4. Otherwise, start new device code flow
107
+ *
108
+ * @param accountId - Account identifier. Defaults to 'device-code' (fixed identifier for device code flow).
109
+ * @returns Access token for API requests
110
+ */
111
+ getAccessToken(accountId?: string): Promise<string>;
112
+ /**
113
+ * Get user email from Microsoft Graph /me endpoint (pure query)
114
+ *
115
+ * @param accountId - Account identifier
116
+ * @returns User's email address (userPrincipalName or mail field)
117
+ */
118
+ getUserEmail(accountId?: string): Promise<string>;
119
+ /**
120
+ * Create auth provider for Microsoft Graph SDK integration
121
+ *
122
+ * Device code provider ALWAYS uses fixed accountId='device-code'
123
+ * This is by design - device code is a single static identity pattern
124
+ *
125
+ * @param accountId - Account identifier (must be 'device-code' or undefined, otherwise throws error)
126
+ * @returns Auth provider with getAccessToken method
127
+ */
128
+ toAuthProvider(accountId?: string): {
129
+ getAccessToken: () => Promise<string>;
130
+ };
131
+ /**
132
+ * Create Microsoft Graph AuthenticationProvider for SDK usage
133
+ *
134
+ * @param accountId - Account identifier
135
+ * @returns AuthenticationProvider that provides access tokens
136
+ */
137
+ private createAuthProvider;
138
+ /**
139
+ * Create middleware wrapper for single-user authentication
140
+ *
141
+ * Middleware wraps tool, resource, and prompt handlers and injects authContext into extra parameter.
142
+ * Handlers receive MicrosoftAuthProvider via extra.authContext.auth for API calls.
143
+ *
144
+ * @returns Object with withToolAuth, withResourceAuth, withPromptAuth methods
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * // Server registration
149
+ * const middleware = provider.authMiddleware();
150
+ * const tools = toolFactories.map(f => f()).map(middleware.withToolAuth);
151
+ * const resources = resourceFactories.map(f => f()).map(middleware.withResourceAuth);
152
+ * const prompts = promptFactories.map(f => f()).map(middleware.withPromptAuth);
153
+ *
154
+ * // Tool handler receives auth
155
+ * async function handler({ id }: In, extra: EnrichedExtra) {
156
+ * // extra.authContext.auth is MicrosoftAuthProvider (from middleware)
157
+ * const graph = Client.initWithMiddleware({ authProvider: extra.authContext.auth });
158
+ * }
159
+ * ```
160
+ */
161
+ authMiddleware(): {
162
+ withToolAuth: <T extends {
163
+ name: string;
164
+ config: unknown;
165
+ handler: unknown;
166
+ }>(module: T) => T;
167
+ withResourceAuth: <T extends {
168
+ name: string;
169
+ template?: unknown;
170
+ config?: unknown;
171
+ handler: unknown;
172
+ }>(module: T) => T;
173
+ withPromptAuth: <T extends {
174
+ name: string;
175
+ config: unknown;
176
+ handler: unknown;
177
+ }>(module: T) => T;
178
+ };
179
+ }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Device Code OAuth Implementation for Microsoft
3
+ *
4
+ * Implements OAuth 2.0 Device Authorization Grant (RFC 8628) for headless/limited-input devices.
5
+ * Designed for scenarios where interactive browser flows are impractical (SSH sessions, CI/CD, etc.).
6
+ *
7
+ * Flow:
8
+ * 1. Request device code from Microsoft endpoint
9
+ * 2. Display user_code and verification_uri to user
10
+ * 3. Poll token endpoint until user completes authentication
11
+ * 4. Cache access token + refresh token to storage
12
+ * 5. Refresh tokens when expired
13
+ *
14
+ * Similar to service accounts in usage pattern: single static identity, minimal account management.
15
+ */
16
+ import { type OAuth2TokenStorageProvider } from '@mcp-z/oauth';
17
+ import type { Keyv } from 'keyv';
18
+ import type { Logger, MicrosoftService } from '../types.js';
19
+ /**
20
+ * Device Code Provider Configuration
21
+ */
22
+ export interface DeviceCodeConfig {
23
+ /** Microsoft service type (e.g., 'outlook') */
24
+ service: MicrosoftService;
25
+ /** Azure AD client ID */
26
+ clientId: string;
27
+ /** Azure AD tenant ID */
28
+ tenantId: string;
29
+ /** OAuth scopes to request (space-separated string or array) */
30
+ scope: string;
31
+ /** Logger instance */
32
+ logger: Logger;
33
+ /** Token storage for caching */
34
+ tokenStore: Keyv<unknown>;
35
+ /** Headless mode - print device code instead of opening browser */
36
+ headless: boolean;
37
+ }
38
+ /**
39
+ * DeviceCodeProvider implements OAuth2TokenStorageProvider using Microsoft Device Code Flow
40
+ *
41
+ * This provider:
42
+ * - Initiates device code flow with Microsoft endpoint
43
+ * - Displays user_code and verification_uri for manual authentication
44
+ * - Polls token endpoint until user completes auth
45
+ * - Stores access tokens + refresh tokens in Keyv storage
46
+ * - Refreshes tokens when expired
47
+ * - Provides single static identity (minimal account management like service accounts)
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const provider = new DeviceCodeProvider({
52
+ * service: 'outlook',
53
+ * clientId: 'your-client-id',
54
+ * tenantId: 'common',
55
+ * scope: 'https://graph.microsoft.com/Mail.Read',
56
+ * logger: console,
57
+ * tokenStore: new Keyv(),
58
+ * headless: true,
59
+ * });
60
+ *
61
+ * // Get authenticated Microsoft Graph client
62
+ * const token = await provider.getAccessToken('default');
63
+ * ```
64
+ */
65
+ export declare class DeviceCodeProvider implements OAuth2TokenStorageProvider {
66
+ private config;
67
+ constructor(config: DeviceCodeConfig);
68
+ /**
69
+ * Start device code flow and poll for token
70
+ *
71
+ * 1. POST to /devicecode endpoint to get device_code and user_code
72
+ * 2. Display verification instructions to user
73
+ * 3. Poll /token endpoint every interval seconds
74
+ * 4. Handle authorization_pending, slow_down, expired_token errors
75
+ * 5. Return token when user completes authentication
76
+ */
77
+ private startDeviceCodeFlow;
78
+ /**
79
+ * Poll Microsoft token endpoint until user completes authentication
80
+ *
81
+ * Handles Microsoft-specific error codes:
82
+ * - authorization_pending: User hasn't completed auth yet, keep polling
83
+ * - slow_down: Increase polling interval by 5 seconds
84
+ * - authorization_declined: User denied authorization
85
+ * - expired_token: Device code expired (typically after 15 minutes)
86
+ */
87
+ private pollForToken;
88
+ /**
89
+ * Refresh expired access token using refresh token
90
+ *
91
+ * @param refreshToken - Refresh token from previous authentication
92
+ * @returns New cached token with fresh access token
93
+ */
94
+ private refreshAccessToken;
95
+ /**
96
+ * Check if token is still valid (not expired)
97
+ */
98
+ private isTokenValid;
99
+ /**
100
+ * Get access token for Microsoft Graph API
101
+ *
102
+ * Flow:
103
+ * 1. Check token storage
104
+ * 2. If valid token exists, return it
105
+ * 3. If expired but has refresh token, try refresh
106
+ * 4. Otherwise, start new device code flow
107
+ *
108
+ * @param accountId - Account identifier. Defaults to 'device-code' (fixed identifier for device code flow).
109
+ * @returns Access token for API requests
110
+ */
111
+ getAccessToken(accountId?: string): Promise<string>;
112
+ /**
113
+ * Get user email from Microsoft Graph /me endpoint (pure query)
114
+ *
115
+ * @param accountId - Account identifier
116
+ * @returns User's email address (userPrincipalName or mail field)
117
+ */
118
+ getUserEmail(accountId?: string): Promise<string>;
119
+ /**
120
+ * Create auth provider for Microsoft Graph SDK integration
121
+ *
122
+ * Device code provider ALWAYS uses fixed accountId='device-code'
123
+ * This is by design - device code is a single static identity pattern
124
+ *
125
+ * @param accountId - Account identifier (must be 'device-code' or undefined, otherwise throws error)
126
+ * @returns Auth provider with getAccessToken method
127
+ */
128
+ toAuthProvider(accountId?: string): {
129
+ getAccessToken: () => Promise<string>;
130
+ };
131
+ /**
132
+ * Create Microsoft Graph AuthenticationProvider for SDK usage
133
+ *
134
+ * @param accountId - Account identifier
135
+ * @returns AuthenticationProvider that provides access tokens
136
+ */
137
+ private createAuthProvider;
138
+ /**
139
+ * Create middleware wrapper for single-user authentication
140
+ *
141
+ * Middleware wraps tool, resource, and prompt handlers and injects authContext into extra parameter.
142
+ * Handlers receive MicrosoftAuthProvider via extra.authContext.auth for API calls.
143
+ *
144
+ * @returns Object with withToolAuth, withResourceAuth, withPromptAuth methods
145
+ *
146
+ * @example
147
+ * ```typescript
148
+ * // Server registration
149
+ * const middleware = provider.authMiddleware();
150
+ * const tools = toolFactories.map(f => f()).map(middleware.withToolAuth);
151
+ * const resources = resourceFactories.map(f => f()).map(middleware.withResourceAuth);
152
+ * const prompts = promptFactories.map(f => f()).map(middleware.withPromptAuth);
153
+ *
154
+ * // Tool handler receives auth
155
+ * async function handler({ id }: In, extra: EnrichedExtra) {
156
+ * // extra.authContext.auth is MicrosoftAuthProvider (from middleware)
157
+ * const graph = Client.initWithMiddleware({ authProvider: extra.authContext.auth });
158
+ * }
159
+ * ```
160
+ */
161
+ authMiddleware(): {
162
+ withToolAuth: <T extends {
163
+ name: string;
164
+ config: unknown;
165
+ handler: unknown;
166
+ }>(module: T) => T;
167
+ withResourceAuth: <T extends {
168
+ name: string;
169
+ template?: unknown;
170
+ config?: unknown;
171
+ handler: unknown;
172
+ }>(module: T) => T;
173
+ withPromptAuth: <T extends {
174
+ name: string;
175
+ config: unknown;
176
+ handler: unknown;
177
+ }>(module: T) => T;
178
+ };
179
+ }