@leanmcp/auth 0.1.1 → 0.3.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/README.md +606 -18
- package/dist/auth0-54GZT2EI.mjs +102 -0
- package/dist/{chunk-NALGJYQB.mjs → chunk-EVD2TRPR.mjs} +63 -15
- package/dist/clerk-FR7ITM33.mjs +115 -0
- package/dist/{cognito-GBSAAMZI.mjs → cognito-I6V5YNYM.mjs} +1 -1
- package/dist/index.d.mts +55 -6
- package/dist/index.d.ts +55 -6
- package/dist/index.js +289 -15
- package/dist/index.mjs +3 -1
- package/package.json +1 -1
- package/dist/chunk-YC7GFXAO.mjs +0 -193
- package/dist/cognito-VCVS77OX.mjs +0 -145
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthProviderBase,
|
|
3
|
+
__name
|
|
4
|
+
} from "./chunk-EVD2TRPR.mjs";
|
|
5
|
+
|
|
6
|
+
// src/providers/auth0.ts
|
|
7
|
+
import axios from "axios";
|
|
8
|
+
import jwt from "jsonwebtoken";
|
|
9
|
+
import jwkToPem from "jwk-to-pem";
|
|
10
|
+
var AuthAuth0 = class extends AuthProviderBase {
|
|
11
|
+
static {
|
|
12
|
+
__name(this, "AuthAuth0");
|
|
13
|
+
}
|
|
14
|
+
domain = "";
|
|
15
|
+
clientId = "";
|
|
16
|
+
clientSecret = "";
|
|
17
|
+
audience = "";
|
|
18
|
+
scopes = "openid profile email offline_access";
|
|
19
|
+
jwksCache = null;
|
|
20
|
+
async init(config) {
|
|
21
|
+
this.domain = config?.domain || process.env.AUTH0_DOMAIN || "";
|
|
22
|
+
this.clientId = config?.clientId || process.env.AUTH0_CLIENT_ID || "";
|
|
23
|
+
this.clientSecret = config?.clientSecret || process.env.AUTH0_CLIENT_SECRET || "";
|
|
24
|
+
this.audience = config?.audience || process.env.AUTH0_AUDIENCE || "";
|
|
25
|
+
this.scopes = config?.scopes || this.scopes;
|
|
26
|
+
if (!this.domain || !this.clientId || !this.audience) {
|
|
27
|
+
throw new Error("Auth0 config missing: domain, clientId, and audience are required");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async refreshToken(refreshToken) {
|
|
31
|
+
const url = `https://${this.domain}/oauth/token`;
|
|
32
|
+
const payload = {
|
|
33
|
+
grant_type: "refresh_token",
|
|
34
|
+
client_id: this.clientId,
|
|
35
|
+
refresh_token: refreshToken,
|
|
36
|
+
audience: this.audience,
|
|
37
|
+
scope: this.scopes
|
|
38
|
+
};
|
|
39
|
+
if (this.clientSecret) {
|
|
40
|
+
payload.client_secret = this.clientSecret;
|
|
41
|
+
}
|
|
42
|
+
const { data } = await axios.post(url, payload, {
|
|
43
|
+
headers: {
|
|
44
|
+
"Content-Type": "application/json"
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return data;
|
|
48
|
+
}
|
|
49
|
+
async verifyToken(token) {
|
|
50
|
+
try {
|
|
51
|
+
await this.verifyJwt(token);
|
|
52
|
+
return true;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (error instanceof Error) {
|
|
55
|
+
if (error.message.includes("jwt expired")) throw new Error("Token has expired");
|
|
56
|
+
if (error.message.includes("invalid signature")) throw new Error("Invalid token signature");
|
|
57
|
+
if (error.message.includes("jwt malformed")) throw new Error("Malformed token");
|
|
58
|
+
if (error.message.includes("invalid issuer")) throw new Error("Invalid token issuer");
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async getUser(idToken) {
|
|
65
|
+
const decoded = jwt.decode(idToken);
|
|
66
|
+
if (!decoded) throw new Error("Invalid ID token");
|
|
67
|
+
return {
|
|
68
|
+
sub: decoded.sub,
|
|
69
|
+
email: decoded.email,
|
|
70
|
+
email_verified: decoded.email_verified,
|
|
71
|
+
name: decoded.name,
|
|
72
|
+
attributes: decoded
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async fetchJWKS() {
|
|
76
|
+
if (!this.jwksCache) {
|
|
77
|
+
const jwksUri = `https://${this.domain}/.well-known/jwks.json`;
|
|
78
|
+
const { data } = await axios.get(jwksUri);
|
|
79
|
+
this.jwksCache = data.keys;
|
|
80
|
+
}
|
|
81
|
+
return this.jwksCache;
|
|
82
|
+
}
|
|
83
|
+
async verifyJwt(token) {
|
|
84
|
+
const decoded = jwt.decode(token, {
|
|
85
|
+
complete: true
|
|
86
|
+
});
|
|
87
|
+
if (!decoded) throw new Error("Invalid token");
|
|
88
|
+
const jwks = await this.fetchJWKS();
|
|
89
|
+
const key = jwks.find((k) => k.kid === decoded.header.kid);
|
|
90
|
+
if (!key) throw new Error("Signing key not found in JWKS");
|
|
91
|
+
const pem = jwkToPem(key);
|
|
92
|
+
return jwt.verify(token, pem, {
|
|
93
|
+
algorithms: [
|
|
94
|
+
"RS256"
|
|
95
|
+
],
|
|
96
|
+
issuer: `https://${this.domain}/`
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
export {
|
|
101
|
+
AuthAuth0
|
|
102
|
+
};
|
|
@@ -6,6 +6,21 @@ import "reflect-metadata";
|
|
|
6
6
|
|
|
7
7
|
// src/decorators.ts
|
|
8
8
|
import "reflect-metadata";
|
|
9
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
10
|
+
var authUserStorage = new AsyncLocalStorage();
|
|
11
|
+
function getAuthUser() {
|
|
12
|
+
return authUserStorage.getStore();
|
|
13
|
+
}
|
|
14
|
+
__name(getAuthUser, "getAuthUser");
|
|
15
|
+
if (typeof globalThis !== "undefined" && !Object.getOwnPropertyDescriptor(globalThis, "authUser")) {
|
|
16
|
+
Object.defineProperty(globalThis, "authUser", {
|
|
17
|
+
get() {
|
|
18
|
+
return authUserStorage.getStore();
|
|
19
|
+
},
|
|
20
|
+
configurable: true,
|
|
21
|
+
enumerable: false
|
|
22
|
+
});
|
|
23
|
+
}
|
|
9
24
|
var AuthenticationError = class extends Error {
|
|
10
25
|
static {
|
|
11
26
|
__name(this, "AuthenticationError");
|
|
@@ -16,11 +31,16 @@ var AuthenticationError = class extends Error {
|
|
|
16
31
|
this.name = "AuthenticationError";
|
|
17
32
|
}
|
|
18
33
|
};
|
|
19
|
-
function Authenticated(authProvider) {
|
|
34
|
+
function Authenticated(authProvider, options) {
|
|
35
|
+
const authOptions = {
|
|
36
|
+
getUser: true,
|
|
37
|
+
...options
|
|
38
|
+
};
|
|
20
39
|
return function(target, propertyKey, descriptor) {
|
|
21
40
|
if (!propertyKey && !descriptor) {
|
|
22
41
|
Reflect.defineMetadata("auth:provider", authProvider, target);
|
|
23
42
|
Reflect.defineMetadata("auth:required", true, target);
|
|
43
|
+
Reflect.defineMetadata("auth:options", authOptions, target);
|
|
24
44
|
const prototype = target.prototype;
|
|
25
45
|
const methodNames = Object.getOwnPropertyNames(prototype).filter((name) => name !== "constructor" && typeof prototype[name] === "function");
|
|
26
46
|
for (const methodName of methodNames) {
|
|
@@ -29,7 +49,8 @@ function Authenticated(authProvider) {
|
|
|
29
49
|
const originalMethod = originalDescriptor.value;
|
|
30
50
|
Reflect.defineMetadata("auth:provider", authProvider, originalMethod);
|
|
31
51
|
Reflect.defineMetadata("auth:required", true, originalMethod);
|
|
32
|
-
|
|
52
|
+
Reflect.defineMetadata("auth:options", authOptions, originalMethod);
|
|
53
|
+
prototype[methodName] = createAuthenticatedMethod(originalMethod, authProvider, authOptions);
|
|
33
54
|
copyMetadata(originalMethod, prototype[methodName]);
|
|
34
55
|
}
|
|
35
56
|
}
|
|
@@ -39,7 +60,8 @@ function Authenticated(authProvider) {
|
|
|
39
60
|
const originalMethod = descriptor.value;
|
|
40
61
|
Reflect.defineMetadata("auth:provider", authProvider, originalMethod);
|
|
41
62
|
Reflect.defineMetadata("auth:required", true, originalMethod);
|
|
42
|
-
|
|
63
|
+
Reflect.defineMetadata("auth:options", authOptions, originalMethod);
|
|
64
|
+
descriptor.value = createAuthenticatedMethod(originalMethod, authProvider, authOptions);
|
|
43
65
|
copyMetadata(originalMethod, descriptor.value);
|
|
44
66
|
return descriptor;
|
|
45
67
|
}
|
|
@@ -47,7 +69,7 @@ function Authenticated(authProvider) {
|
|
|
47
69
|
};
|
|
48
70
|
}
|
|
49
71
|
__name(Authenticated, "Authenticated");
|
|
50
|
-
function createAuthenticatedMethod(originalMethod, authProvider) {
|
|
72
|
+
function createAuthenticatedMethod(originalMethod, authProvider, options) {
|
|
51
73
|
return async function(args, meta) {
|
|
52
74
|
const token = meta?.authorization?.token;
|
|
53
75
|
if (!token) {
|
|
@@ -64,9 +86,29 @@ function createAuthenticatedMethod(originalMethod, authProvider) {
|
|
|
64
86
|
}
|
|
65
87
|
throw new AuthenticationError(`Token verification failed: ${error instanceof Error ? error.message : String(error)}`, "VERIFICATION_FAILED");
|
|
66
88
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
89
|
+
if (options.getUser !== false) {
|
|
90
|
+
try {
|
|
91
|
+
const user = await authProvider.getUser(token);
|
|
92
|
+
return await authUserStorage.run(user, async () => {
|
|
93
|
+
return await originalMethod.apply(this, [
|
|
94
|
+
args
|
|
95
|
+
]);
|
|
96
|
+
});
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.warn("Failed to fetch user information:", error);
|
|
99
|
+
return await authUserStorage.run(void 0, async () => {
|
|
100
|
+
return await originalMethod.apply(this, [
|
|
101
|
+
args
|
|
102
|
+
]);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
return await authUserStorage.run(void 0, async () => {
|
|
107
|
+
return await originalMethod.apply(this, [
|
|
108
|
+
args
|
|
109
|
+
]);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
70
112
|
};
|
|
71
113
|
}
|
|
72
114
|
__name(createAuthenticatedMethod, "createAuthenticatedMethod");
|
|
@@ -123,18 +165,23 @@ var AuthProvider = class extends AuthProviderBase {
|
|
|
123
165
|
const finalConfig = config || this.config;
|
|
124
166
|
switch (this.providerType) {
|
|
125
167
|
case "cognito": {
|
|
126
|
-
const { AuthCognito } = await import("./cognito-
|
|
168
|
+
const { AuthCognito } = await import("./cognito-I6V5YNYM.mjs");
|
|
127
169
|
this.providerInstance = new AuthCognito();
|
|
128
170
|
await this.providerInstance.init(finalConfig);
|
|
129
171
|
break;
|
|
130
172
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
173
|
+
case "auth0": {
|
|
174
|
+
const { AuthAuth0 } = await import("./auth0-54GZT2EI.mjs");
|
|
175
|
+
this.providerInstance = new AuthAuth0();
|
|
176
|
+
await this.providerInstance.init(finalConfig);
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
case "clerk": {
|
|
180
|
+
const { AuthClerk } = await import("./clerk-FR7ITM33.mjs");
|
|
181
|
+
this.providerInstance = new AuthClerk();
|
|
182
|
+
await this.providerInstance.init(finalConfig);
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
138
185
|
default:
|
|
139
186
|
throw new Error(`Unsupported auth provider: ${this.providerType}. Supported providers: cognito`);
|
|
140
187
|
}
|
|
@@ -176,6 +223,7 @@ var AuthProvider = class extends AuthProviderBase {
|
|
|
176
223
|
|
|
177
224
|
export {
|
|
178
225
|
__name,
|
|
226
|
+
getAuthUser,
|
|
179
227
|
AuthenticationError,
|
|
180
228
|
Authenticated,
|
|
181
229
|
isAuthenticationRequired,
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthProviderBase,
|
|
3
|
+
__name
|
|
4
|
+
} from "./chunk-EVD2TRPR.mjs";
|
|
5
|
+
|
|
6
|
+
// src/providers/clerk.ts
|
|
7
|
+
import axios from "axios";
|
|
8
|
+
import jwt from "jsonwebtoken";
|
|
9
|
+
import jwkToPem from "jwk-to-pem";
|
|
10
|
+
var AuthClerk = class extends AuthProviderBase {
|
|
11
|
+
static {
|
|
12
|
+
__name(this, "AuthClerk");
|
|
13
|
+
}
|
|
14
|
+
clerkFrontendApi = "";
|
|
15
|
+
clerkSecretKey = "";
|
|
16
|
+
clerkJWKSUrl = "";
|
|
17
|
+
clerkIssuer = "";
|
|
18
|
+
jwksCache = null;
|
|
19
|
+
mode = "session";
|
|
20
|
+
oauthTokenUrl = "";
|
|
21
|
+
clientId;
|
|
22
|
+
clientSecret;
|
|
23
|
+
redirectUri;
|
|
24
|
+
/**
|
|
25
|
+
* Initialize Clerk Auth Provider
|
|
26
|
+
*/
|
|
27
|
+
async init(config) {
|
|
28
|
+
this.clerkFrontendApi = config?.frontendApi || process.env.CLERK_FRONTEND_API || "";
|
|
29
|
+
this.clerkSecretKey = config?.secretKey || process.env.CLERK_SECRET_KEY || "";
|
|
30
|
+
if (!this.clerkFrontendApi || !this.clerkSecretKey) {
|
|
31
|
+
throw new Error("Missing Clerk configuration: frontendApi and secretKey are required");
|
|
32
|
+
}
|
|
33
|
+
this.clerkIssuer = `https://${this.clerkFrontendApi}`;
|
|
34
|
+
this.clerkJWKSUrl = `${this.clerkIssuer}/.well-known/jwks.json`;
|
|
35
|
+
if (config?.clientId && config?.clientSecret && config?.redirectUri) {
|
|
36
|
+
this.mode = "oauth";
|
|
37
|
+
this.clientId = config.clientId;
|
|
38
|
+
this.clientSecret = config.clientSecret;
|
|
39
|
+
this.redirectUri = config.redirectUri;
|
|
40
|
+
this.oauthTokenUrl = `${this.clerkIssuer}/oauth/token`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Refresh tokens (OAuth mode only)
|
|
45
|
+
*/
|
|
46
|
+
async refreshToken(refreshToken) {
|
|
47
|
+
if (this.mode !== "oauth") {
|
|
48
|
+
throw new Error("Clerk is in Session Mode: refresh tokens are not supported. Enable OAuth mode.");
|
|
49
|
+
}
|
|
50
|
+
const payload = {
|
|
51
|
+
grant_type: "refresh_token",
|
|
52
|
+
refresh_token: refreshToken,
|
|
53
|
+
client_id: this.clientId,
|
|
54
|
+
client_secret: this.clientSecret,
|
|
55
|
+
redirect_uri: this.redirectUri
|
|
56
|
+
};
|
|
57
|
+
const { data } = await axios.post(this.oauthTokenUrl, payload, {
|
|
58
|
+
headers: {
|
|
59
|
+
"Content-Type": "application/json"
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return data;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Verify JWT using JWKS
|
|
66
|
+
*/
|
|
67
|
+
async verifyToken(token) {
|
|
68
|
+
await this.verifyJwt(token);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Extract user data from ID token
|
|
73
|
+
*/
|
|
74
|
+
async getUser(idToken) {
|
|
75
|
+
const decoded = jwt.decode(idToken);
|
|
76
|
+
if (!decoded) throw new Error("Invalid ID token");
|
|
77
|
+
return {
|
|
78
|
+
sub: decoded.sub,
|
|
79
|
+
email: decoded.email,
|
|
80
|
+
email_verified: decoded.email_verified,
|
|
81
|
+
first_name: decoded.given_name,
|
|
82
|
+
last_name: decoded.family_name,
|
|
83
|
+
attributes: decoded
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* JWT verification using JWKS
|
|
88
|
+
*/
|
|
89
|
+
async verifyJwt(token) {
|
|
90
|
+
const decoded = jwt.decode(token, {
|
|
91
|
+
complete: true
|
|
92
|
+
});
|
|
93
|
+
if (!decoded) throw new Error("Invalid token");
|
|
94
|
+
const jwks = await this.fetchJWKS();
|
|
95
|
+
const key = jwks.find((k) => k.kid === decoded.header.kid);
|
|
96
|
+
if (!key) throw new Error("Signing key not found in JWKS");
|
|
97
|
+
const pem = jwkToPem(key);
|
|
98
|
+
return jwt.verify(token, pem, {
|
|
99
|
+
algorithms: [
|
|
100
|
+
"RS256"
|
|
101
|
+
],
|
|
102
|
+
issuer: this.clerkIssuer
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
async fetchJWKS() {
|
|
106
|
+
if (!this.jwksCache) {
|
|
107
|
+
const { data } = await axios.get(this.clerkJWKSUrl);
|
|
108
|
+
this.jwksCache = data.keys;
|
|
109
|
+
}
|
|
110
|
+
return this.jwksCache;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
export {
|
|
114
|
+
AuthClerk
|
|
115
|
+
};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for @leanmcp/auth
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Options for the Authenticated decorator
|
|
6
|
+
*/
|
|
7
|
+
interface AuthenticatedOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Whether to fetch and attach user information to authUser variable
|
|
10
|
+
* @default true
|
|
11
|
+
*/
|
|
12
|
+
getUser?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Global authUser type declaration
|
|
17
|
+
* This makes authUser available in @Authenticated methods without explicit declaration
|
|
18
|
+
*/
|
|
19
|
+
declare global {
|
|
20
|
+
/**
|
|
21
|
+
* Authenticated user object automatically available in @Authenticated methods
|
|
22
|
+
*
|
|
23
|
+
* Implemented as a getter that reads from AsyncLocalStorage for concurrency safety.
|
|
24
|
+
* Each request has its own isolated context - 100% safe for concurrent requests.
|
|
25
|
+
*/
|
|
26
|
+
const authUser: any;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the current authenticated user from the async context
|
|
30
|
+
* This is safe for concurrent requests as each request has its own context
|
|
31
|
+
*/
|
|
32
|
+
declare function getAuthUser(): any;
|
|
1
33
|
/**
|
|
2
34
|
* Authentication error class for better error handling
|
|
3
35
|
*/
|
|
@@ -8,24 +40,40 @@ declare class AuthenticationError extends Error {
|
|
|
8
40
|
/**
|
|
9
41
|
* Decorator to protect MCP tools, prompts, resources, or entire services with authentication
|
|
10
42
|
*
|
|
43
|
+
* CONCURRENCY SAFE: Uses AsyncLocalStorage to ensure each request has its own isolated
|
|
44
|
+
* authUser context, preventing race conditions in high-concurrency scenarios.
|
|
45
|
+
*
|
|
11
46
|
* Usage:
|
|
12
47
|
*
|
|
13
|
-
* 1. Protect individual methods:
|
|
48
|
+
* 1. Protect individual methods with automatic user info:
|
|
14
49
|
* ```typescript
|
|
15
50
|
* @Tool({ description: 'Analyze sentiment' })
|
|
16
|
-
* @Authenticated(authProvider)
|
|
51
|
+
* @Authenticated(authProvider, { getUser: true })
|
|
17
52
|
* async analyzeSentiment(args: AnalyzeSentimentInput): Promise<AnalyzeSentimentOutput> {
|
|
18
|
-
* //
|
|
53
|
+
* // authUser is automatically available in method scope
|
|
54
|
+
* console.log('User:', authUser);
|
|
55
|
+
* console.log('User ID:', authUser.sub);
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* 2. Protect without fetching user info:
|
|
60
|
+
* ```typescript
|
|
61
|
+
* @Tool({ description: 'Public tool' })
|
|
62
|
+
* @Authenticated(authProvider, { getUser: false })
|
|
63
|
+
* async publicTool(args: PublicToolInput): Promise<PublicToolOutput> {
|
|
64
|
+
* // Only verifies token, doesn't fetch user info
|
|
19
65
|
* }
|
|
20
66
|
* ```
|
|
21
67
|
*
|
|
22
|
-
*
|
|
68
|
+
* 3. Protect entire service (all tools/prompts/resources):
|
|
23
69
|
* ```typescript
|
|
24
70
|
* @Authenticated(authProvider)
|
|
25
71
|
* export class SentimentAnalysisService {
|
|
26
72
|
* @Tool({ description: 'Analyze sentiment' })
|
|
27
73
|
* async analyzeSentiment(args: AnalyzeSentimentInput) {
|
|
28
74
|
* // All methods in this service require authentication
|
|
75
|
+
* // authUser is automatically available in all methods
|
|
76
|
+
* console.log('User:', authUser);
|
|
29
77
|
* }
|
|
30
78
|
* }
|
|
31
79
|
* ```
|
|
@@ -48,8 +96,9 @@ declare class AuthenticationError extends Error {
|
|
|
48
96
|
* ```
|
|
49
97
|
*
|
|
50
98
|
* @param authProvider - Instance of AuthProviderBase to use for token verification
|
|
99
|
+
* @param options - Optional configuration for authentication behavior
|
|
51
100
|
*/
|
|
52
|
-
declare function Authenticated(authProvider: AuthProviderBase): (target: any, propertyKey?: string | symbol, descriptor?: PropertyDescriptor) => any;
|
|
101
|
+
declare function Authenticated(authProvider: AuthProviderBase, options?: AuthenticatedOptions): (target: any, propertyKey?: string | symbol, descriptor?: PropertyDescriptor) => any;
|
|
53
102
|
/**
|
|
54
103
|
* Check if a method or class requires authentication
|
|
55
104
|
*/
|
|
@@ -118,4 +167,4 @@ declare class AuthProvider extends AuthProviderBase {
|
|
|
118
167
|
getProviderType(): string;
|
|
119
168
|
}
|
|
120
169
|
|
|
121
|
-
export { AuthProvider, AuthProviderBase, Authenticated, AuthenticationError, getAuthProvider, isAuthenticationRequired };
|
|
170
|
+
export { AuthProvider, AuthProviderBase, Authenticated, type AuthenticatedOptions, AuthenticationError, getAuthProvider, getAuthUser, isAuthenticationRequired };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for @leanmcp/auth
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Options for the Authenticated decorator
|
|
6
|
+
*/
|
|
7
|
+
interface AuthenticatedOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Whether to fetch and attach user information to authUser variable
|
|
10
|
+
* @default true
|
|
11
|
+
*/
|
|
12
|
+
getUser?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Global authUser type declaration
|
|
17
|
+
* This makes authUser available in @Authenticated methods without explicit declaration
|
|
18
|
+
*/
|
|
19
|
+
declare global {
|
|
20
|
+
/**
|
|
21
|
+
* Authenticated user object automatically available in @Authenticated methods
|
|
22
|
+
*
|
|
23
|
+
* Implemented as a getter that reads from AsyncLocalStorage for concurrency safety.
|
|
24
|
+
* Each request has its own isolated context - 100% safe for concurrent requests.
|
|
25
|
+
*/
|
|
26
|
+
const authUser: any;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get the current authenticated user from the async context
|
|
30
|
+
* This is safe for concurrent requests as each request has its own context
|
|
31
|
+
*/
|
|
32
|
+
declare function getAuthUser(): any;
|
|
1
33
|
/**
|
|
2
34
|
* Authentication error class for better error handling
|
|
3
35
|
*/
|
|
@@ -8,24 +40,40 @@ declare class AuthenticationError extends Error {
|
|
|
8
40
|
/**
|
|
9
41
|
* Decorator to protect MCP tools, prompts, resources, or entire services with authentication
|
|
10
42
|
*
|
|
43
|
+
* CONCURRENCY SAFE: Uses AsyncLocalStorage to ensure each request has its own isolated
|
|
44
|
+
* authUser context, preventing race conditions in high-concurrency scenarios.
|
|
45
|
+
*
|
|
11
46
|
* Usage:
|
|
12
47
|
*
|
|
13
|
-
* 1. Protect individual methods:
|
|
48
|
+
* 1. Protect individual methods with automatic user info:
|
|
14
49
|
* ```typescript
|
|
15
50
|
* @Tool({ description: 'Analyze sentiment' })
|
|
16
|
-
* @Authenticated(authProvider)
|
|
51
|
+
* @Authenticated(authProvider, { getUser: true })
|
|
17
52
|
* async analyzeSentiment(args: AnalyzeSentimentInput): Promise<AnalyzeSentimentOutput> {
|
|
18
|
-
* //
|
|
53
|
+
* // authUser is automatically available in method scope
|
|
54
|
+
* console.log('User:', authUser);
|
|
55
|
+
* console.log('User ID:', authUser.sub);
|
|
56
|
+
* }
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* 2. Protect without fetching user info:
|
|
60
|
+
* ```typescript
|
|
61
|
+
* @Tool({ description: 'Public tool' })
|
|
62
|
+
* @Authenticated(authProvider, { getUser: false })
|
|
63
|
+
* async publicTool(args: PublicToolInput): Promise<PublicToolOutput> {
|
|
64
|
+
* // Only verifies token, doesn't fetch user info
|
|
19
65
|
* }
|
|
20
66
|
* ```
|
|
21
67
|
*
|
|
22
|
-
*
|
|
68
|
+
* 3. Protect entire service (all tools/prompts/resources):
|
|
23
69
|
* ```typescript
|
|
24
70
|
* @Authenticated(authProvider)
|
|
25
71
|
* export class SentimentAnalysisService {
|
|
26
72
|
* @Tool({ description: 'Analyze sentiment' })
|
|
27
73
|
* async analyzeSentiment(args: AnalyzeSentimentInput) {
|
|
28
74
|
* // All methods in this service require authentication
|
|
75
|
+
* // authUser is automatically available in all methods
|
|
76
|
+
* console.log('User:', authUser);
|
|
29
77
|
* }
|
|
30
78
|
* }
|
|
31
79
|
* ```
|
|
@@ -48,8 +96,9 @@ declare class AuthenticationError extends Error {
|
|
|
48
96
|
* ```
|
|
49
97
|
*
|
|
50
98
|
* @param authProvider - Instance of AuthProviderBase to use for token verification
|
|
99
|
+
* @param options - Optional configuration for authentication behavior
|
|
51
100
|
*/
|
|
52
|
-
declare function Authenticated(authProvider: AuthProviderBase): (target: any, propertyKey?: string | symbol, descriptor?: PropertyDescriptor) => any;
|
|
101
|
+
declare function Authenticated(authProvider: AuthProviderBase, options?: AuthenticatedOptions): (target: any, propertyKey?: string | symbol, descriptor?: PropertyDescriptor) => any;
|
|
53
102
|
/**
|
|
54
103
|
* Check if a method or class requires authentication
|
|
55
104
|
*/
|
|
@@ -118,4 +167,4 @@ declare class AuthProvider extends AuthProviderBase {
|
|
|
118
167
|
getProviderType(): string;
|
|
119
168
|
}
|
|
120
169
|
|
|
121
|
-
export { AuthProvider, AuthProviderBase, Authenticated, AuthenticationError, getAuthProvider, isAuthenticationRequired };
|
|
170
|
+
export { AuthProvider, AuthProviderBase, Authenticated, type AuthenticatedOptions, AuthenticationError, getAuthProvider, getAuthUser, isAuthenticationRequired };
|