within-sdk 1.0.0 → 1.0.1
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 +1 -1
- package/README.md +211 -77
- package/dist/thirdparty/ksuid/base-convert-int-array.js +49 -49
- package/dist/thirdparty/ksuid/base62.js +24 -24
- package/dist/thirdparty/ksuid/index.d.ts +30 -30
- package/dist/thirdparty/ksuid/index.js +201 -201
- package/package.json +43 -43
- package/dist/middleware.d.ts +0 -40
- package/dist/middleware.js +0 -562
- package/dist/network.d.ts +0 -83
- package/dist/network.js +0 -411
- package/dist/proxy.d.ts +0 -31
- package/dist/proxy.js +0 -158
- package/dist/validate.d.ts +0 -53
- package/dist/validate.js +0 -139
package/dist/validate.js
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* With.in MCP Auth SDK — Token Validation
|
|
3
|
-
*
|
|
4
|
-
* Validates tokens against With.in's JWKS endpoint.
|
|
5
|
-
* Caches the JWKS keys in memory with a 5-minute refresh interval.
|
|
6
|
-
*
|
|
7
|
-
* SYNC NOTE: vendor/lib/validate.ts intentionally diverges here — it adds
|
|
8
|
-
* `ngrok-skip-browser-warning` headers for local-dev only. Keep the
|
|
9
|
-
* published SDK clean of those.
|
|
10
|
-
*/
|
|
11
|
-
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
12
|
-
// Cache JWKS key set per URL
|
|
13
|
-
const jwksCache = new Map();
|
|
14
|
-
function getJwks(jwksUrl) {
|
|
15
|
-
let jwks = jwksCache.get(jwksUrl);
|
|
16
|
-
if (!jwks) {
|
|
17
|
-
jwks = createRemoteJWKSet(new URL(jwksUrl));
|
|
18
|
-
jwksCache.set(jwksUrl, jwks);
|
|
19
|
-
}
|
|
20
|
-
return jwks;
|
|
21
|
-
}
|
|
22
|
-
export async function validateWithinToken(token, jwksUrl) {
|
|
23
|
-
try {
|
|
24
|
-
const jwks = getJwks(jwksUrl);
|
|
25
|
-
const { payload } = await jwtVerify(token, jwks);
|
|
26
|
-
// Verify it's a With.in token
|
|
27
|
-
if (payload.token_type !== 'access' && payload.token_type !== 'identity') {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
return payload;
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
export function extractBearerToken(authHeader) {
|
|
37
|
-
if (!authHeader)
|
|
38
|
-
return null;
|
|
39
|
-
const match = authHeader.match(/^Bearer\s+(.+)$/i);
|
|
40
|
-
return match?.[1] ?? null;
|
|
41
|
-
}
|
|
42
|
-
/** Report to With.in that the vendor recognises this user as an existing customer.
|
|
43
|
-
* Call this when a With.in token arrives and you find the user in your own DB.
|
|
44
|
-
* With.in will mark the user as a subscriber (no conversion fee).
|
|
45
|
-
*
|
|
46
|
-
* Pass `withinAccessToken` whenever it's available — the backend authenticates
|
|
47
|
-
* the request by checking that token's claims match (email + vendor_slug).
|
|
48
|
-
* Without it the call is rejected, so the vendorTokenValidator path (where
|
|
49
|
-
* no With.in token exists) cannot drive conversions. That path is intentionally
|
|
50
|
-
* niche today; broader auth (vendor shared secret) is planned. */
|
|
51
|
-
export async function reportConversion(authServerUrl, vendorSlug, email, withinAccessToken) {
|
|
52
|
-
if (!withinAccessToken)
|
|
53
|
-
return; // No token, no auth → skip silently.
|
|
54
|
-
try {
|
|
55
|
-
await fetch(`${authServerUrl}/oauth/conversion`, {
|
|
56
|
-
method: 'POST',
|
|
57
|
-
headers: {
|
|
58
|
-
'Content-Type': 'application/json',
|
|
59
|
-
Authorization: `Bearer ${withinAccessToken}`,
|
|
60
|
-
},
|
|
61
|
-
body: JSON.stringify({ email, vendor_slug: vendorSlug }),
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// Don't block auth if reporting fails
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
/** Check whether the token holder is now a subscriber for this vendor.
|
|
69
|
-
* Used to unstick users who paid seconds ago but whose cached access
|
|
70
|
-
* token still says the trial is exhausted. Returns false on any error —
|
|
71
|
-
* falling back to the normal trial-exhausted message is the safe default. */
|
|
72
|
-
export async function checkSubscriptionStatus(authServerUrl, token) {
|
|
73
|
-
// Hard-cap the live check so a slow or dead auth server doesn't stall the
|
|
74
|
-
// tool response. Claude will time out the tool call around ~30s; we fail
|
|
75
|
-
// fast at 3s and let the normal trial-ended message render.
|
|
76
|
-
const controller = new AbortController();
|
|
77
|
-
const timer = setTimeout(() => controller.abort(), 3000);
|
|
78
|
-
try {
|
|
79
|
-
const res = await fetch(`${authServerUrl}/oauth/subscription-status`, {
|
|
80
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
81
|
-
signal: controller.signal,
|
|
82
|
-
});
|
|
83
|
-
if (!res.ok)
|
|
84
|
-
return false;
|
|
85
|
-
const data = await res.json();
|
|
86
|
-
return data.subscriber === true;
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
finally {
|
|
92
|
-
clearTimeout(timer);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
/** Report a tool call back to With.in for lead tracking and revocation
|
|
96
|
-
* enforcement. `aiClient` — the MCP client name captured from the session's
|
|
97
|
-
* `initialize` request (`clientInfo.name`). Lets the vendor and the user
|
|
98
|
-
* see which agent (Claude Desktop, Cursor, ChatGPT, …) actually made
|
|
99
|
-
* the call.
|
|
100
|
-
*
|
|
101
|
-
* Returns `{ revoked: true }` when the backend reports the token has been
|
|
102
|
-
* revoked since issuance. Network/server errors return `{ revoked: false }`
|
|
103
|
-
* so reporting outages never block a tool call. */
|
|
104
|
-
export async function reportToolCall(authServerUrl, token, vendorSlug, toolName, aiClient, toolArguments) {
|
|
105
|
-
try {
|
|
106
|
-
const res = await fetch(`${authServerUrl}/oauth/report`, {
|
|
107
|
-
method: 'POST',
|
|
108
|
-
headers: { 'Content-Type': 'application/json' },
|
|
109
|
-
body: JSON.stringify({
|
|
110
|
-
token,
|
|
111
|
-
tool_name: toolName,
|
|
112
|
-
vendor_slug: vendorSlug,
|
|
113
|
-
ai_client: aiClient,
|
|
114
|
-
tool_arguments: toolArguments ?? undefined,
|
|
115
|
-
}),
|
|
116
|
-
});
|
|
117
|
-
if (res.status === 401) {
|
|
118
|
-
try {
|
|
119
|
-
const body = (await res.json());
|
|
120
|
-
if (body?.error === 'token_revoked')
|
|
121
|
-
return { revoked: true };
|
|
122
|
-
}
|
|
123
|
-
catch { }
|
|
124
|
-
}
|
|
125
|
-
if (res.status === 429) {
|
|
126
|
-
try {
|
|
127
|
-
const body = (await res.json());
|
|
128
|
-
if (body?.error === 'tool_budget_exhausted' && typeof body.tool === 'string' && typeof body.limit === 'number') {
|
|
129
|
-
return { revoked: false, budgetExhausted: { tool: body.tool, limit: body.limit } };
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
catch { }
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
catch {
|
|
136
|
-
// Don't block tool calls if reporting fails
|
|
137
|
-
}
|
|
138
|
-
return { revoked: false };
|
|
139
|
-
}
|