@silkweave/auth 3.2.0 → 4.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/build/oauth.d.mts CHANGED
@@ -86,6 +86,19 @@ interface OAuthProxyConfig {
86
86
  declare function createOAuthProxy(config: OAuthProxyConfig): OAuthProvider;
87
87
  //#endregion
88
88
  //#region src/provider/uri.d.ts
89
+ /**
90
+ * Match a concrete `redirect_uri` against the operator's allow-list patterns.
91
+ *
92
+ * Matching is done per URL *component* (scheme, userinfo, host, port, path),
93
+ * NOT by testing one glob-to-regex against the whole string. A naive
94
+ * whole-string `*`->`.*` lets a wildcard cross authority delimiters, so
95
+ * `https://*.example.com/cb` would match `https://attacker.com/x.example.com/cb`
96
+ * and `http://localhost:*` would match `http://localhost:x@attacker.com/cb`
97
+ * (userinfo injection) - both hand the auth code to an attacker host. Comparing
98
+ * components defeats that: the target is parsed with the URL parser (so its host
99
+ * is exactly its host), and a pattern with no userinfo rejects any target that
100
+ * carries userinfo.
101
+ */
89
102
  declare function matchRedirectUri(uri: string, patterns: string[]): boolean;
90
103
  //#endregion
91
104
  //#region src/store/json-store.d.ts
@@ -1 +1 @@
1
- {"version":3,"file":"oauth.d.mts","names":[],"sources":["../src/provider/store.ts","../src/provider/google.ts","../src/provider/proxy.ts","../src/provider/uri.ts","../src/store/json-store.ts","../src/store/memory-store.ts","../src/store/redis-store.ts"],"mappings":";;;UAAiB,YAAA;EACf,QAAA;EACA,WAAA;EACA,aAAA;EACA,mBAAA;EACA,MAAA;EACA,mBAAA;EACA,eAAA;EACA,KAAA;EACA,GAAA;EACA,SAAA;AAAA;AAAA,UAGe,eAAA;EACf,QAAA;EACA,WAAA;EACA,aAAA;EACA,mBAAA;EACA,KAAA;EACA,WAAA;EACA,QAAA;EACA,SAAA;AAAA;AAAA,UAGe,kBAAA;EACf,QAAA;EACA,YAAA;EACA,YAAA;EACA,UAAA;EACA,SAAA;AAAA;AAAA,UAGe,gBAAA;EACf,QAAA;EACA,MAAA;EACA,KAAA;EACA,GAAA;EACA,SAAA;AAAA;AAAA,UAGe,UAAA;EACf,YAAA,CAAa,IAAA,UAAc,IAAA,EAAM,YAAA,GAAe,OAAA;EAChD,WAAA,CAAY,IAAA,WAAe,OAAA,CAAQ,YAAA;EACnC,cAAA,CAAe,IAAA,WAAe,OAAA;EAE9B,eAAA,CAAgB,KAAA,UAAe,IAAA,EAAM,eAAA,GAAkB,OAAA;EACvD,cAAA,CAAe,KAAA,WAAgB,OAAA,CAAQ,eAAA;EACvC,iBAAA,CAAkB,KAAA,WAAgB,OAAA;EAElC,gBAAA,CAAiB,KAAA,UAAe,QAAA,WAAmB,OAAA;EACnD,eAAA,CAAgB,KAAA,WAAgB,OAAA;EAChC,kBAAA,CAAmB,KAAA,WAAgB,OAAA;EAEnC,UAAA,CAAW,QAAA,UAAkB,IAAA,EAAM,kBAAA,GAAqB,OAAA;EACxD,SAAA,CAAU,QAAA,WAAmB,OAAA,CAAQ,kBAAA;EAErC,gBAAA,CAAiB,KAAA,UAAe,IAAA,EAAM,gBAAA,GAAmB,OAAA;EACzD,eAAA,CAAgB,KAAA,WAAgB,OAAA,CAAQ,gBAAA;EACxC,kBAAA,CAAmB,KAAA,WAAgB,OAAA;AAAA;;;UCtDpB,kBAAA;EACf,QAAA;EACA,YAAA;EACA,WAAA;EACA,YAAA;EACA,cAAA;EACA,YAAA;EACA,UAAA;EACA,QAAA;EACA,KAAA,GAAQ,UAAA;AAAA;AAAA,iBAGM,MAAA,CAAO,OAAA,EAAS,kBAAA,GAAqB,UAAA;;;UCTpC,gBAAA;EACf,YAAA;EACA,QAAA;EACA,WAAA;EACA,QAAA;EACA,YAAA;EACA,WAAA;EACA,YAAA;EACA,cAAA;EACA,YAAA;EACA,UAAA;EACA,QAAA;EACA,KAAA,GAAQ,UAAA;AAAA;AAAA,iBA2SM,gBAAA,CAAiB,MAAA,EAAQ,gBAAA,GAAmB,aAAA;;;iBC9T5C,gBAAA,CAAiB,GAAA,UAAa,QAAA;;;iBCwC9B,eAAA,CAAgB,IAAA,WAAe,UAAA;;;iBC5B/B,iBAAA,CAAA,GAAqB,UAAA;;;UCVpB,WAAA;EACf,GAAA,CAAI,GAAA,WAAc,OAAA;EAClB,GAAA,CAAI,GAAA,UAAa,KAAA,UAAe,OAAA;IAAY,EAAA;EAAA,IAAgB,OAAA;EAC5D,GAAA,CAAI,GAAA,WAAc,OAAA;AAAA;AAAA,UAGH,iBAAA;EACf,MAAA,EAAQ,WAAA;EACR,MAAA;AAAA;AAAA,iBAGc,gBAAA,CAAiB,OAAA,EAAS,iBAAA,GAAoB,UAAA"}
1
+ {"version":3,"file":"oauth.d.mts","names":[],"sources":["../src/provider/store.ts","../src/provider/google.ts","../src/provider/proxy.ts","../src/provider/uri.ts","../src/store/json-store.ts","../src/store/memory-store.ts","../src/store/redis-store.ts"],"mappings":";;;UAAiB,YAAA;EACf,QAAA;EACA,WAAA;EACA,aAAA;EACA,mBAAA;EACA,MAAA;EACA,mBAAA;EACA,eAAA;EACA,KAAA;EACA,GAAA;EACA,SAAA;AAAA;AAAA,UAGe,eAAA;EACf,QAAA;EACA,WAAA;EACA,aAAA;EACA,mBAAA;EACA,KAAA;EACA,WAAA;EACA,QAAA;EACA,SAAA;AAAA;AAAA,UAGe,kBAAA;EACf,QAAA;EACA,YAAA;EACA,YAAA;EACA,UAAA;EACA,SAAA;AAAA;AAAA,UAGe,gBAAA;EACf,QAAA;EACA,MAAA;EACA,KAAA;EACA,GAAA;EACA,SAAA;AAAA;AAAA,UAGe,UAAA;EACf,YAAA,CAAa,IAAA,UAAc,IAAA,EAAM,YAAA,GAAe,OAAA;EAChD,WAAA,CAAY,IAAA,WAAe,OAAA,CAAQ,YAAA;EACnC,cAAA,CAAe,IAAA,WAAe,OAAA;EAE9B,eAAA,CAAgB,KAAA,UAAe,IAAA,EAAM,eAAA,GAAkB,OAAA;EACvD,cAAA,CAAe,KAAA,WAAgB,OAAA,CAAQ,eAAA;EACvC,iBAAA,CAAkB,KAAA,WAAgB,OAAA;EAElC,gBAAA,CAAiB,KAAA,UAAe,QAAA,WAAmB,OAAA;EACnD,eAAA,CAAgB,KAAA,WAAgB,OAAA;EAChC,kBAAA,CAAmB,KAAA,WAAgB,OAAA;EAEnC,UAAA,CAAW,QAAA,UAAkB,IAAA,EAAM,kBAAA,GAAqB,OAAA;EACxD,SAAA,CAAU,QAAA,WAAmB,OAAA,CAAQ,kBAAA;EAErC,gBAAA,CAAiB,KAAA,UAAe,IAAA,EAAM,gBAAA,GAAmB,OAAA;EACzD,eAAA,CAAgB,KAAA,WAAgB,OAAA,CAAQ,gBAAA;EACxC,kBAAA,CAAmB,KAAA,WAAgB,OAAA;AAAA;;;UCtDpB,kBAAA;EACf,QAAA;EACA,YAAA;EACA,WAAA;EACA,YAAA;EACA,cAAA;EACA,YAAA;EACA,UAAA;EACA,QAAA;EACA,KAAA,GAAQ,UAAA;AAAA;AAAA,iBAGM,MAAA,CAAO,OAAA,EAAS,kBAAA,GAAqB,UAAA;;;UCRpC,gBAAA;EACf,YAAA;EACA,QAAA;EACA,WAAA;EACA,QAAA;EACA,YAAA;EACA,WAAA;EACA,YAAA;EACA,cAAA;EACA,YAAA;EACA,UAAA;EACA,QAAA;EACA,KAAA,GAAQ,UAAA;AAAA;AAAA,iBAyUM,gBAAA,CAAiB,MAAA,EAAQ,gBAAA,GAAmB,aAAA;;;;;;AF7V5D;;;;;;;;;;iBGagB,gBAAA,CAAiB,GAAA,UAAa,QAAA;;;iBC+B9B,eAAA,CAAgB,IAAA,WAAe,UAAA;;;iBChC/B,iBAAA,CAAA,GAAqB,UAAA;;;UCVpB,WAAA;EACf,GAAA,CAAI,GAAA,WAAc,OAAA;EAClB,GAAA,CAAI,GAAA,UAAa,KAAA,UAAe,OAAA;IAAY,EAAA;EAAA,IAAgB,OAAA;EAC5D,GAAA,CAAI,GAAA,WAAc,OAAA;AAAA;AAAA,UAGH,iBAAA;EACf,MAAA,EAAQ,WAAA;EACR,MAAA;AAAA;AAAA,iBAGc,gBAAA,CAAiB,OAAA,EAAS,iBAAA,GAAoB,UAAA"}
package/build/oauth.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { randomBytes, randomUUID } from "crypto";
2
2
  import { SignJWT, jwtVerify } from "jose";
3
- import { existsSync, readFileSync, writeFileSync } from "fs";
3
+ import { chmodSync, existsSync, readFileSync, writeFileSync } from "fs";
4
4
  //#region src/store/memory-store.ts
5
5
  function withTtl$1(map, key) {
6
6
  const item = map.get(key);
@@ -72,16 +72,136 @@ function createMemoryStore() {
72
72
  };
73
73
  }
74
74
  //#endregion
75
+ //#region src/provider/ssrf.ts
76
+ /**
77
+ * Best-effort SSRF guard for the Client ID Metadata Document (CIMD) fetch. An
78
+ * unknown `client_id` that is an `https://` URL is fetched by the proxy, so an
79
+ * attacker could point it at internal services or a cloud metadata endpoint
80
+ * (`https://169.254.169.254/...`). This blocks the high-value targets without
81
+ * pulling in `node:dns` (the OAuth proxy also runs on edge/Workers).
82
+ *
83
+ * Coverage and residual risk: IP-literal hosts in private/loopback/link-local/
84
+ * reserved ranges and `localhost` are rejected synchronously; a *hostname* that
85
+ * resolves to a private IP is NOT caught here (no DNS at this layer). Callers
86
+ * MUST additionally disallow redirects and set a timeout on the fetch. Deployments
87
+ * with strict internal-network requirements should enforce egress policy at the
88
+ * network layer.
89
+ */
90
+ function assertSafeMetadataUrl(raw) {
91
+ let url;
92
+ try {
93
+ url = new URL(raw);
94
+ } catch {
95
+ return {
96
+ ok: false,
97
+ reason: "invalid URL"
98
+ };
99
+ }
100
+ if (url.protocol !== "https:") return {
101
+ ok: false,
102
+ reason: "must be https"
103
+ };
104
+ const host = url.hostname.toLowerCase();
105
+ if (host === "localhost" || host.endsWith(".localhost")) return {
106
+ ok: false,
107
+ reason: "loopback host"
108
+ };
109
+ if (isBlockedIpLiteral(host)) return {
110
+ ok: false,
111
+ reason: "private or reserved address"
112
+ };
113
+ return { ok: true };
114
+ }
115
+ function isBlockedIpLiteral(host) {
116
+ if (host.startsWith("[") && host.endsWith("]")) return isBlockedIpv6(host.slice(1, -1));
117
+ const v4 = parseIpv4(host);
118
+ return v4 !== null && isBlockedIpv4(v4);
119
+ }
120
+ function parseIpv4(host) {
121
+ const parts = host.split(".");
122
+ if (parts.length !== 4) return null;
123
+ const octets = [];
124
+ for (const part of parts) {
125
+ if (!/^\d{1,3}$/.test(part)) return null;
126
+ const n = Number(part);
127
+ if (n > 255) return null;
128
+ octets.push(n);
129
+ }
130
+ return octets;
131
+ }
132
+ function isBlockedIpv4([a, b]) {
133
+ if (a === 0) return true;
134
+ if (a === 10) return true;
135
+ if (a === 127) return true;
136
+ if (a === 169 && b === 254) return true;
137
+ if (a === 172 && b >= 16 && b <= 31) return true;
138
+ if (a === 192 && b === 168) return true;
139
+ if (a === 100 && b >= 64 && b <= 127) return true;
140
+ if (a === 192 && b === 0) return true;
141
+ if (a === 198 && (b === 18 || b === 19)) return true;
142
+ if (a >= 224) return true;
143
+ return false;
144
+ }
145
+ function isBlockedIpv6(host) {
146
+ const h = host.toLowerCase();
147
+ if (h === "::" || h === "::1") return true;
148
+ if (h.startsWith("::ffff:")) return true;
149
+ if (h.startsWith("fe8") || h.startsWith("fe9") || h.startsWith("fea") || h.startsWith("feb")) return true;
150
+ if (h.startsWith("fc") || h.startsWith("fd")) return true;
151
+ if (h.startsWith("ff")) return true;
152
+ return false;
153
+ }
154
+ //#endregion
75
155
  //#region src/provider/uri.ts
156
+ /**
157
+ * Match a concrete `redirect_uri` against the operator's allow-list patterns.
158
+ *
159
+ * Matching is done per URL *component* (scheme, userinfo, host, port, path),
160
+ * NOT by testing one glob-to-regex against the whole string. A naive
161
+ * whole-string `*`->`.*` lets a wildcard cross authority delimiters, so
162
+ * `https://*.example.com/cb` would match `https://attacker.com/x.example.com/cb`
163
+ * and `http://localhost:*` would match `http://localhost:x@attacker.com/cb`
164
+ * (userinfo injection) - both hand the auth code to an attacker host. Comparing
165
+ * components defeats that: the target is parsed with the URL parser (so its host
166
+ * is exactly its host), and a pattern with no userinfo rejects any target that
167
+ * carries userinfo.
168
+ */
76
169
  function matchRedirectUri(uri, patterns) {
77
- return patterns.some((pattern) => {
78
- return patternToRegex(pattern).test(uri);
79
- });
170
+ let target;
171
+ try {
172
+ target = new URL(uri);
173
+ } catch {
174
+ return false;
175
+ }
176
+ return patterns.some((pattern) => matchPattern(target, pattern));
80
177
  }
81
- function patternToRegex(pattern) {
82
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
178
+ /** Escape regex metacharacters, then expand `*` to `.*` (component-scoped). */
179
+ function componentRegex(part) {
180
+ const escaped = part.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
83
181
  return new RegExp(`^${escaped}$`);
84
182
  }
183
+ /**
184
+ * A pattern may contain `*` in the host (`*.example.com`), the port
185
+ * (`http://localhost:*`), or the path (`https://app.example.com/*`). The
186
+ * concrete `uri` is always a valid URL. Split the pattern into
187
+ * scheme / userinfo / host / (port + path) and match each against the target's
188
+ * corresponding component. Because the port and path are matched as one tail,
189
+ * a trailing `*` (e.g. loopback `http://localhost:*`) still spans any port and
190
+ * path, as before - it just can no longer escape the authority.
191
+ */
192
+ function matchPattern(target, pattern) {
193
+ const parsed = /^([^:]+:)\/\/(?:([^@/]*)@)?([^/:?#]*)(.*)$/.exec(pattern);
194
+ if (!parsed) return false;
195
+ const [, scheme, userinfo, host, tail] = parsed;
196
+ if (scheme !== target.protocol) return false;
197
+ const targetUserinfo = target.password ? `${target.username}:${target.password}` : target.username;
198
+ if (userinfo === void 0) {
199
+ if (targetUserinfo !== "") return false;
200
+ } else if (!componentRegex(userinfo).test(targetUserinfo)) return false;
201
+ if (!componentRegex(host).test(target.hostname)) return false;
202
+ const targetTail = (target.port ? `:${target.port}` : "") + target.pathname + target.search + target.hash;
203
+ return componentRegex(tail).test(targetTail);
204
+ }
85
205
  //#endregion
86
206
  //#region src/provider/proxy.ts
87
207
  const CORS_HEADERS = {
@@ -173,8 +293,13 @@ async function resolveClient(clientId, store, allowedRedirectUris) {
173
293
  const existing = await store.getClient(clientId);
174
294
  if (existing) return existing;
175
295
  if (!clientId.startsWith("https://")) return errorResponse(400, "invalid_client", "Unknown client_id");
296
+ if (!assertSafeMetadataUrl(clientId).ok) return errorResponse(400, "invalid_client", "Client metadata URL is not allowed");
176
297
  try {
177
- const metaRes = await fetch(clientId);
298
+ const metaRes = await fetch(clientId, {
299
+ redirect: "manual",
300
+ signal: AbortSignal.timeout(5e3)
301
+ });
302
+ if (metaRes.status >= 300 && metaRes.status < 400) return errorResponse(400, "invalid_client", "Client metadata document must not redirect");
178
303
  if (!metaRes.ok) return errorResponse(400, "invalid_client", "Failed to fetch client metadata document");
179
304
  const meta = await metaRes.json();
180
305
  const metaRedirectUris = meta.redirect_uris;
@@ -237,19 +362,30 @@ async function handleRefreshToken(body, store, getSigningKey, config) {
237
362
  if (!refreshToken) return errorResponse(400, "invalid_request", "Missing refresh_token");
238
363
  const tokenData = await store.getRefreshToken(refreshToken);
239
364
  if (!tokenData) return errorResponse(400, "invalid_grant", "Invalid or expired refresh token");
365
+ if (body.client_id && body.client_id !== tokenData.clientId) return errorResponse(400, "invalid_grant", "refresh_token was not issued to this client");
366
+ const accessToken = await signAccessToken(await getSigningKey(), {
367
+ scopes: tokenData.scopes,
368
+ email: tokenData.email,
369
+ sub: tokenData.sub,
370
+ clientId: tokenData.clientId,
371
+ issuer: config.resourceUrl,
372
+ ttl: config.tokenTtl
373
+ });
374
+ await store.deleteRefreshToken(refreshToken);
375
+ const newRefreshToken = randomBytes(32).toString("base64url");
376
+ await store.saveRefreshToken(newRefreshToken, {
377
+ clientId: tokenData.clientId,
378
+ scopes: tokenData.scopes,
379
+ email: tokenData.email,
380
+ sub: tokenData.sub,
381
+ expiresAt: tokenData.expiresAt
382
+ });
240
383
  return jsonResponse(200, {
241
- access_token: await signAccessToken(await getSigningKey(), {
242
- scopes: tokenData.scopes,
243
- email: tokenData.email,
244
- sub: tokenData.sub,
245
- clientId: tokenData.clientId,
246
- issuer: config.resourceUrl,
247
- ttl: config.tokenTtl
248
- }),
384
+ access_token: accessToken,
249
385
  token_type: "Bearer",
250
386
  expires_in: config.tokenTtl,
251
387
  scope: tokenData.scopes.join(" "),
252
- refresh_token: refreshToken
388
+ refresh_token: newRefreshToken
253
389
  });
254
390
  }
255
391
  async function handleAuthorizationCode(body, store, getSigningKey, config) {
@@ -461,7 +597,11 @@ function withTtl(obj, key) {
461
597
  return item;
462
598
  }
463
599
  function writeJsonStore(path, store) {
464
- writeFileSync(path, JSON.stringify(store, null, 2), "utf-8");
600
+ writeFileSync(path, JSON.stringify(store, null, 2), {
601
+ encoding: "utf-8",
602
+ mode: 384
603
+ });
604
+ chmodSync(path, 384);
465
605
  }
466
606
  function readJsonStore(path) {
467
607
  if (!existsSync(path)) return {
@@ -1 +1 @@
1
- {"version":3,"file":"oauth.mjs","names":["withTtl","client"],"sources":["../src/store/memory-store.ts","../src/provider/uri.ts","../src/provider/proxy.ts","../src/provider/google.ts","../src/store/json-store.ts","../src/store/redis-store.ts"],"sourcesContent":["import { AuthCodeData, ClientRegistration, OAuthStore, PendingAuthData, RefreshTokenData } from '../provider/store.js'\n\nfunction withTtl<T extends { expiresAt: number }>(map: Map<string, T>, key: string): T | undefined {\n const item = map.get(key)\n if (!item) { return undefined }\n if (item.expiresAt < Date.now() / 1000) {\n map.delete(key)\n return undefined\n }\n return item\n}\n\nexport function createMemoryStore(): OAuthStore {\n const authCodes = new Map<string, AuthCodeData>()\n const pendingAuths = new Map<string, PendingAuthData>()\n const pkceVerifiers = new Map<string, { verifier: string; expiresAt: number }>()\n const clients = new Map<string, ClientRegistration>()\n const refreshTokens = new Map<string, RefreshTokenData>()\n\n return {\n async saveAuthCode(code, data) { authCodes.set(code, data) },\n async getAuthCode(code) { return withTtl(authCodes, code) },\n async deleteAuthCode(code) { authCodes.delete(code) },\n\n async savePendingAuth(state, data) { pendingAuths.set(state, data) },\n async getPendingAuth(state) { return withTtl(pendingAuths, state) },\n async deletePendingAuth(state) { pendingAuths.delete(state) },\n\n async savePkceVerifier(state, verifier) {\n pkceVerifiers.set(state, { verifier, expiresAt: Date.now() / 1000 + 600 })\n },\n async getPkceVerifier(state) {\n const item = pkceVerifiers.get(state)\n if (!item) { return undefined }\n if (item.expiresAt < Date.now() / 1000) {\n pkceVerifiers.delete(state)\n return undefined\n }\n return item.verifier\n },\n async deletePkceVerifier(state) { pkceVerifiers.delete(state) },\n\n async saveClient(clientId, data) { clients.set(clientId, data) },\n async getClient(clientId) { return clients.get(clientId) },\n\n async saveRefreshToken(token, data) { refreshTokens.set(token, data) },\n async getRefreshToken(token) { return withTtl(refreshTokens, token) },\n async deleteRefreshToken(token) { refreshTokens.delete(token) }\n }\n}\n","export function matchRedirectUri(uri: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n const regex = patternToRegex(pattern)\n return regex.test(uri)\n })\n}\n\nfunction patternToRegex(pattern: string): RegExp {\n const escaped = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n return new RegExp(`^${escaped}$`)\n}\n","import { randomBytes, randomUUID } from 'crypto'\nimport { SignJWT, jwtVerify } from 'jose'\nimport { createMemoryStore } from '../store/memory-store.js'\nimport { OAuthStore } from './store.js'\nimport { AuthInfo, OAuthProvider, OAuthResponse } from './types.js'\nimport { matchRedirectUri } from './uri.js'\n\nexport interface OAuthProxyConfig {\n authorizeUrl: string\n tokenUrl: string\n userinfoUrl?: string\n clientId: string\n clientSecret: string\n resourceUrl: string\n redirectUris: string[]\n requiredScopes?: string[]\n callbackPath?: string\n signingKey?: string\n tokenTtl?: number\n store?: OAuthStore\n}\n\nconst CORS_HEADERS: Record<string, string> = {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',\n 'Access-Control-Allow-Headers': '*'\n}\n\nfunction generateCode(): string {\n return randomBytes(32).toString('base64url')\n}\n\nasync function generatePkce(): Promise<{ verifier: string; challenge: string }> {\n const verifier = randomBytes(32).toString('base64url')\n const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier))\n const challenge = Buffer.from(hash).toString('base64url')\n return { verifier, challenge }\n}\n\nasync function verifyPkce(verifier: string, challenge: string): Promise<boolean> {\n const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier))\n const computed = Buffer.from(hash).toString('base64url')\n return computed === challenge\n}\n\nfunction jsonResponse(status: number, body: Record<string, unknown>, headers: Record<string, string> = {}): OAuthResponse {\n return {\n status,\n headers: { 'Content-Type': 'application/json', ...CORS_HEADERS, ...headers },\n body\n }\n}\n\nfunction redirectResponse(url: string): OAuthResponse {\n return {\n status: 302,\n headers: { Location: url, ...CORS_HEADERS },\n body: undefined\n }\n}\n\nfunction errorResponse(status: number, error: string, description: string): OAuthResponse {\n return jsonResponse(status, { error, error_description: description })\n}\n\nasync function signAccessToken(\n key: Uint8Array,\n opts: { scopes: string[]; email?: string; sub?: string; clientId: string; issuer: string; ttl: number }\n): Promise<string> {\n const now = Math.floor(Date.now() / 1000)\n return new SignJWT({ scope: opts.scopes.join(' '), email: opts.email })\n .setProtectedHeader({ alg: 'HS256' })\n .setSubject(opts.sub ?? opts.email ?? opts.clientId)\n .setIssuer(opts.issuer)\n .setAudience(opts.issuer)\n .setIssuedAt(now)\n .setExpirationTime(now + opts.ttl)\n .sign(key)\n}\n\nasync function handleRegister(\n req: { body?: Record<string, string> },\n store: OAuthStore,\n allowedRedirectUris: string[]\n): Promise<OAuthResponse> {\n const body = req.body\n if (!body) { return errorResponse(400, 'invalid_request', 'Missing request body') }\n\n const redirectUris = body.redirect_uris\n if (!redirectUris) { return errorResponse(400, 'invalid_request', 'redirect_uris is required') }\n\n let uris: string[]\n try {\n uris = typeof redirectUris === 'string' ? JSON.parse(redirectUris) : redirectUris\n } catch {\n uris = [redirectUris]\n }\n\n if (!Array.isArray(uris) || uris.length === 0) {\n return errorResponse(400, 'invalid_request', 'redirect_uris must be a non-empty array')\n }\n\n for (const uri of uris) {\n if (!matchRedirectUri(uri, allowedRedirectUris)) {\n return errorResponse(400, 'invalid_redirect_uri', `Redirect URI not allowed: ${uri}`)\n }\n }\n\n const clientId = randomUUID()\n const clientSecret = randomBytes(32).toString('base64url')\n const registration = {\n clientId,\n clientSecret,\n redirectUris: uris,\n clientName: body.client_name,\n createdAt: Date.now() / 1000\n }\n\n await store.saveClient(clientId, registration)\n\n return jsonResponse(201, {\n client_id: clientId,\n client_secret: clientSecret,\n redirect_uris: uris,\n client_name: body.client_name,\n token_endpoint_auth_method: 'none'\n })\n}\n\nasync function resolveClient(\n clientId: string,\n store: OAuthStore,\n allowedRedirectUris: string[]\n): Promise<{ clientId: string; clientSecret: string; redirectUris: string[]; clientName?: string; createdAt: number } | OAuthResponse> {\n const existing = await store.getClient(clientId)\n if (existing) { return existing }\n\n if (!clientId.startsWith('https://')) {\n return errorResponse(400, 'invalid_client', 'Unknown client_id')\n }\n\n try {\n const metaRes = await fetch(clientId)\n if (!metaRes.ok) {\n return errorResponse(400, 'invalid_client', 'Failed to fetch client metadata document')\n }\n const meta = await metaRes.json() as Record<string, unknown>\n const metaRedirectUris = meta.redirect_uris as string[] | undefined\n if (!Array.isArray(metaRedirectUris) || metaRedirectUris.length === 0) {\n return errorResponse(400, 'invalid_client', 'Client metadata must include redirect_uris')\n }\n for (const uri of metaRedirectUris) {\n if (!matchRedirectUri(uri, allowedRedirectUris)) {\n return errorResponse(400, 'invalid_redirect_uri', `Redirect URI not allowed: ${uri}`)\n }\n }\n const client = {\n clientId,\n clientSecret: '',\n redirectUris: metaRedirectUris,\n clientName: meta.client_name as string | undefined,\n createdAt: Date.now() / 1000\n }\n await store.saveClient(clientId, client)\n return client\n } catch {\n return errorResponse(400, 'invalid_client', 'Failed to fetch client metadata document')\n }\n}\n\nasync function exchangeUpstreamCode(\n code: string,\n config: OAuthProxyConfig,\n callbackPath: string,\n pkceVerifier: string\n): Promise<{ accessToken: string; idToken?: string } | OAuthResponse> {\n const tokenBody = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: `${config.resourceUrl}${callbackPath}`,\n client_id: config.clientId,\n client_secret: config.clientSecret,\n code_verifier: pkceVerifier\n })\n\n const tokenResponse = await fetch(config.tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: tokenBody.toString()\n })\n\n if (!tokenResponse.ok) {\n const errorBody = await tokenResponse.text()\n console.error('Upstream token exchange failed:', errorBody)\n return errorResponse(502, 'upstream_error', 'Failed to exchange code with upstream provider')\n }\n\n const tokens = await tokenResponse.json() as Record<string, unknown>\n return {\n accessToken: tokens.access_token as string,\n idToken: tokens.id_token as string | undefined\n }\n}\n\nasync function fetchUserinfo(\n userinfoUrl: string | undefined,\n accessToken: string\n): Promise<{ email?: string; sub?: string }> {\n if (!userinfoUrl || !accessToken) { return {} }\n try {\n const res = await fetch(userinfoUrl, {\n headers: { Authorization: `Bearer ${accessToken}` }\n })\n if (res.ok) {\n const userinfo = await res.json() as Record<string, unknown>\n return { email: userinfo.email as string | undefined, sub: userinfo.sub as string | undefined }\n }\n } catch {\n // Userinfo fetch is best-effort\n }\n return {}\n}\n\nasync function handleRefreshToken(\n body: Record<string, string>,\n store: OAuthStore,\n getSigningKey: () => Promise<Uint8Array>,\n config: { resourceUrl: string; tokenTtl: number }\n): Promise<OAuthResponse> {\n const refreshToken = body.refresh_token\n if (!refreshToken) {\n return errorResponse(400, 'invalid_request', 'Missing refresh_token')\n }\n\n const tokenData = await store.getRefreshToken(refreshToken)\n if (!tokenData) {\n return errorResponse(400, 'invalid_grant', 'Invalid or expired refresh token')\n }\n\n const key = await getSigningKey()\n const accessToken = await signAccessToken(key, {\n scopes: tokenData.scopes,\n email: tokenData.email,\n sub: tokenData.sub,\n clientId: tokenData.clientId,\n issuer: config.resourceUrl,\n ttl: config.tokenTtl\n })\n\n return jsonResponse(200, {\n access_token: accessToken,\n token_type: 'Bearer',\n expires_in: config.tokenTtl,\n scope: tokenData.scopes.join(' '),\n refresh_token: refreshToken\n })\n}\n\nasync function handleAuthorizationCode(\n body: Record<string, string>,\n store: OAuthStore,\n getSigningKey: () => Promise<Uint8Array>,\n config: { resourceUrl: string; tokenTtl: number }\n): Promise<OAuthResponse> {\n const code = body.code\n const redirectUri = body.redirect_uri\n const clientId = body.client_id\n const codeVerifier = body.code_verifier\n\n if (!code || !codeVerifier || !clientId) {\n return errorResponse(400, 'invalid_request', 'Missing required parameters')\n }\n\n const authCode = await store.getAuthCode(code)\n if (!authCode) { return errorResponse(400, 'invalid_grant', 'Invalid or expired authorization code') }\n\n await store.deleteAuthCode(code)\n\n if (authCode.clientId !== clientId) {\n return errorResponse(400, 'invalid_grant', 'client_id mismatch')\n }\n if (redirectUri && authCode.redirectUri !== redirectUri) {\n return errorResponse(400, 'invalid_grant', 'redirect_uri mismatch')\n }\n\n const pkceValid = await verifyPkce(codeVerifier, authCode.codeChallenge)\n if (!pkceValid) {\n return errorResponse(400, 'invalid_grant', 'PKCE verification failed')\n }\n\n const key = await getSigningKey()\n const accessToken = await signAccessToken(key, {\n scopes: authCode.scopes,\n email: authCode.email,\n sub: authCode.sub,\n clientId: authCode.clientId,\n issuer: config.resourceUrl,\n ttl: config.tokenTtl\n })\n\n const refreshToken = randomBytes(32).toString('base64url')\n await store.saveRefreshToken(refreshToken, {\n clientId: authCode.clientId,\n scopes: authCode.scopes,\n email: authCode.email,\n sub: authCode.sub,\n expiresAt: Math.floor(Date.now() / 1000) + 30 * 24 * 3600\n })\n\n return jsonResponse(200, {\n access_token: accessToken,\n token_type: 'Bearer',\n expires_in: config.tokenTtl,\n scope: authCode.scopes.join(' '),\n refresh_token: refreshToken\n })\n}\n\nexport function createOAuthProxy(config: OAuthProxyConfig): OAuthProvider {\n const store = config.store ?? createMemoryStore()\n const callbackPath = config.callbackPath ?? '/auth/callback'\n const tokenTtl = config.tokenTtl ?? 3600\n const scopes = config.requiredScopes ?? []\n\n let signingKey: Uint8Array | null = null\n\n async function getSigningKey(): Promise<Uint8Array> {\n if (signingKey) { return signingKey }\n if (config.signingKey) {\n signingKey = new TextEncoder().encode(config.signingKey)\n } else {\n signingKey = randomBytes(32)\n }\n return signingKey\n }\n\n return {\n metadata(): OAuthResponse {\n return jsonResponse(200, {\n issuer: config.resourceUrl,\n authorization_endpoint: `${config.resourceUrl}/authorize`,\n token_endpoint: `${config.resourceUrl}/token`,\n registration_endpoint: `${config.resourceUrl}/register`,\n response_types_supported: ['code'],\n grant_types_supported: ['authorization_code', 'refresh_token'],\n code_challenge_methods_supported: ['S256'],\n token_endpoint_auth_methods_supported: ['none', 'client_secret_post'],\n scopes_supported: scopes\n }, { 'Cache-Control': 'max-age=3600' })\n },\n\n register(req) {\n return handleRegister(req, store, config.redirectUris)\n },\n\n async authorize(req): Promise<OAuthResponse> {\n const params = req.url.searchParams\n const responseType = params.get('response_type')\n const clientId = params.get('client_id')\n const redirectUri = params.get('redirect_uri')\n const scope = params.get('scope') ?? ''\n const clientState = params.get('state') ?? ''\n const codeChallenge = params.get('code_challenge')\n const codeChallengeMethod = params.get('code_challenge_method')\n const resource = params.get('resource')\n\n if (responseType !== 'code') {\n return errorResponse(400, 'unsupported_response_type', 'Only response_type=code is supported')\n }\n if (!clientId) { return errorResponse(400, 'invalid_request', 'client_id is required') }\n if (!redirectUri) { return errorResponse(400, 'invalid_request', 'redirect_uri is required') }\n if (!codeChallenge || codeChallengeMethod !== 'S256') {\n return errorResponse(400, 'invalid_request', 'PKCE with S256 is required')\n }\n\n const result = await resolveClient(clientId, store, config.redirectUris)\n if ('status' in result) { return result }\n const client = result\n\n if (!client.redirectUris.includes(redirectUri)) {\n return errorResponse(400, 'invalid_redirect_uri', 'redirect_uri does not match registered URIs')\n }\n\n const proxyState = randomUUID()\n const pkce = await generatePkce()\n\n await store.savePendingAuth(proxyState, {\n clientId,\n redirectUri,\n codeChallenge,\n codeChallengeMethod,\n scope,\n clientState,\n resource: resource ?? undefined,\n expiresAt: Date.now() / 1000 + 600\n })\n\n await store.savePkceVerifier(proxyState, pkce.verifier)\n\n const upstreamScopes = scopes.length > 0 ? scopes.join(' ') : scope\n const upstreamUrl = new URL(config.authorizeUrl)\n upstreamUrl.searchParams.set('response_type', 'code')\n upstreamUrl.searchParams.set('client_id', config.clientId)\n upstreamUrl.searchParams.set('redirect_uri', `${config.resourceUrl}${callbackPath}`)\n upstreamUrl.searchParams.set('scope', upstreamScopes)\n upstreamUrl.searchParams.set('state', proxyState)\n upstreamUrl.searchParams.set('code_challenge', pkce.challenge)\n upstreamUrl.searchParams.set('code_challenge_method', 'S256')\n if (params.has('access_type')) {\n upstreamUrl.searchParams.set('access_type', params.get('access_type')!)\n }\n\n return redirectResponse(upstreamUrl.toString())\n },\n\n async callback(req): Promise<OAuthResponse> {\n const params = req.url.searchParams\n const upstreamCode = params.get('code')\n const proxyState = params.get('state')\n const upstreamError = params.get('error')\n\n if (upstreamError) {\n return errorResponse(400, 'upstream_error', params.get('error_description') ?? upstreamError)\n }\n if (!upstreamCode || !proxyState) {\n return errorResponse(400, 'invalid_request', 'Missing code or state')\n }\n\n const pending = await store.getPendingAuth(proxyState)\n if (!pending) { return errorResponse(400, 'invalid_request', 'Unknown or expired state') }\n\n const pkceVerifier = await store.getPkceVerifier(proxyState)\n if (!pkceVerifier) { return errorResponse(400, 'invalid_request', 'Missing PKCE verifier') }\n\n await store.deletePendingAuth(proxyState)\n await store.deletePkceVerifier(proxyState)\n\n const upstream = await exchangeUpstreamCode(upstreamCode, config, callbackPath, pkceVerifier)\n if ('status' in upstream) { return upstream }\n\n const userinfo = await fetchUserinfo(config.userinfoUrl, upstream.accessToken)\n\n const mcpCode = generateCode()\n await store.saveAuthCode(mcpCode, {\n clientId: pending.clientId,\n redirectUri: pending.redirectUri,\n codeChallenge: pending.codeChallenge,\n codeChallengeMethod: pending.codeChallengeMethod,\n scopes: pending.scope.split(' ').filter(Boolean),\n upstreamAccessToken: upstream.accessToken,\n upstreamIdToken: upstream.idToken,\n email: userinfo.email,\n sub: userinfo.sub,\n expiresAt: Date.now() / 1000 + 600\n })\n\n const clientRedirect = new URL(pending.redirectUri)\n clientRedirect.searchParams.set('code', mcpCode)\n if (pending.clientState) {\n clientRedirect.searchParams.set('state', pending.clientState)\n }\n\n return redirectResponse(clientRedirect.toString())\n },\n\n async token(req): Promise<OAuthResponse> {\n const body = req.body\n if (!body) { return errorResponse(400, 'invalid_request', 'Missing request body') }\n\n const tokenConfig = { resourceUrl: config.resourceUrl, tokenTtl }\n\n if (body.grant_type === 'refresh_token') {\n return handleRefreshToken(body, store, getSigningKey, tokenConfig)\n }\n if (body.grant_type !== 'authorization_code') {\n return errorResponse(400, 'unsupported_grant_type', 'Supported grant types: authorization_code, refresh_token')\n }\n return handleAuthorizationCode(body, store, getSigningKey, tokenConfig)\n },\n\n async verifyToken(token): Promise<AuthInfo | undefined> {\n try {\n const key = await getSigningKey()\n const { payload } = await jwtVerify(token, key, {\n issuer: config.resourceUrl,\n audience: config.resourceUrl\n })\n return {\n token,\n clientId: payload.sub ?? undefined,\n scopes: typeof payload.scope === 'string' ? payload.scope.split(' ').filter(Boolean) : [],\n expiresAt: payload.exp,\n email: payload.email as string | undefined\n }\n } catch {\n return undefined\n }\n }\n }\n}\n","import { AuthConfig } from '../types.js'\nimport { createOAuthProxy } from './proxy.js'\nimport { OAuthStore } from './store.js'\n\nexport interface GoogleOAuthOptions {\n clientId: string\n clientSecret: string\n resourceUrl: string\n redirectUris: string[]\n requiredScopes?: string[]\n callbackPath?: string\n signingKey?: string\n tokenTtl?: number\n store?: OAuthStore\n}\n\nexport function google(options: GoogleOAuthOptions): AuthConfig {\n const provider = createOAuthProxy({\n authorizeUrl: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenUrl: 'https://oauth2.googleapis.com/token',\n userinfoUrl: 'https://openidconnect.googleapis.com/v1/userinfo',\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n resourceUrl: options.resourceUrl,\n redirectUris: options.redirectUris,\n requiredScopes: options.requiredScopes ?? ['openid', 'email'],\n callbackPath: options.callbackPath,\n signingKey: options.signingKey,\n tokenTtl: options.tokenTtl,\n store: options.store\n })\n\n return {\n verifyToken: (token) => provider.verifyToken(token),\n required: true,\n resourceUrl: options.resourceUrl,\n authorizationServers: [options.resourceUrl],\n provider,\n callbackPath: options.callbackPath ?? '/auth/callback'\n }\n}\n","import { existsSync, readFileSync, writeFileSync } from 'fs'\nimport { AuthCodeData, ClientRegistration, OAuthStore, PendingAuthData, RefreshTokenData } from '../provider/store.js'\n\nfunction withTtl<T extends { expiresAt: number }>(obj: Record<string, T>, key: string): T | undefined {\n const item = obj[key]\n if (!item) { return undefined }\n if (item.expiresAt < Date.now() / 1000) {\n delete obj[key]\n return undefined\n }\n return item\n}\n\ninterface JsonStore {\n authCodes: Record<string, AuthCodeData>\n pendingAuths: Record<string, PendingAuthData>\n pkceVerifiers: Record<string, { verifier: string; expiresAt: number }>\n clients: Record<string, ClientRegistration>\n refreshTokens: Record<string, RefreshTokenData>\n}\n\nfunction writeJsonStore(path: string, store: JsonStore) {\n writeFileSync(path, JSON.stringify(store, null, 2), 'utf-8')\n}\n\nfunction readJsonStore(path: string): JsonStore {\n if (!existsSync(path)) {\n return {\n authCodes: {},\n pendingAuths: {},\n pkceVerifiers: {},\n clients: {},\n refreshTokens: {}\n }\n }\n const data = JSON.parse(readFileSync(path, 'utf-8'))\n if (!data.refreshTokens) { data.refreshTokens = {} }\n return data\n}\n\nexport function createJsonStore(path: string): OAuthStore {\n const store: JsonStore = readJsonStore(path)\n\n return {\n async saveAuthCode(code, data) {\n store.authCodes[code] = data\n writeJsonStore(path, store)\n },\n async getAuthCode(code) { return withTtl(store.authCodes, code) },\n async deleteAuthCode(code) {\n delete store.authCodes[code]\n writeJsonStore(path, store)\n },\n async savePendingAuth(state, data) {\n store.pendingAuths[state] = data\n writeJsonStore(path, store)\n },\n async getPendingAuth(state) { return withTtl(store.pendingAuths, state) },\n async deletePendingAuth(state) {\n delete store.pendingAuths[state]\n writeJsonStore(path, store)\n },\n async savePkceVerifier(state, verifier) {\n store.pkceVerifiers[state] = { verifier, expiresAt: Date.now() / 1000 + 600 }\n writeJsonStore(path, store)\n },\n async getPkceVerifier(state) {\n const item = store.pkceVerifiers[state]\n if (!item) { return undefined }\n if (item.expiresAt < Date.now() / 1000) {\n delete store.pkceVerifiers[state]\n writeJsonStore(path, store)\n return undefined\n }\n return item.verifier\n },\n async deletePkceVerifier(state) {\n delete store.pkceVerifiers[state]\n writeJsonStore(path, store)\n },\n async saveClient(clientId, data) {\n store.clients[clientId] = data\n writeJsonStore(path, store)\n },\n async getClient(clientId) { return store.clients[clientId] },\n\n async saveRefreshToken(token, data) {\n store.refreshTokens[token] = data\n writeJsonStore(path, store)\n },\n async getRefreshToken(token) { return withTtl(store.refreshTokens, token) },\n async deleteRefreshToken(token) {\n delete store.refreshTokens[token]\n writeJsonStore(path, store)\n }\n }\n}\n","import type { OAuthStore } from '../provider/store.js'\n\nexport interface RedisClient {\n get(key: string): Promise<unknown>\n set(key: string, value: string, options?: { ex?: number }): Promise<unknown>\n del(key: string): Promise<unknown>\n}\n\nexport interface RedisStoreOptions {\n client: RedisClient\n prefix?: string\n}\n\nexport function createRedisStore(options: RedisStoreOptions): OAuthStore {\n const { client, prefix = 'silkweave:oauth:' } = options\n\n function key(namespace: string, id: string) {\n return `${prefix}${namespace}:${id}`\n }\n\n function ttlFromExpiry(expiresAt: number): number | undefined {\n const seconds = Math.floor(expiresAt - Date.now() / 1000)\n return seconds > 0 ? seconds : undefined\n }\n\n async function save<T>(namespace: string, id: string, data: T, expiresAt?: number) {\n const opts: { ex?: number } = {}\n const ttl = expiresAt != null ? ttlFromExpiry(expiresAt) : undefined\n if (ttl != null) { opts.ex = ttl }\n await client.set(key(namespace, id), JSON.stringify(data), opts)\n }\n\n async function load<T>(namespace: string, id: string): Promise<T | undefined> {\n const raw = await client.get(key(namespace, id))\n if (raw == null) { return undefined }\n return (typeof raw === 'string' ? JSON.parse(raw) : raw) as T\n }\n\n async function remove(namespace: string, id: string) {\n await client.del(key(namespace, id))\n }\n\n return {\n async saveAuthCode(code, data) { await save('auth-code', code, data, data.expiresAt) },\n async getAuthCode(code) { return load('auth-code', code) },\n async deleteAuthCode(code) { await remove('auth-code', code) },\n\n async savePendingAuth(state, data) { await save('pending-auth', state, data, data.expiresAt) },\n async getPendingAuth(state) { return load('pending-auth', state) },\n async deletePendingAuth(state) { await remove('pending-auth', state) },\n\n async savePkceVerifier(state, verifier) {\n const expiresAt = Date.now() / 1000 + 600\n await save('pkce-verifier', state, { verifier, expiresAt }, expiresAt)\n },\n async getPkceVerifier(state) {\n const item = await load<{ verifier: string; expiresAt: number }>('pkce-verifier', state)\n return item?.verifier\n },\n async deletePkceVerifier(state) { await remove('pkce-verifier', state) },\n\n async saveClient(clientId, data) { await save('client', clientId, data) },\n async getClient(clientId) { return load('client', clientId) },\n\n async saveRefreshToken(token, data) { await save('refresh-token', token, data, data.expiresAt) },\n async getRefreshToken(token) { return load('refresh-token', token) },\n async deleteRefreshToken(token) { await remove('refresh-token', token) }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"oauth.mjs","names":["withTtl","client"],"sources":["../src/store/memory-store.ts","../src/provider/ssrf.ts","../src/provider/uri.ts","../src/provider/proxy.ts","../src/provider/google.ts","../src/store/json-store.ts","../src/store/redis-store.ts"],"sourcesContent":["import { AuthCodeData, ClientRegistration, OAuthStore, PendingAuthData, RefreshTokenData } from '../provider/store.js'\n\nfunction withTtl<T extends { expiresAt: number }>(map: Map<string, T>, key: string): T | undefined {\n const item = map.get(key)\n if (!item) { return undefined }\n if (item.expiresAt < Date.now() / 1000) {\n map.delete(key)\n return undefined\n }\n return item\n}\n\nexport function createMemoryStore(): OAuthStore {\n const authCodes = new Map<string, AuthCodeData>()\n const pendingAuths = new Map<string, PendingAuthData>()\n const pkceVerifiers = new Map<string, { verifier: string; expiresAt: number }>()\n const clients = new Map<string, ClientRegistration>()\n const refreshTokens = new Map<string, RefreshTokenData>()\n\n return {\n async saveAuthCode(code, data) { authCodes.set(code, data) },\n async getAuthCode(code) { return withTtl(authCodes, code) },\n async deleteAuthCode(code) { authCodes.delete(code) },\n\n async savePendingAuth(state, data) { pendingAuths.set(state, data) },\n async getPendingAuth(state) { return withTtl(pendingAuths, state) },\n async deletePendingAuth(state) { pendingAuths.delete(state) },\n\n async savePkceVerifier(state, verifier) {\n pkceVerifiers.set(state, { verifier, expiresAt: Date.now() / 1000 + 600 })\n },\n async getPkceVerifier(state) {\n const item = pkceVerifiers.get(state)\n if (!item) { return undefined }\n if (item.expiresAt < Date.now() / 1000) {\n pkceVerifiers.delete(state)\n return undefined\n }\n return item.verifier\n },\n async deletePkceVerifier(state) { pkceVerifiers.delete(state) },\n\n async saveClient(clientId, data) { clients.set(clientId, data) },\n async getClient(clientId) { return clients.get(clientId) },\n\n async saveRefreshToken(token, data) { refreshTokens.set(token, data) },\n async getRefreshToken(token) { return withTtl(refreshTokens, token) },\n async deleteRefreshToken(token) { refreshTokens.delete(token) }\n }\n}\n","/**\n * Best-effort SSRF guard for the Client ID Metadata Document (CIMD) fetch. An\n * unknown `client_id` that is an `https://` URL is fetched by the proxy, so an\n * attacker could point it at internal services or a cloud metadata endpoint\n * (`https://169.254.169.254/...`). This blocks the high-value targets without\n * pulling in `node:dns` (the OAuth proxy also runs on edge/Workers).\n *\n * Coverage and residual risk: IP-literal hosts in private/loopback/link-local/\n * reserved ranges and `localhost` are rejected synchronously; a *hostname* that\n * resolves to a private IP is NOT caught here (no DNS at this layer). Callers\n * MUST additionally disallow redirects and set a timeout on the fetch. Deployments\n * with strict internal-network requirements should enforce egress policy at the\n * network layer.\n */\nexport function assertSafeMetadataUrl(raw: string): { ok: true } | { ok: false; reason: string } {\n let url: URL\n try {\n url = new URL(raw)\n } catch {\n return { ok: false, reason: 'invalid URL' }\n }\n if (url.protocol !== 'https:') {\n return { ok: false, reason: 'must be https' }\n }\n const host = url.hostname.toLowerCase()\n if (host === 'localhost' || host.endsWith('.localhost')) {\n return { ok: false, reason: 'loopback host' }\n }\n if (isBlockedIpLiteral(host)) {\n return { ok: false, reason: 'private or reserved address' }\n }\n return { ok: true }\n}\n\nfunction isBlockedIpLiteral(host: string): boolean {\n // URL.hostname keeps IPv6 literals in brackets (`[::1]`); strip them first.\n if (host.startsWith('[') && host.endsWith(']')) {\n return isBlockedIpv6(host.slice(1, -1))\n }\n const v4 = parseIpv4(host)\n return v4 !== null && isBlockedIpv4(v4)\n}\n\nfunction parseIpv4(host: string): [number, number, number, number] | null {\n const parts = host.split('.')\n if (parts.length !== 4) { return null }\n const octets: number[] = []\n for (const part of parts) {\n if (!/^\\d{1,3}$/.test(part)) { return null }\n const n = Number(part)\n if (n > 255) { return null }\n octets.push(n)\n }\n return octets as [number, number, number, number]\n}\n\nfunction isBlockedIpv4([a, b]: [number, number, number, number]): boolean {\n if (a === 0) { return true } // 0.0.0.0/8\n if (a === 10) { return true } // 10.0.0.0/8\n if (a === 127) { return true } // 127.0.0.0/8 loopback\n if (a === 169 && b === 254) { return true } // 169.254.0.0/16 link-local (metadata)\n if (a === 172 && b >= 16 && b <= 31) { return true } // 172.16.0.0/12\n if (a === 192 && b === 168) { return true } // 192.168.0.0/16\n if (a === 100 && b >= 64 && b <= 127) { return true } // 100.64.0.0/10 CGNAT\n if (a === 192 && b === 0) { return true } // 192.0.0.0/24 IETF\n if (a === 198 && (b === 18 || b === 19)) { return true } // 198.18.0.0/15 benchmark\n if (a >= 224) { return true } // 224.0.0.0/4 multicast + 240.0.0.0/4 reserved\n return false\n}\n\nfunction isBlockedIpv6(host: string): boolean {\n const h = host.toLowerCase()\n if (h === '::' || h === '::1') { return true } // unspecified, loopback\n // IPv4-mapped (::ffff:...). URL normalizes the embedded v4 to hex\n // (`::ffff:a00:1`), so rather than decode it, block all mapped addresses -\n // a legit client metadata URL never uses an IPv4-mapped IPv6 literal.\n if (h.startsWith('::ffff:')) { return true }\n if (h.startsWith('fe8') || h.startsWith('fe9') || h.startsWith('fea') || h.startsWith('feb')) {\n return true // fe80::/10 link-local\n }\n if (h.startsWith('fc') || h.startsWith('fd')) { return true } // fc00::/7 unique-local\n if (h.startsWith('ff')) { return true } // ff00::/8 multicast\n return false\n}\n","/**\n * Match a concrete `redirect_uri` against the operator's allow-list patterns.\n *\n * Matching is done per URL *component* (scheme, userinfo, host, port, path),\n * NOT by testing one glob-to-regex against the whole string. A naive\n * whole-string `*`->`.*` lets a wildcard cross authority delimiters, so\n * `https://*.example.com/cb` would match `https://attacker.com/x.example.com/cb`\n * and `http://localhost:*` would match `http://localhost:x@attacker.com/cb`\n * (userinfo injection) - both hand the auth code to an attacker host. Comparing\n * components defeats that: the target is parsed with the URL parser (so its host\n * is exactly its host), and a pattern with no userinfo rejects any target that\n * carries userinfo.\n */\nexport function matchRedirectUri(uri: string, patterns: string[]): boolean {\n let target: URL\n try {\n target = new URL(uri)\n } catch {\n return false\n }\n return patterns.some((pattern) => matchPattern(target, pattern))\n}\n\n/** Escape regex metacharacters, then expand `*` to `.*` (component-scoped). */\nfunction componentRegex(part: string): RegExp {\n const escaped = part\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*/g, '.*')\n return new RegExp(`^${escaped}$`)\n}\n\n/**\n * A pattern may contain `*` in the host (`*.example.com`), the port\n * (`http://localhost:*`), or the path (`https://app.example.com/*`). The\n * concrete `uri` is always a valid URL. Split the pattern into\n * scheme / userinfo / host / (port + path) and match each against the target's\n * corresponding component. Because the port and path are matched as one tail,\n * a trailing `*` (e.g. loopback `http://localhost:*`) still spans any port and\n * path, as before - it just can no longer escape the authority.\n */\nfunction matchPattern(target: URL, pattern: string): boolean {\n const parsed = /^([^:]+:)\\/\\/(?:([^@/]*)@)?([^/:?#]*)(.*)$/.exec(pattern)\n if (!parsed) {\n return false\n }\n const [, scheme, userinfo, host, tail] = parsed\n\n if (scheme !== target.protocol) {\n return false\n }\n\n // Userinfo: a pattern without userinfo must reject any target that carries it\n // (this is what blocks `localhost:x@attacker.com`). With userinfo, match it.\n const targetUserinfo = target.password\n ? `${target.username}:${target.password}`\n : target.username\n if (userinfo === undefined) {\n if (targetUserinfo !== '') { return false }\n } else if (!componentRegex(userinfo).test(targetUserinfo)) {\n return false\n }\n\n if (!componentRegex(host).test(target.hostname)) {\n return false\n }\n\n const targetTail =\n (target.port ? `:${target.port}` : '') + target.pathname + target.search + target.hash\n return componentRegex(tail).test(targetTail)\n}\n","import { randomBytes, randomUUID } from 'crypto'\nimport { SignJWT, jwtVerify } from 'jose'\nimport { createMemoryStore } from '../store/memory-store.js'\nimport { OAuthStore } from './store.js'\nimport { AuthInfo, OAuthProvider, OAuthResponse } from './types.js'\nimport { assertSafeMetadataUrl } from './ssrf.js'\nimport { matchRedirectUri } from './uri.js'\n\nexport interface OAuthProxyConfig {\n authorizeUrl: string\n tokenUrl: string\n userinfoUrl?: string\n clientId: string\n clientSecret: string\n resourceUrl: string\n redirectUris: string[]\n requiredScopes?: string[]\n callbackPath?: string\n signingKey?: string\n tokenTtl?: number\n store?: OAuthStore\n}\n\nconst CORS_HEADERS: Record<string, string> = {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',\n 'Access-Control-Allow-Headers': '*'\n}\n\nfunction generateCode(): string {\n return randomBytes(32).toString('base64url')\n}\n\nasync function generatePkce(): Promise<{ verifier: string; challenge: string }> {\n const verifier = randomBytes(32).toString('base64url')\n const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier))\n const challenge = Buffer.from(hash).toString('base64url')\n return { verifier, challenge }\n}\n\nasync function verifyPkce(verifier: string, challenge: string): Promise<boolean> {\n const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier))\n const computed = Buffer.from(hash).toString('base64url')\n return computed === challenge\n}\n\nfunction jsonResponse(status: number, body: Record<string, unknown>, headers: Record<string, string> = {}): OAuthResponse {\n return {\n status,\n headers: { 'Content-Type': 'application/json', ...CORS_HEADERS, ...headers },\n body\n }\n}\n\nfunction redirectResponse(url: string): OAuthResponse {\n return {\n status: 302,\n headers: { Location: url, ...CORS_HEADERS },\n body: undefined\n }\n}\n\nfunction errorResponse(status: number, error: string, description: string): OAuthResponse {\n return jsonResponse(status, { error, error_description: description })\n}\n\nasync function signAccessToken(\n key: Uint8Array,\n opts: { scopes: string[]; email?: string; sub?: string; clientId: string; issuer: string; ttl: number }\n): Promise<string> {\n const now = Math.floor(Date.now() / 1000)\n return new SignJWT({ scope: opts.scopes.join(' '), email: opts.email })\n .setProtectedHeader({ alg: 'HS256' })\n .setSubject(opts.sub ?? opts.email ?? opts.clientId)\n .setIssuer(opts.issuer)\n .setAudience(opts.issuer)\n .setIssuedAt(now)\n .setExpirationTime(now + opts.ttl)\n .sign(key)\n}\n\nasync function handleRegister(\n req: { body?: Record<string, string> },\n store: OAuthStore,\n allowedRedirectUris: string[]\n): Promise<OAuthResponse> {\n const body = req.body\n if (!body) { return errorResponse(400, 'invalid_request', 'Missing request body') }\n\n const redirectUris = body.redirect_uris\n if (!redirectUris) { return errorResponse(400, 'invalid_request', 'redirect_uris is required') }\n\n let uris: string[]\n try {\n uris = typeof redirectUris === 'string' ? JSON.parse(redirectUris) : redirectUris\n } catch {\n uris = [redirectUris]\n }\n\n if (!Array.isArray(uris) || uris.length === 0) {\n return errorResponse(400, 'invalid_request', 'redirect_uris must be a non-empty array')\n }\n\n for (const uri of uris) {\n if (!matchRedirectUri(uri, allowedRedirectUris)) {\n return errorResponse(400, 'invalid_redirect_uri', `Redirect URI not allowed: ${uri}`)\n }\n }\n\n const clientId = randomUUID()\n const clientSecret = randomBytes(32).toString('base64url')\n const registration = {\n clientId,\n clientSecret,\n redirectUris: uris,\n clientName: body.client_name,\n createdAt: Date.now() / 1000\n }\n\n await store.saveClient(clientId, registration)\n\n return jsonResponse(201, {\n client_id: clientId,\n client_secret: clientSecret,\n redirect_uris: uris,\n client_name: body.client_name,\n token_endpoint_auth_method: 'none'\n })\n}\n\nasync function resolveClient(\n clientId: string,\n store: OAuthStore,\n allowedRedirectUris: string[]\n): Promise<{ clientId: string; clientSecret: string; redirectUris: string[]; clientName?: string; createdAt: number } | OAuthResponse> {\n const existing = await store.getClient(clientId)\n if (existing) { return existing }\n\n if (!clientId.startsWith('https://')) {\n return errorResponse(400, 'invalid_client', 'Unknown client_id')\n }\n\n const safety = assertSafeMetadataUrl(clientId)\n if (!safety.ok) {\n return errorResponse(400, 'invalid_client', 'Client metadata URL is not allowed')\n }\n\n try {\n // SSRF hardening: block redirects (a public URL could 3xx to an internal\n // one) and cap the request time. See assertSafeMetadataUrl for coverage.\n const metaRes = await fetch(clientId, { redirect: 'manual', signal: AbortSignal.timeout(5000) })\n if (metaRes.status >= 300 && metaRes.status < 400) {\n return errorResponse(400, 'invalid_client', 'Client metadata document must not redirect')\n }\n if (!metaRes.ok) {\n return errorResponse(400, 'invalid_client', 'Failed to fetch client metadata document')\n }\n const meta = await metaRes.json() as Record<string, unknown>\n const metaRedirectUris = meta.redirect_uris as string[] | undefined\n if (!Array.isArray(metaRedirectUris) || metaRedirectUris.length === 0) {\n return errorResponse(400, 'invalid_client', 'Client metadata must include redirect_uris')\n }\n for (const uri of metaRedirectUris) {\n if (!matchRedirectUri(uri, allowedRedirectUris)) {\n return errorResponse(400, 'invalid_redirect_uri', `Redirect URI not allowed: ${uri}`)\n }\n }\n const client = {\n clientId,\n clientSecret: '',\n redirectUris: metaRedirectUris,\n clientName: meta.client_name as string | undefined,\n createdAt: Date.now() / 1000\n }\n await store.saveClient(clientId, client)\n return client\n } catch {\n return errorResponse(400, 'invalid_client', 'Failed to fetch client metadata document')\n }\n}\n\nasync function exchangeUpstreamCode(\n code: string,\n config: OAuthProxyConfig,\n callbackPath: string,\n pkceVerifier: string\n): Promise<{ accessToken: string; idToken?: string } | OAuthResponse> {\n const tokenBody = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: `${config.resourceUrl}${callbackPath}`,\n client_id: config.clientId,\n client_secret: config.clientSecret,\n code_verifier: pkceVerifier\n })\n\n const tokenResponse = await fetch(config.tokenUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: tokenBody.toString()\n })\n\n if (!tokenResponse.ok) {\n const errorBody = await tokenResponse.text()\n console.error('Upstream token exchange failed:', errorBody)\n return errorResponse(502, 'upstream_error', 'Failed to exchange code with upstream provider')\n }\n\n const tokens = await tokenResponse.json() as Record<string, unknown>\n return {\n accessToken: tokens.access_token as string,\n idToken: tokens.id_token as string | undefined\n }\n}\n\nasync function fetchUserinfo(\n userinfoUrl: string | undefined,\n accessToken: string\n): Promise<{ email?: string; sub?: string }> {\n if (!userinfoUrl || !accessToken) { return {} }\n try {\n const res = await fetch(userinfoUrl, {\n headers: { Authorization: `Bearer ${accessToken}` }\n })\n if (res.ok) {\n const userinfo = await res.json() as Record<string, unknown>\n return { email: userinfo.email as string | undefined, sub: userinfo.sub as string | undefined }\n }\n } catch {\n // Userinfo fetch is best-effort\n }\n return {}\n}\n\nasync function handleRefreshToken(\n body: Record<string, string>,\n store: OAuthStore,\n getSigningKey: () => Promise<Uint8Array>,\n config: { resourceUrl: string; tokenTtl: number }\n): Promise<OAuthResponse> {\n const refreshToken = body.refresh_token\n if (!refreshToken) {\n return errorResponse(400, 'invalid_request', 'Missing refresh_token')\n }\n\n const tokenData = await store.getRefreshToken(refreshToken)\n if (!tokenData) {\n return errorResponse(400, 'invalid_grant', 'Invalid or expired refresh token')\n }\n\n // Bind the refresh token to the presenting client. Public clients\n // (token_endpoint_auth_method 'none') send client_id in the body; a token\n // presented by a different client_id than it was issued to is rejected.\n if (body.client_id && body.client_id !== tokenData.clientId) {\n return errorResponse(400, 'invalid_grant', 'refresh_token was not issued to this client')\n }\n\n const key = await getSigningKey()\n const accessToken = await signAccessToken(key, {\n scopes: tokenData.scopes,\n email: tokenData.email,\n sub: tokenData.sub,\n clientId: tokenData.clientId,\n issuer: config.resourceUrl,\n ttl: config.tokenTtl\n })\n\n // Rotate (OAuth 2.1 for public clients): invalidate the presented token and\n // issue a fresh one. Preserve the original absolute expiry so repeated\n // rotation cannot extend a leaked token's lifetime indefinitely.\n await store.deleteRefreshToken(refreshToken)\n const newRefreshToken = randomBytes(32).toString('base64url')\n await store.saveRefreshToken(newRefreshToken, {\n clientId: tokenData.clientId,\n scopes: tokenData.scopes,\n email: tokenData.email,\n sub: tokenData.sub,\n expiresAt: tokenData.expiresAt\n })\n\n return jsonResponse(200, {\n access_token: accessToken,\n token_type: 'Bearer',\n expires_in: config.tokenTtl,\n scope: tokenData.scopes.join(' '),\n refresh_token: newRefreshToken\n })\n}\n\nasync function handleAuthorizationCode(\n body: Record<string, string>,\n store: OAuthStore,\n getSigningKey: () => Promise<Uint8Array>,\n config: { resourceUrl: string; tokenTtl: number }\n): Promise<OAuthResponse> {\n const code = body.code\n const redirectUri = body.redirect_uri\n const clientId = body.client_id\n const codeVerifier = body.code_verifier\n\n if (!code || !codeVerifier || !clientId) {\n return errorResponse(400, 'invalid_request', 'Missing required parameters')\n }\n\n const authCode = await store.getAuthCode(code)\n if (!authCode) { return errorResponse(400, 'invalid_grant', 'Invalid or expired authorization code') }\n\n await store.deleteAuthCode(code)\n\n if (authCode.clientId !== clientId) {\n return errorResponse(400, 'invalid_grant', 'client_id mismatch')\n }\n if (redirectUri && authCode.redirectUri !== redirectUri) {\n return errorResponse(400, 'invalid_grant', 'redirect_uri mismatch')\n }\n\n const pkceValid = await verifyPkce(codeVerifier, authCode.codeChallenge)\n if (!pkceValid) {\n return errorResponse(400, 'invalid_grant', 'PKCE verification failed')\n }\n\n const key = await getSigningKey()\n const accessToken = await signAccessToken(key, {\n scopes: authCode.scopes,\n email: authCode.email,\n sub: authCode.sub,\n clientId: authCode.clientId,\n issuer: config.resourceUrl,\n ttl: config.tokenTtl\n })\n\n const refreshToken = randomBytes(32).toString('base64url')\n await store.saveRefreshToken(refreshToken, {\n clientId: authCode.clientId,\n scopes: authCode.scopes,\n email: authCode.email,\n sub: authCode.sub,\n expiresAt: Math.floor(Date.now() / 1000) + 30 * 24 * 3600\n })\n\n return jsonResponse(200, {\n access_token: accessToken,\n token_type: 'Bearer',\n expires_in: config.tokenTtl,\n scope: authCode.scopes.join(' '),\n refresh_token: refreshToken\n })\n}\n\nexport function createOAuthProxy(config: OAuthProxyConfig): OAuthProvider {\n const store = config.store ?? createMemoryStore()\n const callbackPath = config.callbackPath ?? '/auth/callback'\n const tokenTtl = config.tokenTtl ?? 3600\n const scopes = config.requiredScopes ?? []\n\n let signingKey: Uint8Array | null = null\n\n async function getSigningKey(): Promise<Uint8Array> {\n if (signingKey) { return signingKey }\n if (config.signingKey) {\n signingKey = new TextEncoder().encode(config.signingKey)\n } else {\n signingKey = randomBytes(32)\n }\n return signingKey\n }\n\n return {\n metadata(): OAuthResponse {\n return jsonResponse(200, {\n issuer: config.resourceUrl,\n authorization_endpoint: `${config.resourceUrl}/authorize`,\n token_endpoint: `${config.resourceUrl}/token`,\n registration_endpoint: `${config.resourceUrl}/register`,\n response_types_supported: ['code'],\n grant_types_supported: ['authorization_code', 'refresh_token'],\n code_challenge_methods_supported: ['S256'],\n token_endpoint_auth_methods_supported: ['none', 'client_secret_post'],\n scopes_supported: scopes\n }, { 'Cache-Control': 'max-age=3600' })\n },\n\n register(req) {\n return handleRegister(req, store, config.redirectUris)\n },\n\n async authorize(req): Promise<OAuthResponse> {\n const params = req.url.searchParams\n const responseType = params.get('response_type')\n const clientId = params.get('client_id')\n const redirectUri = params.get('redirect_uri')\n const scope = params.get('scope') ?? ''\n const clientState = params.get('state') ?? ''\n const codeChallenge = params.get('code_challenge')\n const codeChallengeMethod = params.get('code_challenge_method')\n const resource = params.get('resource')\n\n if (responseType !== 'code') {\n return errorResponse(400, 'unsupported_response_type', 'Only response_type=code is supported')\n }\n if (!clientId) { return errorResponse(400, 'invalid_request', 'client_id is required') }\n if (!redirectUri) { return errorResponse(400, 'invalid_request', 'redirect_uri is required') }\n if (!codeChallenge || codeChallengeMethod !== 'S256') {\n return errorResponse(400, 'invalid_request', 'PKCE with S256 is required')\n }\n\n const result = await resolveClient(clientId, store, config.redirectUris)\n if ('status' in result) { return result }\n const client = result\n\n if (!client.redirectUris.includes(redirectUri)) {\n return errorResponse(400, 'invalid_redirect_uri', 'redirect_uri does not match registered URIs')\n }\n\n const proxyState = randomUUID()\n const pkce = await generatePkce()\n\n await store.savePendingAuth(proxyState, {\n clientId,\n redirectUri,\n codeChallenge,\n codeChallengeMethod,\n scope,\n clientState,\n resource: resource ?? undefined,\n expiresAt: Date.now() / 1000 + 600\n })\n\n await store.savePkceVerifier(proxyState, pkce.verifier)\n\n const upstreamScopes = scopes.length > 0 ? scopes.join(' ') : scope\n const upstreamUrl = new URL(config.authorizeUrl)\n upstreamUrl.searchParams.set('response_type', 'code')\n upstreamUrl.searchParams.set('client_id', config.clientId)\n upstreamUrl.searchParams.set('redirect_uri', `${config.resourceUrl}${callbackPath}`)\n upstreamUrl.searchParams.set('scope', upstreamScopes)\n upstreamUrl.searchParams.set('state', proxyState)\n upstreamUrl.searchParams.set('code_challenge', pkce.challenge)\n upstreamUrl.searchParams.set('code_challenge_method', 'S256')\n if (params.has('access_type')) {\n upstreamUrl.searchParams.set('access_type', params.get('access_type')!)\n }\n\n return redirectResponse(upstreamUrl.toString())\n },\n\n async callback(req): Promise<OAuthResponse> {\n const params = req.url.searchParams\n const upstreamCode = params.get('code')\n const proxyState = params.get('state')\n const upstreamError = params.get('error')\n\n if (upstreamError) {\n return errorResponse(400, 'upstream_error', params.get('error_description') ?? upstreamError)\n }\n if (!upstreamCode || !proxyState) {\n return errorResponse(400, 'invalid_request', 'Missing code or state')\n }\n\n const pending = await store.getPendingAuth(proxyState)\n if (!pending) { return errorResponse(400, 'invalid_request', 'Unknown or expired state') }\n\n const pkceVerifier = await store.getPkceVerifier(proxyState)\n if (!pkceVerifier) { return errorResponse(400, 'invalid_request', 'Missing PKCE verifier') }\n\n await store.deletePendingAuth(proxyState)\n await store.deletePkceVerifier(proxyState)\n\n const upstream = await exchangeUpstreamCode(upstreamCode, config, callbackPath, pkceVerifier)\n if ('status' in upstream) { return upstream }\n\n const userinfo = await fetchUserinfo(config.userinfoUrl, upstream.accessToken)\n\n const mcpCode = generateCode()\n await store.saveAuthCode(mcpCode, {\n clientId: pending.clientId,\n redirectUri: pending.redirectUri,\n codeChallenge: pending.codeChallenge,\n codeChallengeMethod: pending.codeChallengeMethod,\n scopes: pending.scope.split(' ').filter(Boolean),\n upstreamAccessToken: upstream.accessToken,\n upstreamIdToken: upstream.idToken,\n email: userinfo.email,\n sub: userinfo.sub,\n expiresAt: Date.now() / 1000 + 600\n })\n\n const clientRedirect = new URL(pending.redirectUri)\n clientRedirect.searchParams.set('code', mcpCode)\n if (pending.clientState) {\n clientRedirect.searchParams.set('state', pending.clientState)\n }\n\n return redirectResponse(clientRedirect.toString())\n },\n\n async token(req): Promise<OAuthResponse> {\n const body = req.body\n if (!body) { return errorResponse(400, 'invalid_request', 'Missing request body') }\n\n const tokenConfig = { resourceUrl: config.resourceUrl, tokenTtl }\n\n if (body.grant_type === 'refresh_token') {\n return handleRefreshToken(body, store, getSigningKey, tokenConfig)\n }\n if (body.grant_type !== 'authorization_code') {\n return errorResponse(400, 'unsupported_grant_type', 'Supported grant types: authorization_code, refresh_token')\n }\n return handleAuthorizationCode(body, store, getSigningKey, tokenConfig)\n },\n\n async verifyToken(token): Promise<AuthInfo | undefined> {\n try {\n const key = await getSigningKey()\n const { payload } = await jwtVerify(token, key, {\n issuer: config.resourceUrl,\n audience: config.resourceUrl\n })\n return {\n token,\n clientId: payload.sub ?? undefined,\n scopes: typeof payload.scope === 'string' ? payload.scope.split(' ').filter(Boolean) : [],\n expiresAt: payload.exp,\n email: payload.email as string | undefined\n }\n } catch {\n return undefined\n }\n }\n }\n}\n","import { AuthConfig } from '../types.js'\nimport { createOAuthProxy } from './proxy.js'\nimport { OAuthStore } from './store.js'\n\nexport interface GoogleOAuthOptions {\n clientId: string\n clientSecret: string\n resourceUrl: string\n redirectUris: string[]\n requiredScopes?: string[]\n callbackPath?: string\n signingKey?: string\n tokenTtl?: number\n store?: OAuthStore\n}\n\nexport function google(options: GoogleOAuthOptions): AuthConfig {\n const provider = createOAuthProxy({\n authorizeUrl: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenUrl: 'https://oauth2.googleapis.com/token',\n userinfoUrl: 'https://openidconnect.googleapis.com/v1/userinfo',\n clientId: options.clientId,\n clientSecret: options.clientSecret,\n resourceUrl: options.resourceUrl,\n redirectUris: options.redirectUris,\n requiredScopes: options.requiredScopes ?? ['openid', 'email'],\n callbackPath: options.callbackPath,\n signingKey: options.signingKey,\n tokenTtl: options.tokenTtl,\n store: options.store\n })\n\n return {\n verifyToken: (token) => provider.verifyToken(token),\n required: true,\n resourceUrl: options.resourceUrl,\n authorizationServers: [options.resourceUrl],\n provider,\n callbackPath: options.callbackPath ?? '/auth/callback'\n }\n}\n","import { chmodSync, existsSync, readFileSync, writeFileSync } from 'fs'\nimport { AuthCodeData, ClientRegistration, OAuthStore, PendingAuthData, RefreshTokenData } from '../provider/store.js'\n\nfunction withTtl<T extends { expiresAt: number }>(obj: Record<string, T>, key: string): T | undefined {\n const item = obj[key]\n if (!item) { return undefined }\n if (item.expiresAt < Date.now() / 1000) {\n delete obj[key]\n return undefined\n }\n return item\n}\n\ninterface JsonStore {\n authCodes: Record<string, AuthCodeData>\n pendingAuths: Record<string, PendingAuthData>\n pkceVerifiers: Record<string, { verifier: string; expiresAt: number }>\n clients: Record<string, ClientRegistration>\n refreshTokens: Record<string, RefreshTokenData>\n}\n\nfunction writeJsonStore(path: string, store: JsonStore) {\n // The store holds client secrets, refresh tokens, upstream access/ID tokens\n // and PKCE verifiers in plaintext - keep it owner-only (0600), not the default\n // 0644. `mode` only applies on creation, so also chmod an existing file.\n writeFileSync(path, JSON.stringify(store, null, 2), { encoding: 'utf-8', mode: 0o600 })\n chmodSync(path, 0o600)\n}\n\nfunction readJsonStore(path: string): JsonStore {\n if (!existsSync(path)) {\n return {\n authCodes: {},\n pendingAuths: {},\n pkceVerifiers: {},\n clients: {},\n refreshTokens: {}\n }\n }\n const data = JSON.parse(readFileSync(path, 'utf-8'))\n if (!data.refreshTokens) { data.refreshTokens = {} }\n return data\n}\n\nexport function createJsonStore(path: string): OAuthStore {\n const store: JsonStore = readJsonStore(path)\n\n return {\n async saveAuthCode(code, data) {\n store.authCodes[code] = data\n writeJsonStore(path, store)\n },\n async getAuthCode(code) { return withTtl(store.authCodes, code) },\n async deleteAuthCode(code) {\n delete store.authCodes[code]\n writeJsonStore(path, store)\n },\n async savePendingAuth(state, data) {\n store.pendingAuths[state] = data\n writeJsonStore(path, store)\n },\n async getPendingAuth(state) { return withTtl(store.pendingAuths, state) },\n async deletePendingAuth(state) {\n delete store.pendingAuths[state]\n writeJsonStore(path, store)\n },\n async savePkceVerifier(state, verifier) {\n store.pkceVerifiers[state] = { verifier, expiresAt: Date.now() / 1000 + 600 }\n writeJsonStore(path, store)\n },\n async getPkceVerifier(state) {\n const item = store.pkceVerifiers[state]\n if (!item) { return undefined }\n if (item.expiresAt < Date.now() / 1000) {\n delete store.pkceVerifiers[state]\n writeJsonStore(path, store)\n return undefined\n }\n return item.verifier\n },\n async deletePkceVerifier(state) {\n delete store.pkceVerifiers[state]\n writeJsonStore(path, store)\n },\n async saveClient(clientId, data) {\n store.clients[clientId] = data\n writeJsonStore(path, store)\n },\n async getClient(clientId) { return store.clients[clientId] },\n\n async saveRefreshToken(token, data) {\n store.refreshTokens[token] = data\n writeJsonStore(path, store)\n },\n async getRefreshToken(token) { return withTtl(store.refreshTokens, token) },\n async deleteRefreshToken(token) {\n delete store.refreshTokens[token]\n writeJsonStore(path, store)\n }\n }\n}\n","import type { OAuthStore } from '../provider/store.js'\n\nexport interface RedisClient {\n get(key: string): Promise<unknown>\n set(key: string, value: string, options?: { ex?: number }): Promise<unknown>\n del(key: string): Promise<unknown>\n}\n\nexport interface RedisStoreOptions {\n client: RedisClient\n prefix?: string\n}\n\nexport function createRedisStore(options: RedisStoreOptions): OAuthStore {\n const { client, prefix = 'silkweave:oauth:' } = options\n\n function key(namespace: string, id: string) {\n return `${prefix}${namespace}:${id}`\n }\n\n function ttlFromExpiry(expiresAt: number): number | undefined {\n const seconds = Math.floor(expiresAt - Date.now() / 1000)\n return seconds > 0 ? seconds : undefined\n }\n\n async function save<T>(namespace: string, id: string, data: T, expiresAt?: number) {\n const opts: { ex?: number } = {}\n const ttl = expiresAt != null ? ttlFromExpiry(expiresAt) : undefined\n if (ttl != null) { opts.ex = ttl }\n await client.set(key(namespace, id), JSON.stringify(data), opts)\n }\n\n async function load<T>(namespace: string, id: string): Promise<T | undefined> {\n const raw = await client.get(key(namespace, id))\n if (raw == null) { return undefined }\n return (typeof raw === 'string' ? JSON.parse(raw) : raw) as T\n }\n\n async function remove(namespace: string, id: string) {\n await client.del(key(namespace, id))\n }\n\n return {\n async saveAuthCode(code, data) { await save('auth-code', code, data, data.expiresAt) },\n async getAuthCode(code) { return load('auth-code', code) },\n async deleteAuthCode(code) { await remove('auth-code', code) },\n\n async savePendingAuth(state, data) { await save('pending-auth', state, data, data.expiresAt) },\n async getPendingAuth(state) { return load('pending-auth', state) },\n async deletePendingAuth(state) { await remove('pending-auth', state) },\n\n async savePkceVerifier(state, verifier) {\n const expiresAt = Date.now() / 1000 + 600\n await save('pkce-verifier', state, { verifier, expiresAt }, expiresAt)\n },\n async getPkceVerifier(state) {\n const item = await load<{ verifier: string; expiresAt: number }>('pkce-verifier', state)\n return item?.verifier\n },\n async deletePkceVerifier(state) { await remove('pkce-verifier', state) },\n\n async saveClient(clientId, data) { await save('client', clientId, data) },\n async getClient(clientId) { return load('client', clientId) },\n\n async saveRefreshToken(token, data) { await save('refresh-token', token, data, data.expiresAt) },\n async getRefreshToken(token) { return load('refresh-token', token) },\n async deleteRefreshToken(token) { await remove('refresh-token', token) }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silkweave/auth",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "description": "Silkweave Auth - Bearer token and OAuth 2.1 support for MCP servers",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.silkweave.dev",
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "dependencies": {
34
34
  "jose": "^6.0.0",
35
- "@silkweave/core": "3.2.0"
35
+ "@silkweave/core": "4.0.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@eslint/js": "^10.0.1",