paygate-mcp 2.9.0 → 3.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.
- package/README.md +89 -0
- package/dist/audit.d.ts +1 -1
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js.map +1 -1
- package/dist/gate.d.ts +3 -3
- package/dist/gate.d.ts.map +1 -1
- package/dist/gate.js +40 -4
- package/dist/gate.js.map +1 -1
- package/dist/http-proxy.d.ts +2 -2
- package/dist/http-proxy.d.ts.map +1 -1
- package/dist/http-proxy.js +5 -5
- package/dist/http-proxy.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +2 -2
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +5 -5
- package/dist/proxy.js.map +1 -1
- package/dist/router.d.ts +2 -2
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +9 -9
- package/dist/router.js.map +1 -1
- package/dist/server.d.ts +7 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +97 -7
- package/dist/server.js.map +1 -1
- package/dist/tokens.d.ts +71 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +126 -0
- package/dist/tokens.js.map +1 -0
- package/package.json +1 -1
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ScopedTokenManager — Issue and validate short-lived scoped tokens
|
|
4
|
+
* derived from API keys.
|
|
5
|
+
*
|
|
6
|
+
* Tokens are self-contained (no server-side state): the payload is
|
|
7
|
+
* HMAC-SHA256 signed and base64url-encoded. Validation is a pure
|
|
8
|
+
* crypto check — no DB lookup needed.
|
|
9
|
+
*
|
|
10
|
+
* Format: pgt_<base64url(JSON payload)>.<base64url(HMAC signature)>
|
|
11
|
+
*
|
|
12
|
+
* Use cases:
|
|
13
|
+
* - Browser-based agents that shouldn't hold long-lived API keys
|
|
14
|
+
* - Temporary scoped access (e.g., only allow specific tools)
|
|
15
|
+
* - Token delegation from a master key to a downstream agent
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.ScopedTokenManager = void 0;
|
|
19
|
+
const crypto_1 = require("crypto");
|
|
20
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
21
|
+
const TOKEN_PREFIX = 'pgt_';
|
|
22
|
+
const MAX_TTL_SECONDS = 86400; // 24 hours
|
|
23
|
+
const DEFAULT_TTL_SECONDS = 3600; // 1 hour
|
|
24
|
+
const MAX_TOKEN_AGE_CHECK_MS = MAX_TTL_SECONDS * 1000;
|
|
25
|
+
// ─── Manager ──────────────────────────────────────────────────────────────────
|
|
26
|
+
class ScopedTokenManager {
|
|
27
|
+
secret;
|
|
28
|
+
/**
|
|
29
|
+
* @param secret — Signing secret (the admin key is used by default)
|
|
30
|
+
*/
|
|
31
|
+
constructor(secret) {
|
|
32
|
+
if (!secret || secret.length < 8) {
|
|
33
|
+
throw new Error('Token signing secret must be at least 8 characters');
|
|
34
|
+
}
|
|
35
|
+
this.secret = secret;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Issue a new scoped token.
|
|
39
|
+
*/
|
|
40
|
+
create(options) {
|
|
41
|
+
const ttl = Math.min(Math.max(1, options.ttlSeconds || DEFAULT_TTL_SECONDS), MAX_TTL_SECONDS);
|
|
42
|
+
const expiresAt = options.expiresAt || new Date(Date.now() + ttl * 1000).toISOString();
|
|
43
|
+
const payload = {
|
|
44
|
+
apiKey: options.apiKey,
|
|
45
|
+
expiresAt,
|
|
46
|
+
issuedAt: new Date().toISOString(),
|
|
47
|
+
...(options.allowedTools?.length ? { allowedTools: options.allowedTools } : {}),
|
|
48
|
+
...(options.label ? { label: options.label } : {}),
|
|
49
|
+
};
|
|
50
|
+
const payloadB64 = this.base64urlEncode(JSON.stringify(payload));
|
|
51
|
+
const signature = this.sign(payloadB64);
|
|
52
|
+
return `${TOKEN_PREFIX}${payloadB64}.${signature}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Validate a scoped token. Returns the payload if valid.
|
|
56
|
+
*/
|
|
57
|
+
validate(token) {
|
|
58
|
+
if (!token.startsWith(TOKEN_PREFIX)) {
|
|
59
|
+
return { valid: false, reason: 'not_a_scoped_token' };
|
|
60
|
+
}
|
|
61
|
+
const body = token.slice(TOKEN_PREFIX.length);
|
|
62
|
+
const dotIdx = body.lastIndexOf('.');
|
|
63
|
+
if (dotIdx === -1) {
|
|
64
|
+
return { valid: false, reason: 'malformed_token' };
|
|
65
|
+
}
|
|
66
|
+
const payloadB64 = body.slice(0, dotIdx);
|
|
67
|
+
const signatureB64 = body.slice(dotIdx + 1);
|
|
68
|
+
// Verify HMAC
|
|
69
|
+
const expectedSig = this.sign(payloadB64);
|
|
70
|
+
if (!this.timingSafeCompare(signatureB64, expectedSig)) {
|
|
71
|
+
return { valid: false, reason: 'invalid_signature' };
|
|
72
|
+
}
|
|
73
|
+
// Decode payload
|
|
74
|
+
let payload;
|
|
75
|
+
try {
|
|
76
|
+
payload = JSON.parse(this.base64urlDecode(payloadB64));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return { valid: false, reason: 'malformed_payload' };
|
|
80
|
+
}
|
|
81
|
+
// Check required fields
|
|
82
|
+
if (!payload.apiKey || !payload.expiresAt || !payload.issuedAt) {
|
|
83
|
+
return { valid: false, reason: 'missing_required_fields' };
|
|
84
|
+
}
|
|
85
|
+
// Check expiry
|
|
86
|
+
const now = Date.now();
|
|
87
|
+
const expiresAtMs = new Date(payload.expiresAt).getTime();
|
|
88
|
+
if (isNaN(expiresAtMs) || expiresAtMs <= now) {
|
|
89
|
+
return { valid: false, reason: 'token_expired' };
|
|
90
|
+
}
|
|
91
|
+
// Sanity: token can't be valid for more than MAX_TTL
|
|
92
|
+
const issuedAtMs = new Date(payload.issuedAt).getTime();
|
|
93
|
+
if (expiresAtMs - issuedAtMs > MAX_TOKEN_AGE_CHECK_MS) {
|
|
94
|
+
return { valid: false, reason: 'token_ttl_exceeded' };
|
|
95
|
+
}
|
|
96
|
+
return { valid: true, payload };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Check if a string looks like a scoped token (prefix check only).
|
|
100
|
+
*/
|
|
101
|
+
static isToken(value) {
|
|
102
|
+
return value.startsWith(TOKEN_PREFIX);
|
|
103
|
+
}
|
|
104
|
+
// ─── Crypto helpers ───────────────────────────────────────────────────────
|
|
105
|
+
sign(data) {
|
|
106
|
+
const hmac = (0, crypto_1.createHmac)('sha256', this.secret);
|
|
107
|
+
hmac.update(data);
|
|
108
|
+
return this.base64urlEncode(hmac.digest());
|
|
109
|
+
}
|
|
110
|
+
timingSafeCompare(a, b) {
|
|
111
|
+
if (a.length !== b.length)
|
|
112
|
+
return false;
|
|
113
|
+
const bufA = Buffer.from(a);
|
|
114
|
+
const bufB = Buffer.from(b);
|
|
115
|
+
return (0, crypto_1.timingSafeEqual)(bufA, bufB);
|
|
116
|
+
}
|
|
117
|
+
base64urlEncode(input) {
|
|
118
|
+
const buf = typeof input === 'string' ? Buffer.from(input) : input;
|
|
119
|
+
return buf.toString('base64url');
|
|
120
|
+
}
|
|
121
|
+
base64urlDecode(input) {
|
|
122
|
+
return Buffer.from(input, 'base64url').toString('utf-8');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.ScopedTokenManager = ScopedTokenManager;
|
|
126
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paygate-mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Pay-per-tool-call gating proxy for MCP servers. Wrap any MCP server with API key auth, per-tool pricing, rate limiting, and usage metering.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|