@prmichaelsen/mcp-auth 0.1.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.
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/auth/base-provider.js +173 -0
- package/dist/auth/base-provider.js.map +7 -0
- package/dist/auth/index.js +11 -0
- package/dist/auth/index.js.map +7 -0
- package/dist/auth/providers/env-provider.js +71 -0
- package/dist/auth/providers/env-provider.js.map +7 -0
- package/dist/auth/providers/index.js +11 -0
- package/dist/auth/providers/index.js.map +7 -0
- package/dist/auth/providers/simple-resolver.js +185 -0
- package/dist/auth/providers/simple-resolver.js.map +7 -0
- package/dist/auth/types.js +1 -0
- package/dist/auth/types.js.map +7 -0
- package/dist/index.js +111 -0
- package/dist/index.js.map +7 -0
- package/dist/server/config.js +1 -0
- package/dist/server/config.js.map +7 -0
- package/dist/server/decorators.js +149 -0
- package/dist/server/decorators.js.map +7 -0
- package/dist/server/index.js +25 -0
- package/dist/server/index.js.map +7 -0
- package/dist/server/mcp-server.js +269 -0
- package/dist/server/mcp-server.js.map +7 -0
- package/dist/server/tool.js +66 -0
- package/dist/server/tool.js.map +7 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +7 -0
- package/dist/utils/errors.js +143 -0
- package/dist/utils/errors.js.map +7 -0
- package/dist/utils/index.js +81 -0
- package/dist/utils/index.js.map +7 -0
- package/dist/utils/logger.js +200 -0
- package/dist/utils/logger.js.map +7 -0
- package/dist/utils/validation.js +172 -0
- package/dist/utils/validation.js.map +7 -0
- package/dist/wrapper/config.js +1 -0
- package/dist/wrapper/config.js.map +7 -0
- package/dist/wrapper/index.js +10 -0
- package/dist/wrapper/index.js.map +7 -0
- package/dist/wrapper/server-wrapper.js +427 -0
- package/dist/wrapper/server-wrapper.js.map +7 -0
- package/package.json +93 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 prmichaelsen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# @prmichaelsen/mcp-auth
|
|
2
|
+
|
|
3
|
+
Authentication and multi-tenancy framework for MCP (Model Context Protocol) servers.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`@prmichaelsen/mcp-auth` provides a pluggable authentication system for MCP servers, enabling:
|
|
8
|
+
|
|
9
|
+
- **Multi-tenancy**: Multiple users with separate resource tokens
|
|
10
|
+
- **Auth-agnostic**: Support for JWT, OAuth, API Keys, or custom auth schemes
|
|
11
|
+
- **Transport-agnostic**: Works with stdio, HTTP, and SSE transports
|
|
12
|
+
- **Type-safe**: Full TypeScript support
|
|
13
|
+
- **Composable**: Middleware for rate limiting, logging, etc.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @prmichaelsen/mcp-auth @modelcontextprotocol/sdk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### Simple Function-Based Tool
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { withAuth, AuthenticatedMCPServer } from '@prmichaelsen/mcp-auth';
|
|
27
|
+
import { EnvAuthProvider, SimpleTokenResolver } from '@prmichaelsen/mcp-auth/providers/env';
|
|
28
|
+
|
|
29
|
+
// Setup auth provider
|
|
30
|
+
const authProvider = new EnvAuthProvider();
|
|
31
|
+
const tokenResolver = new SimpleTokenResolver({ tokenEnvVar: 'API_TOKEN' });
|
|
32
|
+
|
|
33
|
+
// Create server
|
|
34
|
+
const server = new AuthenticatedMCPServer({
|
|
35
|
+
name: 'my-mcp-server',
|
|
36
|
+
authProvider,
|
|
37
|
+
tokenResolver,
|
|
38
|
+
resourceType: 'myapi',
|
|
39
|
+
transport: { type: 'stdio' }
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Register tool with automatic auth
|
|
43
|
+
server.registerTool('get_data', withAuth(async (args, accessToken, userId) => {
|
|
44
|
+
// accessToken and userId are automatically injected
|
|
45
|
+
const client = new MyAPIClient(accessToken);
|
|
46
|
+
return client.getData(args);
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
await server.start();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Class-Based Tool
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { Tool, AuthenticatedTool } from '@prmichaelsen/mcp-auth';
|
|
56
|
+
|
|
57
|
+
class GetDataTool implements Tool {
|
|
58
|
+
name = 'get_data';
|
|
59
|
+
description = 'Fetch data from API';
|
|
60
|
+
|
|
61
|
+
async execute(args, accessToken, userId) {
|
|
62
|
+
const client = new MyAPIClient(accessToken);
|
|
63
|
+
return client.getData(args);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
server.registerTool(new AuthenticatedTool(new GetDataTool()));
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Authentication Providers
|
|
71
|
+
|
|
72
|
+
### Environment Variable (Simple)
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { EnvAuthProvider, SimpleTokenResolver } from '@prmichaelsen/mcp-auth/providers/env';
|
|
76
|
+
|
|
77
|
+
const authProvider = new EnvAuthProvider();
|
|
78
|
+
const tokenResolver = new SimpleTokenResolver({ tokenEnvVar: 'API_TOKEN' });
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### JWT (Multi-tenant)
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { JWTAuthProvider } from '@prmichaelsen/mcp-auth/providers/jwt';
|
|
85
|
+
|
|
86
|
+
const authProvider = new JWTAuthProvider({
|
|
87
|
+
jwtSecret: process.env.JWT_SECRET,
|
|
88
|
+
database: {
|
|
89
|
+
host: 'localhost',
|
|
90
|
+
database: 'users'
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### OAuth 2.0
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { OAuthProvider } from '@prmichaelsen/mcp-auth/providers/oauth';
|
|
99
|
+
|
|
100
|
+
const authProvider = new OAuthProvider({
|
|
101
|
+
authorizationUrl: 'https://auth.example.com/authorize',
|
|
102
|
+
tokenUrl: 'https://auth.example.com/token',
|
|
103
|
+
clientId: process.env.OAUTH_CLIENT_ID,
|
|
104
|
+
clientSecret: process.env.OAUTH_CLIENT_SECRET
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Custom Provider
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { AuthProvider, AuthResult, RequestContext } from '@prmichaelsen/mcp-auth';
|
|
112
|
+
|
|
113
|
+
class CustomAuthProvider implements AuthProvider {
|
|
114
|
+
async authenticate(context: RequestContext): Promise<AuthResult> {
|
|
115
|
+
// Your custom auth logic
|
|
116
|
+
const apiKey = context.headers?.['x-api-key'];
|
|
117
|
+
|
|
118
|
+
if (!apiKey) {
|
|
119
|
+
return { authenticated: false, error: 'No API key' };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Validate and return user ID
|
|
123
|
+
return {
|
|
124
|
+
authenticated: true,
|
|
125
|
+
userId: 'user-123'
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Middleware Composition
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { compose, withAuth, withRateLimit, withLogging } from '@prmichaelsen/mcp-auth';
|
|
135
|
+
|
|
136
|
+
const getTool = compose(
|
|
137
|
+
withLogging(),
|
|
138
|
+
withRateLimit({ maxRequests: 100, windowMs: 60000 }),
|
|
139
|
+
withAuth(),
|
|
140
|
+
async (args, accessToken, userId) => {
|
|
141
|
+
// Your tool logic
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
server.registerTool('get_data', getTool);
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Transports
|
|
149
|
+
|
|
150
|
+
### Stdio (Local)
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
const server = new AuthenticatedMCPServer({
|
|
154
|
+
// ...
|
|
155
|
+
transport: { type: 'stdio' }
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### HTTP/SSE (Remote)
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const server = new AuthenticatedMCPServer({
|
|
163
|
+
// ...
|
|
164
|
+
transport: {
|
|
165
|
+
type: 'sse',
|
|
166
|
+
port: 3000,
|
|
167
|
+
host: '0.0.0.0',
|
|
168
|
+
basePath: '/mcp'
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Documentation
|
|
174
|
+
|
|
175
|
+
See the [`agent/`](./agent/) directory for detailed architecture documentation:
|
|
176
|
+
|
|
177
|
+
- [`multi-tenant-architecture.md`](./agent/multi-tenant-architecture.md) - Overall architecture and auth schemes
|
|
178
|
+
- [`lib-structure.md`](./agent/lib-structure.md) - Package structure and design
|
|
179
|
+
- [`tool-patterns.md`](./agent/tool-patterns.md) - Tool registration patterns
|
|
180
|
+
|
|
181
|
+
## Examples
|
|
182
|
+
|
|
183
|
+
See the [`examples/`](./examples/) directory for complete examples:
|
|
184
|
+
|
|
185
|
+
- `simple-stdio/` - Single-user stdio server
|
|
186
|
+
- `jwt-multi-tenant/` - Multi-tenant JWT server
|
|
187
|
+
- `oauth-server/` - OAuth authentication flow
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
MIT
|
|
192
|
+
|
|
193
|
+
## Contributing
|
|
194
|
+
|
|
195
|
+
Contributions welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for details.
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger.js";
|
|
2
|
+
class BaseAuthProvider {
|
|
3
|
+
config;
|
|
4
|
+
logger;
|
|
5
|
+
authCache;
|
|
6
|
+
constructor(config = {}) {
|
|
7
|
+
this.config = {
|
|
8
|
+
errorMessages: {
|
|
9
|
+
noAuth: "No authentication credentials provided",
|
|
10
|
+
invalidAuth: "Invalid authentication credentials",
|
|
11
|
+
expiredAuth: "Authentication credentials have expired",
|
|
12
|
+
...config.errorMessages
|
|
13
|
+
},
|
|
14
|
+
cacheResults: config.cacheResults ?? false,
|
|
15
|
+
cacheTtl: config.cacheTtl ?? 6e4
|
|
16
|
+
// 1 minute default
|
|
17
|
+
};
|
|
18
|
+
this.logger = createLogger({ enabled: true, level: "info" });
|
|
19
|
+
this.authCache = /* @__PURE__ */ new Map();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Authenticate a request
|
|
23
|
+
* Implements caching if enabled
|
|
24
|
+
*/
|
|
25
|
+
async authenticate(context) {
|
|
26
|
+
try {
|
|
27
|
+
const cacheKey = this.getCacheKey(context);
|
|
28
|
+
if (this.config.cacheResults && cacheKey) {
|
|
29
|
+
const cached = this.authCache.get(cacheKey);
|
|
30
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
31
|
+
this.logger.debug("Authentication result retrieved from cache", {
|
|
32
|
+
cacheKey,
|
|
33
|
+
userId: cached.result.userId
|
|
34
|
+
});
|
|
35
|
+
return cached.result;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const result = await this.doAuthenticate(context);
|
|
39
|
+
if (result.authenticated && this.config.cacheResults && cacheKey) {
|
|
40
|
+
this.authCache.set(cacheKey, {
|
|
41
|
+
result,
|
|
42
|
+
expiresAt: Date.now() + (this.config.cacheTtl ?? 6e4)
|
|
43
|
+
});
|
|
44
|
+
this.logger.debug("Authentication result cached", {
|
|
45
|
+
cacheKey,
|
|
46
|
+
userId: result.userId,
|
|
47
|
+
ttl: this.config.cacheTtl
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (result.authenticated) {
|
|
51
|
+
this.logger.info("Authentication successful", {
|
|
52
|
+
userId: result.userId,
|
|
53
|
+
transport: context.transport
|
|
54
|
+
});
|
|
55
|
+
} else {
|
|
56
|
+
this.logger.warn("Authentication failed", {
|
|
57
|
+
error: result.error,
|
|
58
|
+
transport: context.transport
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
this.logger.error("Authentication error", error, {
|
|
64
|
+
transport: context.transport
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
authenticated: false,
|
|
68
|
+
error: error instanceof Error ? error.message : "Authentication failed"
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Generate cache key from request context
|
|
74
|
+
* Override this to customize caching behavior
|
|
75
|
+
*/
|
|
76
|
+
getCacheKey(context) {
|
|
77
|
+
const authHeader = context.headers?.["authorization"];
|
|
78
|
+
if (typeof authHeader === "string") {
|
|
79
|
+
return authHeader;
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Extract authorization header from context
|
|
85
|
+
*/
|
|
86
|
+
getAuthorizationHeader(context) {
|
|
87
|
+
const authHeader = context.headers?.["authorization"];
|
|
88
|
+
if (typeof authHeader === "string") {
|
|
89
|
+
return authHeader;
|
|
90
|
+
}
|
|
91
|
+
if (Array.isArray(authHeader) && authHeader.length > 0) {
|
|
92
|
+
return authHeader[0];
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Extract bearer token from authorization header
|
|
98
|
+
*/
|
|
99
|
+
extractBearerToken(context) {
|
|
100
|
+
const authHeader = this.getAuthorizationHeader(context);
|
|
101
|
+
if (!authHeader) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
const parts = authHeader.split(" ");
|
|
105
|
+
if (parts.length === 2 && parts[0].toLowerCase() === "bearer") {
|
|
106
|
+
return parts[1];
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Create authentication failure result
|
|
112
|
+
*/
|
|
113
|
+
createFailureResult(error) {
|
|
114
|
+
return {
|
|
115
|
+
authenticated: false,
|
|
116
|
+
error
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Create authentication success result
|
|
121
|
+
*/
|
|
122
|
+
createSuccessResult(userId, metadata) {
|
|
123
|
+
return {
|
|
124
|
+
authenticated: true,
|
|
125
|
+
userId,
|
|
126
|
+
metadata
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Optional: Initialize the provider
|
|
131
|
+
*/
|
|
132
|
+
async initialize() {
|
|
133
|
+
this.logger.info("Authentication provider initialized", {
|
|
134
|
+
provider: this.constructor.name,
|
|
135
|
+
cacheEnabled: this.config.cacheResults
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Optional: Cleanup resources
|
|
140
|
+
*/
|
|
141
|
+
async cleanup() {
|
|
142
|
+
this.authCache.clear();
|
|
143
|
+
this.logger.info("Authentication provider cleaned up", {
|
|
144
|
+
provider: this.constructor.name
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Optional: Validate provider configuration
|
|
149
|
+
*/
|
|
150
|
+
async validate() {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Clear authentication cache
|
|
155
|
+
*/
|
|
156
|
+
clearCache() {
|
|
157
|
+
this.authCache.clear();
|
|
158
|
+
this.logger.debug("Authentication cache cleared");
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Get cache statistics
|
|
162
|
+
*/
|
|
163
|
+
getCacheStats() {
|
|
164
|
+
return {
|
|
165
|
+
size: this.authCache.size,
|
|
166
|
+
keys: Array.from(this.authCache.keys())
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
export {
|
|
171
|
+
BaseAuthProvider
|
|
172
|
+
};
|
|
173
|
+
//# sourceMappingURL=base-provider.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/auth/base-provider.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Base authentication provider implementation\n * \n * Provides common functionality for authentication providers.\n */\n\nimport type { AuthProvider, AuthProviderConfig } from './types.js';\nimport type { RequestContext, AuthResult } from '../types.js';\nimport { AuthenticationError } from '../utils/errors.js';\nimport { createLogger, type Logger } from '../utils/logger.js';\n\n/**\n * Abstract base class for authentication providers\n * \n * Provides common functionality like caching, logging, and error handling.\n * Extend this class to implement custom authentication logic.\n * \n * @example\n * ```typescript\n * class MyAuthProvider extends BaseAuthProvider {\n * protected async doAuthenticate(context: RequestContext): Promise<AuthResult> {\n * // Your authentication logic here\n * return { authenticated: true, userId: 'user-123' };\n * }\n * }\n * ```\n */\nexport abstract class BaseAuthProvider implements AuthProvider {\n protected config: AuthProviderConfig;\n protected logger: Logger;\n private authCache: Map<string, { result: AuthResult; expiresAt: number }>;\n \n constructor(config: AuthProviderConfig = {}) {\n this.config = {\n errorMessages: {\n noAuth: 'No authentication credentials provided',\n invalidAuth: 'Invalid authentication credentials',\n expiredAuth: 'Authentication credentials have expired',\n ...config.errorMessages\n },\n cacheResults: config.cacheResults ?? false,\n cacheTtl: config.cacheTtl ?? 60000 // 1 minute default\n };\n \n this.logger = createLogger({ enabled: true, level: 'info' });\n this.authCache = new Map();\n }\n \n /**\n * Authenticate a request\n * Implements caching if enabled\n */\n async authenticate(context: RequestContext): Promise<AuthResult> {\n try {\n // Generate cache key from context\n const cacheKey = this.getCacheKey(context);\n \n // Check cache if enabled\n if (this.config.cacheResults && cacheKey) {\n const cached = this.authCache.get(cacheKey);\n if (cached && cached.expiresAt > Date.now()) {\n this.logger.debug('Authentication result retrieved from cache', {\n cacheKey,\n userId: cached.result.userId\n });\n return cached.result;\n }\n }\n \n // Perform authentication\n const result = await this.doAuthenticate(context);\n \n // Cache result if successful and caching is enabled\n if (result.authenticated && this.config.cacheResults && cacheKey) {\n this.authCache.set(cacheKey, {\n result,\n expiresAt: Date.now() + (this.config.cacheTtl ?? 60000)\n });\n \n this.logger.debug('Authentication result cached', {\n cacheKey,\n userId: result.userId,\n ttl: this.config.cacheTtl\n });\n }\n \n // Log result\n if (result.authenticated) {\n this.logger.info('Authentication successful', {\n userId: result.userId,\n transport: context.transport\n });\n } else {\n this.logger.warn('Authentication failed', {\n error: result.error,\n transport: context.transport\n });\n }\n \n return result;\n \n } catch (error) {\n this.logger.error('Authentication error', error as Error, {\n transport: context.transport\n });\n \n return {\n authenticated: false,\n error: error instanceof Error ? error.message : 'Authentication failed'\n };\n }\n }\n \n /**\n * Abstract method to implement authentication logic\n * Override this in your provider implementation\n */\n protected abstract doAuthenticate(context: RequestContext): Promise<AuthResult>;\n \n /**\n * Generate cache key from request context\n * Override this to customize caching behavior\n */\n protected getCacheKey(context: RequestContext): string | null {\n // Default: use authorization header as cache key\n const authHeader = context.headers?.['authorization'];\n if (typeof authHeader === 'string') {\n return authHeader;\n }\n return null;\n }\n \n /**\n * Extract authorization header from context\n */\n protected getAuthorizationHeader(context: RequestContext): string | null {\n const authHeader = context.headers?.['authorization'];\n \n if (typeof authHeader === 'string') {\n return authHeader;\n }\n \n if (Array.isArray(authHeader) && authHeader.length > 0) {\n return authHeader[0];\n }\n \n return null;\n }\n \n /**\n * Extract bearer token from authorization header\n */\n protected extractBearerToken(context: RequestContext): string | null {\n const authHeader = this.getAuthorizationHeader(context);\n \n if (!authHeader) {\n return null;\n }\n \n const parts = authHeader.split(' ');\n if (parts.length === 2 && parts[0].toLowerCase() === 'bearer') {\n return parts[1];\n }\n \n return null;\n }\n \n /**\n * Create authentication failure result\n */\n protected createFailureResult(error: string): AuthResult {\n return {\n authenticated: false,\n error\n };\n }\n \n /**\n * Create authentication success result\n */\n protected createSuccessResult(\n userId: string,\n metadata?: Record<string, unknown>\n ): AuthResult {\n return {\n authenticated: true,\n userId,\n metadata\n };\n }\n \n /**\n * Optional: Initialize the provider\n */\n async initialize(): Promise<void> {\n this.logger.info('Authentication provider initialized', {\n provider: this.constructor.name,\n cacheEnabled: this.config.cacheResults\n });\n }\n \n /**\n * Optional: Cleanup resources\n */\n async cleanup(): Promise<void> {\n // Clear cache\n this.authCache.clear();\n \n this.logger.info('Authentication provider cleaned up', {\n provider: this.constructor.name\n });\n }\n \n /**\n * Optional: Validate provider configuration\n */\n async validate(): Promise<boolean> {\n // Base implementation always returns true\n // Override in subclasses to add validation logic\n return true;\n }\n \n /**\n * Clear authentication cache\n */\n clearCache(): void {\n this.authCache.clear();\n this.logger.debug('Authentication cache cleared');\n }\n \n /**\n * Get cache statistics\n */\n getCacheStats(): { size: number; keys: string[] } {\n return {\n size: this.authCache.size,\n keys: Array.from(this.authCache.keys())\n };\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/auth/index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Authentication module exports\n */\n\n// Types\nexport type {\n AuthProvider,\n ResourceTokenResolver,\n AuthenticatedContext,\n AuthProviderConfig,\n TokenResolverConfig\n} from './types.js';\n\n// Base provider\nexport { BaseAuthProvider } from './base-provider.js';\n\n// Providers\nexport {\n EnvAuthProvider,\n type EnvAuthProviderConfig,\n SimpleTokenResolver,\n type SimpleTokenResolverConfig\n} from './providers/index.js';\n"],
|
|
5
|
+
"mappings": "AAcA,SAAS,wBAAwB;AAGjC;AAAA,EACE;AAAA,EAEA;AAAA,OAEK;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { BaseAuthProvider } from "../base-provider.js";
|
|
2
|
+
class EnvAuthProvider extends BaseAuthProvider {
|
|
3
|
+
envConfig;
|
|
4
|
+
constructor(config = {}) {
|
|
5
|
+
super(config);
|
|
6
|
+
this.envConfig = {
|
|
7
|
+
...config,
|
|
8
|
+
userIdEnvVar: config.userIdEnvVar ?? "MCP_USER_ID",
|
|
9
|
+
defaultUserId: config.defaultUserId ?? "default-user",
|
|
10
|
+
requireUserId: config.requireUserId ?? false,
|
|
11
|
+
errorMessages: config.errorMessages ?? {},
|
|
12
|
+
cacheResults: config.cacheResults ?? false,
|
|
13
|
+
cacheTtl: config.cacheTtl ?? 6e4
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Authenticate request by reading user ID from environment
|
|
18
|
+
*/
|
|
19
|
+
async doAuthenticate(context) {
|
|
20
|
+
const userId = process.env[this.envConfig.userIdEnvVar];
|
|
21
|
+
if (!userId) {
|
|
22
|
+
if (this.envConfig.requireUserId) {
|
|
23
|
+
return this.createFailureResult(
|
|
24
|
+
`${this.envConfig.userIdEnvVar} environment variable is required`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
this.logger.debug("Using default user ID", {
|
|
28
|
+
defaultUserId: this.envConfig.defaultUserId
|
|
29
|
+
});
|
|
30
|
+
return this.createSuccessResult(this.envConfig.defaultUserId, {
|
|
31
|
+
source: "default"
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if (userId.trim().length === 0) {
|
|
35
|
+
return this.createFailureResult("User ID cannot be empty");
|
|
36
|
+
}
|
|
37
|
+
this.logger.debug("User ID resolved from environment", {
|
|
38
|
+
envVar: this.envConfig.userIdEnvVar,
|
|
39
|
+
userId
|
|
40
|
+
});
|
|
41
|
+
return this.createSuccessResult(userId, {
|
|
42
|
+
source: "environment",
|
|
43
|
+
envVar: this.envConfig.userIdEnvVar
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Validate provider configuration
|
|
48
|
+
*/
|
|
49
|
+
async validate() {
|
|
50
|
+
if (this.envConfig.requireUserId) {
|
|
51
|
+
const userId = process.env[this.envConfig.userIdEnvVar];
|
|
52
|
+
if (!userId) {
|
|
53
|
+
this.logger.error("Validation failed: Required environment variable not set", void 0, {
|
|
54
|
+
envVar: this.envConfig.userIdEnvVar
|
|
55
|
+
});
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get current user ID from environment
|
|
63
|
+
*/
|
|
64
|
+
getUserId() {
|
|
65
|
+
return process.env[this.envConfig.userIdEnvVar] ?? this.envConfig.defaultUserId;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
EnvAuthProvider
|
|
70
|
+
};
|
|
71
|
+
//# sourceMappingURL=env-provider.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/auth/providers/env-provider.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Environment variable authentication provider\n * \n * Simple provider for single-user scenarios that reads credentials from environment variables.\n * Useful for local development and stdio mode.\n */\n\nimport { BaseAuthProvider } from '../base-provider.js';\nimport type { AuthProviderConfig } from '../types.js';\nimport type { RequestContext, AuthResult } from '../../types.js';\nimport { MissingCredentialsError } from '../../utils/errors.js';\n\n/**\n * Configuration for EnvAuthProvider\n */\nexport interface EnvAuthProviderConfig extends AuthProviderConfig {\n /**\n * Environment variable name containing the user ID\n * @default 'MCP_USER_ID'\n */\n userIdEnvVar?: string;\n \n /**\n * Default user ID if environment variable is not set\n * @default 'default-user'\n */\n defaultUserId?: string;\n \n /**\n * Whether to require user ID environment variable\n * @default false\n */\n requireUserId?: boolean;\n}\n\n/**\n * Environment variable authentication provider\n * \n * This provider is designed for single-user scenarios where authentication\n * is not required (e.g., local development, stdio mode).\n * \n * It reads the user ID from an environment variable or uses a default value.\n * \n * @example\n * ```typescript\n * const provider = new EnvAuthProvider({\n * userIdEnvVar: 'MY_USER_ID',\n * defaultUserId: 'local-user'\n * });\n * \n * // Or with defaults\n * const provider = new EnvAuthProvider();\n * ```\n */\nexport class EnvAuthProvider extends BaseAuthProvider {\n private envConfig: Required<EnvAuthProviderConfig>;\n \n constructor(config: EnvAuthProviderConfig = {}) {\n super(config);\n \n this.envConfig = {\n ...config,\n userIdEnvVar: config.userIdEnvVar ?? 'MCP_USER_ID',\n defaultUserId: config.defaultUserId ?? 'default-user',\n requireUserId: config.requireUserId ?? false,\n errorMessages: config.errorMessages ?? {},\n cacheResults: config.cacheResults ?? false,\n cacheTtl: config.cacheTtl ?? 60000\n };\n }\n \n /**\n * Authenticate request by reading user ID from environment\n */\n protected async doAuthenticate(context: RequestContext): Promise<AuthResult> {\n // Read user ID from environment variable\n const userId = process.env[this.envConfig.userIdEnvVar];\n \n if (!userId) {\n if (this.envConfig.requireUserId) {\n return this.createFailureResult(\n `${this.envConfig.userIdEnvVar} environment variable is required`\n );\n }\n \n // Use default user ID\n this.logger.debug('Using default user ID', {\n defaultUserId: this.envConfig.defaultUserId\n });\n \n return this.createSuccessResult(this.envConfig.defaultUserId, {\n source: 'default'\n });\n }\n \n // Validate user ID\n if (userId.trim().length === 0) {\n return this.createFailureResult('User ID cannot be empty');\n }\n \n this.logger.debug('User ID resolved from environment', {\n envVar: this.envConfig.userIdEnvVar,\n userId\n });\n \n return this.createSuccessResult(userId, {\n source: 'environment',\n envVar: this.envConfig.userIdEnvVar\n });\n }\n \n /**\n * Validate provider configuration\n */\n async validate(): Promise<boolean> {\n if (this.envConfig.requireUserId) {\n const userId = process.env[this.envConfig.userIdEnvVar];\n if (!userId) {\n this.logger.error('Validation failed: Required environment variable not set', undefined, {\n envVar: this.envConfig.userIdEnvVar\n });\n return false;\n }\n }\n \n return true;\n }\n \n /**\n * Get current user ID from environment\n */\n getUserId(): string {\n return process.env[this.envConfig.userIdEnvVar] ?? this.envConfig.defaultUserId;\n }\n}\n"],
|
|
5
|
+
"mappings": "AAOA,SAAS,wBAAwB;AA+C1B,MAAM,wBAAwB,iBAAiB;AAAA,EAC5C;AAAA,EAER,YAAY,SAAgC,CAAC,GAAG;AAC9C,UAAM,MAAM;AAEZ,SAAK,YAAY;AAAA,MACf,GAAG;AAAA,MACH,cAAc,OAAO,gBAAgB;AAAA,MACrC,eAAe,OAAO,iBAAiB;AAAA,MACvC,eAAe,OAAO,iBAAiB;AAAA,MACvC,eAAe,OAAO,iBAAiB,CAAC;AAAA,MACxC,cAAc,OAAO,gBAAgB;AAAA,MACrC,UAAU,OAAO,YAAY;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAgB,eAAe,SAA8C;AAE3E,UAAM,SAAS,QAAQ,IAAI,KAAK,UAAU,YAAY;AAEtD,QAAI,CAAC,QAAQ;AACX,UAAI,KAAK,UAAU,eAAe;AAChC,eAAO,KAAK;AAAA,UACV,GAAG,KAAK,UAAU,YAAY;AAAA,QAChC;AAAA,MACF;AAGA,WAAK,OAAO,MAAM,yBAAyB;AAAA,QACzC,eAAe,KAAK,UAAU;AAAA,MAChC,CAAC;AAED,aAAO,KAAK,oBAAoB,KAAK,UAAU,eAAe;AAAA,QAC5D,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAGA,QAAI,OAAO,KAAK,EAAE,WAAW,GAAG;AAC9B,aAAO,KAAK,oBAAoB,yBAAyB;AAAA,IAC3D;AAEA,SAAK,OAAO,MAAM,qCAAqC;AAAA,MACrD,QAAQ,KAAK,UAAU;AAAA,MACvB;AAAA,IACF,CAAC;AAED,WAAO,KAAK,oBAAoB,QAAQ;AAAA,MACtC,QAAQ;AAAA,MACR,QAAQ,KAAK,UAAU;AAAA,IACzB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,WAA6B;AACjC,QAAI,KAAK,UAAU,eAAe;AAChC,YAAM,SAAS,QAAQ,IAAI,KAAK,UAAU,YAAY;AACtD,UAAI,CAAC,QAAQ;AACX,aAAK,OAAO,MAAM,4DAA4D,QAAW;AAAA,UACvF,QAAQ,KAAK,UAAU;AAAA,QACzB,CAAC;AACD,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,YAAoB;AAClB,WAAO,QAAQ,IAAI,KAAK,UAAU,YAAY,KAAK,KAAK,UAAU;AAAA,EACpE;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/auth/providers/index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Authentication providers module exports\n */\n\n// Environment-based provider\nexport { \n EnvAuthProvider,\n type EnvAuthProviderConfig \n} from './env-provider.js';\n\n// Simple token resolver\nexport { \n SimpleTokenResolver,\n type SimpleTokenResolverConfig \n} from './simple-resolver.js';\n"],
|
|
5
|
+
"mappings": "AAKA;AAAA,EACE;AAAA,OAEK;AAGP;AAAA,EACE;AAAA,OAEK;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|