mcp-oauth-provider 0.0.2 → 0.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-oauth-provider",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "MCP OAuth client provider for streamable HTTP clients",
5
5
  "author": "Clutch Creator",
6
6
  "license": "MIT",
@@ -97,34 +97,6 @@ describe('MCPOAuthClientProvider Integration', () => {
97
97
  });
98
98
  });
99
99
 
100
- describe('authorization server metadata', () => {
101
- test('should accept authorizationServerMetadata through constructor', () => {
102
- const metadata = {
103
- issuer: 'https://auth.example.com',
104
- authorization_endpoint: 'https://auth.example.com/authorize',
105
- token_endpoint: 'https://auth.example.com/token',
106
- };
107
- const provider = new MCPOAuthClientProvider({
108
- ...config,
109
- authorizationServerMetadata: metadata,
110
- });
111
-
112
- expect(provider.authorizationServerMetadata).toBeDefined();
113
- expect(provider.authorizationServerMetadata?.issuer).toBe(
114
- 'https://auth.example.com'
115
- );
116
- expect(provider.authorizationServerMetadata?.token_endpoint).toBe(
117
- 'https://auth.example.com/token'
118
- );
119
- });
120
-
121
- test('should be undefined when not provided', () => {
122
- const provider = new MCPOAuthClientProvider(config);
123
-
124
- expect(provider.authorizationServerMetadata).toBeUndefined();
125
- });
126
- });
127
-
128
100
  describe('token management', () => {
129
101
  test('should return undefined when no tokens exist', async () => {
130
102
  const provider = new MCPOAuthClientProvider(config);
@@ -49,7 +49,7 @@ export interface OAuthConfig {
49
49
  /**
50
50
  * Redirect URI for OAuth callbacks
51
51
  */
52
- redirectUri: string;
52
+ redirectUri?: string;
53
53
 
54
54
  /**
55
55
  * OAuth scope to request
@@ -76,6 +76,11 @@ export interface OAuthConfig {
76
76
  */
77
77
  tokens?: OAuthTokens;
78
78
 
79
+ /**
80
+ * Token refresh endpoint URL (optional, can be obtained from metadata)
81
+ */
82
+ tokenEndpoint?: string;
83
+
79
84
  /**
80
85
  * Token refresh configuration
81
86
  */
@@ -90,16 +95,6 @@ export interface OAuthConfig {
90
95
  */
91
96
  retryDelay?: number;
92
97
  };
93
-
94
- /**
95
- * Authorization server metadata (can be provided or obtained during auth flow)
96
- */
97
- authorizationServerMetadata?: {
98
- issuer: string;
99
- authorization_endpoint: string;
100
- token_endpoint: string;
101
- [key: string]: unknown;
102
- };
103
98
  }
104
99
 
105
100
  /**
@@ -26,35 +26,44 @@ interface TokenRefreshConfig {
26
26
  /**
27
27
  * MCP OAuth Client Provider implementation with automatic token refresh
28
28
  */
29
- interface ProcessedOAuthConfig
30
- extends Omit<
31
- Required<OAuthConfig>,
32
- 'clientSecret' | 'tokens' | 'authorizationServerMetadata'
33
- > {
34
- clientSecret?: string;
35
- tokens?: OAuthTokens;
36
- clientMetadata: OAuthClientMetadata;
37
- tokenRefresh: TokenRefreshConfig;
38
- }
39
-
40
29
  export class MCPOAuthClientProvider implements OAuthClientProvider {
41
- private readonly config: ProcessedOAuthConfig;
42
30
  private readonly oauthStorage: OAuthStorage;
43
31
  private readonly sessionId: string;
32
+ private readonly redirectUri: string | undefined;
33
+ private readonly tokenRefreshConfig: TokenRefreshConfig;
34
+ private readonly _clientMetadata: OAuthClientMetadata;
44
35
  private cachedClientInformation?: OAuthClientInformationFull;
45
36
 
37
+ public tokenEndpoint?: string;
46
38
  public authorizationServerMetadata?: AuthorizationServerMetadata;
47
39
 
48
40
  constructor(config: OAuthConfig) {
49
41
  this.sessionId = config.sessionId ?? generateSessionId();
50
42
  const storage = config.storage ?? new MemoryStorage();
51
43
 
52
- // Set authorization server metadata if provided
53
- if (config.authorizationServerMetadata) {
54
- this.authorizationServerMetadata =
55
- config.authorizationServerMetadata as AuthorizationServerMetadata;
44
+ // Set token endpoint if provided
45
+ if (config.tokenEndpoint) {
46
+ this.tokenEndpoint = config.tokenEndpoint;
56
47
  }
57
48
 
49
+ // Store redirect URI
50
+ this.redirectUri = config.redirectUri;
51
+
52
+ // Store token refresh configuration
53
+ this.tokenRefreshConfig = {
54
+ maxRetries: 3,
55
+ retryDelay: 1000,
56
+ ...(config.tokenRefresh ?? {}),
57
+ };
58
+
59
+ // Store client metadata
60
+ this._clientMetadata = {
61
+ ...DEFAULT_CLIENT_METADATA,
62
+ ...(config.clientMetadata ?? {}),
63
+ redirect_uris: config.redirectUri ? [config.redirectUri] : [], // Always required
64
+ scope: config.scope ?? DEFAULT_CLIENT_METADATA.scope,
65
+ };
66
+
58
67
  this.oauthStorage = new OAuthStorage(storage, this.sessionId, {
59
68
  staticClientInfo: config.clientId
60
69
  ? {
@@ -64,42 +73,24 @@ export class MCPOAuthClientProvider implements OAuthClientProvider {
64
73
  : undefined,
65
74
  initialTokens: config.tokens,
66
75
  });
67
-
68
- // Merge with defaults
69
- this.config = {
70
- clientId: config.clientId ?? '',
71
- clientSecret: config.clientSecret ?? undefined,
72
- redirectUri: config.redirectUri,
73
- scope: config.scope ?? 'read write',
74
- sessionId: this.sessionId,
75
- storage,
76
- tokens: config.tokens,
77
- clientMetadata: {
78
- ...DEFAULT_CLIENT_METADATA,
79
- ...(config.clientMetadata ?? {}),
80
- redirect_uris: [config.redirectUri], // Always required
81
- scope: config.scope ?? DEFAULT_CLIENT_METADATA.scope,
82
- },
83
- tokenRefresh: {
84
- maxRetries: 3,
85
- retryDelay: 1000,
86
- ...(config.tokenRefresh ?? {}),
87
- },
88
- };
89
76
  }
90
77
 
91
78
  /**
92
79
  * The URL to redirect the user agent to after authorization
93
80
  */
94
81
  get redirectUrl(): string | URL {
95
- return this.config.redirectUri;
82
+ if (!this.redirectUri) {
83
+ throw new Error('No redirect URI configured for this OAuth client');
84
+ }
85
+
86
+ return this.redirectUri;
96
87
  }
97
88
 
98
89
  /**
99
90
  * Metadata about this OAuth client
100
91
  */
101
92
  get clientMetadata(): OAuthClientMetadata {
102
- return this.config.clientMetadata;
93
+ return this._clientMetadata;
103
94
  }
104
95
 
105
96
  /**
@@ -144,11 +135,7 @@ export class MCPOAuthClientProvider implements OAuthClientProvider {
144
135
  const needsRefresh = areTokensExpired(currentTokens, undefined, 300);
145
136
 
146
137
  // If tokens need refresh and we have the server metadata and refresh token, refresh automatically
147
- if (
148
- needsRefresh &&
149
- currentTokens.refresh_token &&
150
- this.authorizationServerMetadata?.token_endpoint
151
- ) {
138
+ if (needsRefresh && currentTokens.refresh_token && this.tokenEndpoint) {
152
139
  try {
153
140
  // Use the token endpoint from authorization server metadata
154
141
  const newTokens = await this.refreshTokens();
@@ -172,13 +159,13 @@ export class MCPOAuthClientProvider implements OAuthClientProvider {
172
159
  * @throws Error if no authorization server metadata is available
173
160
  */
174
161
  async refreshTokens(): Promise<OAuthTokens> {
175
- if (!this.authorizationServerMetadata?.token_endpoint) {
162
+ if (!this.tokenEndpoint) {
176
163
  throw new Error(
177
- 'No authorization server metadata available. Cannot refresh tokens without token_endpoint.'
164
+ 'No token endpoint available. Cannot refresh tokens without token_endpoint.'
178
165
  );
179
166
  }
180
167
 
181
- const { maxRetries, retryDelay } = this.config.tokenRefresh;
168
+ const { maxRetries, retryDelay } = this.tokenRefreshConfig;
182
169
 
183
170
  const currentTokens = await this.oauthStorage.getTokens();
184
171
 
@@ -195,7 +182,7 @@ export class MCPOAuthClientProvider implements OAuthClientProvider {
195
182
  try {
196
183
  // Use helper function for retry logic with token endpoint
197
184
  const newTokens = await refreshTokensWithRetry(
198
- this.authorizationServerMetadata.token_endpoint,
185
+ this.tokenEndpoint,
199
186
  clientInfo,
200
187
  currentTokens.refresh_token,
201
188
  this.addClientAuthentication,
@@ -283,6 +270,7 @@ export class MCPOAuthClientProvider implements OAuthClientProvider {
283
270
  const clientInfo = this.cachedClientInformation;
284
271
 
285
272
  this.authorizationServerMetadata = metadata;
273
+ this.tokenEndpoint = metadata?.token_endpoint;
286
274
 
287
275
  if (!clientInfo) {
288
276
  throw new Error('No client information available for authentication');