@talismn/gandalf 0.0.0 → 0.0.2
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 +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,7 +111,7 @@ app.get("/v1/prices", (c) => {
|
|
|
111
111
|
| Variable | Default | Description |
|
|
112
112
|
|----------|---------|-------------|
|
|
113
113
|
| `jwksUrl` | `https://gandalf.talisman.xyz/.well-known/jwks.json` | JWKS endpoint URL |
|
|
114
|
-
| `issuer` | `gandalf.talisman.xyz` | Expected JWT issuer claim |
|
|
114
|
+
| `issuer` | `https://gandalf.talisman.xyz` | Expected JWT issuer claim |
|
|
115
115
|
| `clockTolerance` | `30` | Seconds of clock skew tolerance for exp/nbf checks |
|
|
116
116
|
|
|
117
117
|
## JWKS Caching
|
package/dist/index.cjs
CHANGED
|
@@ -45,7 +45,7 @@ function getJWKS(jwksUrl = DEFAULT_JWKS_URL) {
|
|
|
45
45
|
|
|
46
46
|
// src/verify.ts
|
|
47
47
|
var import_jose2 = require("jose");
|
|
48
|
-
var DEFAULT_ISSUER = "gandalf.talisman.xyz";
|
|
48
|
+
var DEFAULT_ISSUER = "https://gandalf.talisman.xyz";
|
|
49
49
|
function extractBearerToken(authHeader) {
|
|
50
50
|
if (!authHeader) {
|
|
51
51
|
throw new GandalfAuthError("Missing Authorization header", 401);
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/jwks.ts","../src/verify.ts"],"sourcesContent":["// Core\n\n// JWKS\nexport { getJWKS } from \"./jwks\";\n// Types\nexport type { AuthContext, VerifyOpts } from \"./types\";\nexport {\n\textractBearerToken,\n\tGandalfAuthError,\n\tverifyAccessToken,\n} from \"./verify\";\n","import { createRemoteJWKSet } from \"jose\";\n\nconst DEFAULT_JWKS_URL = \"https://gandalf.talisman.xyz/.well-known/jwks.json\";\n\n/** In-memory JWKS cache keyed by URL */\nconst jwksCache = new Map<\n\tstring,\n\t{ jwks: ReturnType<typeof createRemoteJWKSet>; createdAt: number }\n>();\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\n/**\n * Get a cached JWKS function for the given URL.\n *\n * The returned function is compatible with `jose.jwtVerify()`.\n * On unknown `kid`, jose's `createRemoteJWKSet` automatically refetches.\n */\nexport function getJWKS(\n\tjwksUrl: string = DEFAULT_JWKS_URL,\n): ReturnType<typeof createRemoteJWKSet> {\n\tconst now = Date.now();\n\tconst cached = jwksCache.get(jwksUrl);\n\n\tif (cached && now - cached.createdAt < CACHE_TTL_MS) {\n\t\treturn cached.jwks;\n\t}\n\n\tconst jwks = createRemoteJWKSet(new URL(jwksUrl));\n\tjwksCache.set(jwksUrl, { jwks, createdAt: now });\n\n\treturn jwks;\n}\n","import { jwtVerify } from \"jose\";\nimport { getJWKS } from \"./jwks\";\nimport type { AuthContext, VerifyOpts } from \"./types\";\n\nconst DEFAULT_ISSUER = \"gandalf.talisman.xyz\";\n\n/**\n * Extract a Bearer token from an `Authorization` header value.\n *\n * @param authHeader - The full `Authorization` header string (e.g. `\"Bearer eyJ...\"`).\n * @returns The raw JWT string.\n * @throws GandalfAuthError if the header is missing or malformed.\n */\nexport function extractBearerToken(\n\tauthHeader: string | null | undefined,\n): string {\n\tif (!authHeader) {\n\t\tthrow new GandalfAuthError(\"Missing Authorization header\", 401);\n\t}\n\n\tconst parts = authHeader.split(\" \");\n\tif (parts.length !== 2 || parts[0] !== \"Bearer\" || !parts[1]) {\n\t\tthrow new GandalfAuthError(\n\t\t\t\"Invalid Authorization header format. Expected: Bearer <token>\",\n\t\t\t401,\n\t\t);\n\t}\n\n\treturn parts[1];\n}\n\n/**\n * Verify a Gandalf access token.\n *\n * Verifies the JWT signature and claims, and returns an `AuthContext` on success.\n *\n * @param token - The raw JWT string (not the full Authorization header).\n * @param opts - Optional verification overrides.\n * @throws GandalfAuthError on any verification failure.\n */\nexport async function verifyAccessToken(\n\ttoken: string,\n\topts: VerifyOpts = {},\n): Promise<AuthContext> {\n\t// Get JWKS\n\tconst jwks = getJWKS(opts.jwksUrl);\n\tconst issuer = opts.issuer ?? DEFAULT_ISSUER;\n\n\t// Verify JWT\n\ttry {\n\t\tconst { payload } = await jwtVerify(token, jwks, {\n\t\t\tissuer,\n\t\t\tclockTolerance: opts.clockTolerance ?? 30,\n\t\t});\n\n\t\t// Extract installId from `sub` claim (format: \"install:<id>\")\n\t\tconst sub = payload.sub;\n\t\tif (!sub?.startsWith(\"install:\")) {\n\t\t\tthrow new GandalfAuthError(\"Invalid sub claim format\", 401);\n\t\t}\n\t\tconst installId = sub.slice(\"install:\".length);\n\n\t\treturn { installId, claims: payload };\n\t} catch (err) {\n\t\tif (err instanceof GandalfAuthError) throw err;\n\n\t\tconst message =\n\t\t\terr instanceof Error ? err.message : \"Token verification failed\";\n\t\tthrow new GandalfAuthError(message, 401);\n\t}\n}\n\n/** Error thrown during Gandalf auth verification */\nexport class GandalfAuthError extends Error {\n\tpublic readonly status: number;\n\n\tconstructor(message: string, status: number) {\n\t\tsuper(message);\n\t\tthis.name = \"GandalfAuthError\";\n\t\tthis.status = status;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAAmC;AAEnC,IAAM,mBAAmB;AAGzB,IAAM,YAAY,oBAAI,IAGpB;AAEF,IAAM,eAAe,IAAI,KAAK;AAQvB,SAAS,QACf,UAAkB,kBACsB;AACxC,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,SAAS,UAAU,IAAI,OAAO;AAEpC,MAAI,UAAU,MAAM,OAAO,YAAY,cAAc;AACpD,WAAO,OAAO;AAAA,EACf;AAEA,QAAM,WAAO,gCAAmB,IAAI,IAAI,OAAO,CAAC;AAChD,YAAU,IAAI,SAAS,EAAE,MAAM,WAAW,IAAI,CAAC;AAE/C,SAAO;AACR;;;AChCA,IAAAA,eAA0B;AAI1B,IAAM,iBAAiB;AAShB,SAAS,mBACf,YACS;AACT,MAAI,CAAC,YAAY;AAChB,UAAM,IAAI,iBAAiB,gCAAgC,GAAG;AAAA,EAC/D;AAEA,QAAM,QAAQ,WAAW,MAAM,GAAG;AAClC,MAAI,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM,YAAY,CAAC,MAAM,CAAC,GAAG;AAC7D,UAAM,IAAI;AAAA,MACT;AAAA,MACA;AAAA,IACD;AAAA,EACD;AAEA,SAAO,MAAM,CAAC;AACf;AAWA,eAAsB,kBACrB,OACA,OAAmB,CAAC,GACG;AAEvB,QAAM,OAAO,QAAQ,KAAK,OAAO;AACjC,QAAM,SAAS,KAAK,UAAU;AAG9B,MAAI;AACH,UAAM,EAAE,QAAQ,IAAI,UAAM,wBAAU,OAAO,MAAM;AAAA,MAChD;AAAA,MACA,gBAAgB,KAAK,kBAAkB;AAAA,IACxC,CAAC;AAGD,UAAM,MAAM,QAAQ;AACpB,QAAI,CAAC,KAAK,WAAW,UAAU,GAAG;AACjC,YAAM,IAAI,iBAAiB,4BAA4B,GAAG;AAAA,IAC3D;AACA,UAAM,YAAY,IAAI,MAAM,WAAW,MAAM;AAE7C,WAAO,EAAE,WAAW,QAAQ,QAAQ;AAAA,EACrC,SAAS,KAAK;AACb,QAAI,eAAe,iBAAkB,OAAM;AAE3C,UAAM,UACL,eAAe,QAAQ,IAAI,UAAU;AACtC,UAAM,IAAI,iBAAiB,SAAS,GAAG;AAAA,EACxC;AACD;AAGO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC3B;AAAA,EAEhB,YAAY,SAAiB,QAAgB;AAC5C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EACf;AACD;","names":["import_jose"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/jwks.ts","../src/verify.ts"],"sourcesContent":["// Core\n\n// JWKS\nexport { getJWKS } from \"./jwks\";\n// Types\nexport type { AuthContext, VerifyOpts } from \"./types\";\nexport {\n\textractBearerToken,\n\tGandalfAuthError,\n\tverifyAccessToken,\n} from \"./verify\";\n","import { createRemoteJWKSet } from \"jose\";\n\nconst DEFAULT_JWKS_URL = \"https://gandalf.talisman.xyz/.well-known/jwks.json\";\n\n/** In-memory JWKS cache keyed by URL */\nconst jwksCache = new Map<\n\tstring,\n\t{ jwks: ReturnType<typeof createRemoteJWKSet>; createdAt: number }\n>();\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\n/**\n * Get a cached JWKS function for the given URL.\n *\n * The returned function is compatible with `jose.jwtVerify()`.\n * On unknown `kid`, jose's `createRemoteJWKSet` automatically refetches.\n */\nexport function getJWKS(\n\tjwksUrl: string = DEFAULT_JWKS_URL,\n): ReturnType<typeof createRemoteJWKSet> {\n\tconst now = Date.now();\n\tconst cached = jwksCache.get(jwksUrl);\n\n\tif (cached && now - cached.createdAt < CACHE_TTL_MS) {\n\t\treturn cached.jwks;\n\t}\n\n\tconst jwks = createRemoteJWKSet(new URL(jwksUrl));\n\tjwksCache.set(jwksUrl, { jwks, createdAt: now });\n\n\treturn jwks;\n}\n","import { jwtVerify } from \"jose\";\nimport { getJWKS } from \"./jwks\";\nimport type { AuthContext, VerifyOpts } from \"./types\";\n\nconst DEFAULT_ISSUER = \"https://gandalf.talisman.xyz\";\n\n/**\n * Extract a Bearer token from an `Authorization` header value.\n *\n * @param authHeader - The full `Authorization` header string (e.g. `\"Bearer eyJ...\"`).\n * @returns The raw JWT string.\n * @throws GandalfAuthError if the header is missing or malformed.\n */\nexport function extractBearerToken(\n\tauthHeader: string | null | undefined,\n): string {\n\tif (!authHeader) {\n\t\tthrow new GandalfAuthError(\"Missing Authorization header\", 401);\n\t}\n\n\tconst parts = authHeader.split(\" \");\n\tif (parts.length !== 2 || parts[0] !== \"Bearer\" || !parts[1]) {\n\t\tthrow new GandalfAuthError(\n\t\t\t\"Invalid Authorization header format. Expected: Bearer <token>\",\n\t\t\t401,\n\t\t);\n\t}\n\n\treturn parts[1];\n}\n\n/**\n * Verify a Gandalf access token.\n *\n * Verifies the JWT signature and claims, and returns an `AuthContext` on success.\n *\n * @param token - The raw JWT string (not the full Authorization header).\n * @param opts - Optional verification overrides.\n * @throws GandalfAuthError on any verification failure.\n */\nexport async function verifyAccessToken(\n\ttoken: string,\n\topts: VerifyOpts = {},\n): Promise<AuthContext> {\n\t// Get JWKS\n\tconst jwks = getJWKS(opts.jwksUrl);\n\tconst issuer = opts.issuer ?? DEFAULT_ISSUER;\n\n\t// Verify JWT\n\ttry {\n\t\tconst { payload } = await jwtVerify(token, jwks, {\n\t\t\tissuer,\n\t\t\tclockTolerance: opts.clockTolerance ?? 30,\n\t\t});\n\n\t\t// Extract installId from `sub` claim (format: \"install:<id>\")\n\t\tconst sub = payload.sub;\n\t\tif (!sub?.startsWith(\"install:\")) {\n\t\t\tthrow new GandalfAuthError(\"Invalid sub claim format\", 401);\n\t\t}\n\t\tconst installId = sub.slice(\"install:\".length);\n\n\t\treturn { installId, claims: payload };\n\t} catch (err) {\n\t\tif (err instanceof GandalfAuthError) throw err;\n\n\t\tconst message =\n\t\t\terr instanceof Error ? err.message : \"Token verification failed\";\n\t\tthrow new GandalfAuthError(message, 401);\n\t}\n}\n\n/** Error thrown during Gandalf auth verification */\nexport class GandalfAuthError extends Error {\n\tpublic readonly status: number;\n\n\tconstructor(message: string, status: number) {\n\t\tsuper(message);\n\t\tthis.name = \"GandalfAuthError\";\n\t\tthis.status = status;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAAmC;AAEnC,IAAM,mBAAmB;AAGzB,IAAM,YAAY,oBAAI,IAGpB;AAEF,IAAM,eAAe,IAAI,KAAK;AAQvB,SAAS,QACf,UAAkB,kBACsB;AACxC,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,SAAS,UAAU,IAAI,OAAO;AAEpC,MAAI,UAAU,MAAM,OAAO,YAAY,cAAc;AACpD,WAAO,OAAO;AAAA,EACf;AAEA,QAAM,WAAO,gCAAmB,IAAI,IAAI,OAAO,CAAC;AAChD,YAAU,IAAI,SAAS,EAAE,MAAM,WAAW,IAAI,CAAC;AAE/C,SAAO;AACR;;;AChCA,IAAAA,eAA0B;AAI1B,IAAM,iBAAiB;AAShB,SAAS,mBACf,YACS;AACT,MAAI,CAAC,YAAY;AAChB,UAAM,IAAI,iBAAiB,gCAAgC,GAAG;AAAA,EAC/D;AAEA,QAAM,QAAQ,WAAW,MAAM,GAAG;AAClC,MAAI,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM,YAAY,CAAC,MAAM,CAAC,GAAG;AAC7D,UAAM,IAAI;AAAA,MACT;AAAA,MACA;AAAA,IACD;AAAA,EACD;AAEA,SAAO,MAAM,CAAC;AACf;AAWA,eAAsB,kBACrB,OACA,OAAmB,CAAC,GACG;AAEvB,QAAM,OAAO,QAAQ,KAAK,OAAO;AACjC,QAAM,SAAS,KAAK,UAAU;AAG9B,MAAI;AACH,UAAM,EAAE,QAAQ,IAAI,UAAM,wBAAU,OAAO,MAAM;AAAA,MAChD;AAAA,MACA,gBAAgB,KAAK,kBAAkB;AAAA,IACxC,CAAC;AAGD,UAAM,MAAM,QAAQ;AACpB,QAAI,CAAC,KAAK,WAAW,UAAU,GAAG;AACjC,YAAM,IAAI,iBAAiB,4BAA4B,GAAG;AAAA,IAC3D;AACA,UAAM,YAAY,IAAI,MAAM,WAAW,MAAM;AAE7C,WAAO,EAAE,WAAW,QAAQ,QAAQ;AAAA,EACrC,SAAS,KAAK;AACb,QAAI,eAAe,iBAAkB,OAAM;AAE3C,UAAM,UACL,eAAe,QAAQ,IAAI,UAAU;AACtC,UAAM,IAAI,iBAAiB,SAAS,GAAG;AAAA,EACxC;AACD;AAGO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC3B;AAAA,EAEhB,YAAY,SAAiB,QAAgB;AAC5C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EACf;AACD;","names":["import_jose"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -19,7 +19,7 @@ interface AuthContext {
|
|
|
19
19
|
interface VerifyOpts {
|
|
20
20
|
/** URL to fetch the JWKS from (default: https://gandalf.talisman.xyz/.well-known/jwks.json) */
|
|
21
21
|
jwksUrl?: string;
|
|
22
|
-
/** Expected `iss` claim (default: gandalf.talisman.xyz) */
|
|
22
|
+
/** Expected `iss` claim (default: https://gandalf.talisman.xyz) */
|
|
23
23
|
issuer?: string;
|
|
24
24
|
/** Clock tolerance in seconds for `exp`/`nbf` checks (default: 30) */
|
|
25
25
|
clockTolerance?: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ interface AuthContext {
|
|
|
19
19
|
interface VerifyOpts {
|
|
20
20
|
/** URL to fetch the JWKS from (default: https://gandalf.talisman.xyz/.well-known/jwks.json) */
|
|
21
21
|
jwksUrl?: string;
|
|
22
|
-
/** Expected `iss` claim (default: gandalf.talisman.xyz) */
|
|
22
|
+
/** Expected `iss` claim (default: https://gandalf.talisman.xyz) */
|
|
23
23
|
issuer?: string;
|
|
24
24
|
/** Clock tolerance in seconds for `exp`/`nbf` checks (default: 30) */
|
|
25
25
|
clockTolerance?: number;
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ function getJWKS(jwksUrl = DEFAULT_JWKS_URL) {
|
|
|
16
16
|
|
|
17
17
|
// src/verify.ts
|
|
18
18
|
import { jwtVerify } from "jose";
|
|
19
|
-
var DEFAULT_ISSUER = "gandalf.talisman.xyz";
|
|
19
|
+
var DEFAULT_ISSUER = "https://gandalf.talisman.xyz";
|
|
20
20
|
function extractBearerToken(authHeader) {
|
|
21
21
|
if (!authHeader) {
|
|
22
22
|
throw new GandalfAuthError("Missing Authorization header", 401);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/jwks.ts","../src/verify.ts"],"sourcesContent":["import { createRemoteJWKSet } from \"jose\";\n\nconst DEFAULT_JWKS_URL = \"https://gandalf.talisman.xyz/.well-known/jwks.json\";\n\n/** In-memory JWKS cache keyed by URL */\nconst jwksCache = new Map<\n\tstring,\n\t{ jwks: ReturnType<typeof createRemoteJWKSet>; createdAt: number }\n>();\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\n/**\n * Get a cached JWKS function for the given URL.\n *\n * The returned function is compatible with `jose.jwtVerify()`.\n * On unknown `kid`, jose's `createRemoteJWKSet` automatically refetches.\n */\nexport function getJWKS(\n\tjwksUrl: string = DEFAULT_JWKS_URL,\n): ReturnType<typeof createRemoteJWKSet> {\n\tconst now = Date.now();\n\tconst cached = jwksCache.get(jwksUrl);\n\n\tif (cached && now - cached.createdAt < CACHE_TTL_MS) {\n\t\treturn cached.jwks;\n\t}\n\n\tconst jwks = createRemoteJWKSet(new URL(jwksUrl));\n\tjwksCache.set(jwksUrl, { jwks, createdAt: now });\n\n\treturn jwks;\n}\n","import { jwtVerify } from \"jose\";\nimport { getJWKS } from \"./jwks\";\nimport type { AuthContext, VerifyOpts } from \"./types\";\n\nconst DEFAULT_ISSUER = \"gandalf.talisman.xyz\";\n\n/**\n * Extract a Bearer token from an `Authorization` header value.\n *\n * @param authHeader - The full `Authorization` header string (e.g. `\"Bearer eyJ...\"`).\n * @returns The raw JWT string.\n * @throws GandalfAuthError if the header is missing or malformed.\n */\nexport function extractBearerToken(\n\tauthHeader: string | null | undefined,\n): string {\n\tif (!authHeader) {\n\t\tthrow new GandalfAuthError(\"Missing Authorization header\", 401);\n\t}\n\n\tconst parts = authHeader.split(\" \");\n\tif (parts.length !== 2 || parts[0] !== \"Bearer\" || !parts[1]) {\n\t\tthrow new GandalfAuthError(\n\t\t\t\"Invalid Authorization header format. Expected: Bearer <token>\",\n\t\t\t401,\n\t\t);\n\t}\n\n\treturn parts[1];\n}\n\n/**\n * Verify a Gandalf access token.\n *\n * Verifies the JWT signature and claims, and returns an `AuthContext` on success.\n *\n * @param token - The raw JWT string (not the full Authorization header).\n * @param opts - Optional verification overrides.\n * @throws GandalfAuthError on any verification failure.\n */\nexport async function verifyAccessToken(\n\ttoken: string,\n\topts: VerifyOpts = {},\n): Promise<AuthContext> {\n\t// Get JWKS\n\tconst jwks = getJWKS(opts.jwksUrl);\n\tconst issuer = opts.issuer ?? DEFAULT_ISSUER;\n\n\t// Verify JWT\n\ttry {\n\t\tconst { payload } = await jwtVerify(token, jwks, {\n\t\t\tissuer,\n\t\t\tclockTolerance: opts.clockTolerance ?? 30,\n\t\t});\n\n\t\t// Extract installId from `sub` claim (format: \"install:<id>\")\n\t\tconst sub = payload.sub;\n\t\tif (!sub?.startsWith(\"install:\")) {\n\t\t\tthrow new GandalfAuthError(\"Invalid sub claim format\", 401);\n\t\t}\n\t\tconst installId = sub.slice(\"install:\".length);\n\n\t\treturn { installId, claims: payload };\n\t} catch (err) {\n\t\tif (err instanceof GandalfAuthError) throw err;\n\n\t\tconst message =\n\t\t\terr instanceof Error ? err.message : \"Token verification failed\";\n\t\tthrow new GandalfAuthError(message, 401);\n\t}\n}\n\n/** Error thrown during Gandalf auth verification */\nexport class GandalfAuthError extends Error {\n\tpublic readonly status: number;\n\n\tconstructor(message: string, status: number) {\n\t\tsuper(message);\n\t\tthis.name = \"GandalfAuthError\";\n\t\tthis.status = status;\n\t}\n}\n"],"mappings":";AAAA,SAAS,0BAA0B;AAEnC,IAAM,mBAAmB;AAGzB,IAAM,YAAY,oBAAI,IAGpB;AAEF,IAAM,eAAe,IAAI,KAAK;AAQvB,SAAS,QACf,UAAkB,kBACsB;AACxC,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,SAAS,UAAU,IAAI,OAAO;AAEpC,MAAI,UAAU,MAAM,OAAO,YAAY,cAAc;AACpD,WAAO,OAAO;AAAA,EACf;AAEA,QAAM,OAAO,mBAAmB,IAAI,IAAI,OAAO,CAAC;AAChD,YAAU,IAAI,SAAS,EAAE,MAAM,WAAW,IAAI,CAAC;AAE/C,SAAO;AACR;;;AChCA,SAAS,iBAAiB;AAI1B,IAAM,iBAAiB;AAShB,SAAS,mBACf,YACS;AACT,MAAI,CAAC,YAAY;AAChB,UAAM,IAAI,iBAAiB,gCAAgC,GAAG;AAAA,EAC/D;AAEA,QAAM,QAAQ,WAAW,MAAM,GAAG;AAClC,MAAI,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM,YAAY,CAAC,MAAM,CAAC,GAAG;AAC7D,UAAM,IAAI;AAAA,MACT;AAAA,MACA;AAAA,IACD;AAAA,EACD;AAEA,SAAO,MAAM,CAAC;AACf;AAWA,eAAsB,kBACrB,OACA,OAAmB,CAAC,GACG;AAEvB,QAAM,OAAO,QAAQ,KAAK,OAAO;AACjC,QAAM,SAAS,KAAK,UAAU;AAG9B,MAAI;AACH,UAAM,EAAE,QAAQ,IAAI,MAAM,UAAU,OAAO,MAAM;AAAA,MAChD;AAAA,MACA,gBAAgB,KAAK,kBAAkB;AAAA,IACxC,CAAC;AAGD,UAAM,MAAM,QAAQ;AACpB,QAAI,CAAC,KAAK,WAAW,UAAU,GAAG;AACjC,YAAM,IAAI,iBAAiB,4BAA4B,GAAG;AAAA,IAC3D;AACA,UAAM,YAAY,IAAI,MAAM,WAAW,MAAM;AAE7C,WAAO,EAAE,WAAW,QAAQ,QAAQ;AAAA,EACrC,SAAS,KAAK;AACb,QAAI,eAAe,iBAAkB,OAAM;AAE3C,UAAM,UACL,eAAe,QAAQ,IAAI,UAAU;AACtC,UAAM,IAAI,iBAAiB,SAAS,GAAG;AAAA,EACxC;AACD;AAGO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC3B;AAAA,EAEhB,YAAY,SAAiB,QAAgB;AAC5C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EACf;AACD;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/jwks.ts","../src/verify.ts"],"sourcesContent":["import { createRemoteJWKSet } from \"jose\";\n\nconst DEFAULT_JWKS_URL = \"https://gandalf.talisman.xyz/.well-known/jwks.json\";\n\n/** In-memory JWKS cache keyed by URL */\nconst jwksCache = new Map<\n\tstring,\n\t{ jwks: ReturnType<typeof createRemoteJWKSet>; createdAt: number }\n>();\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\n/**\n * Get a cached JWKS function for the given URL.\n *\n * The returned function is compatible with `jose.jwtVerify()`.\n * On unknown `kid`, jose's `createRemoteJWKSet` automatically refetches.\n */\nexport function getJWKS(\n\tjwksUrl: string = DEFAULT_JWKS_URL,\n): ReturnType<typeof createRemoteJWKSet> {\n\tconst now = Date.now();\n\tconst cached = jwksCache.get(jwksUrl);\n\n\tif (cached && now - cached.createdAt < CACHE_TTL_MS) {\n\t\treturn cached.jwks;\n\t}\n\n\tconst jwks = createRemoteJWKSet(new URL(jwksUrl));\n\tjwksCache.set(jwksUrl, { jwks, createdAt: now });\n\n\treturn jwks;\n}\n","import { jwtVerify } from \"jose\";\nimport { getJWKS } from \"./jwks\";\nimport type { AuthContext, VerifyOpts } from \"./types\";\n\nconst DEFAULT_ISSUER = \"https://gandalf.talisman.xyz\";\n\n/**\n * Extract a Bearer token from an `Authorization` header value.\n *\n * @param authHeader - The full `Authorization` header string (e.g. `\"Bearer eyJ...\"`).\n * @returns The raw JWT string.\n * @throws GandalfAuthError if the header is missing or malformed.\n */\nexport function extractBearerToken(\n\tauthHeader: string | null | undefined,\n): string {\n\tif (!authHeader) {\n\t\tthrow new GandalfAuthError(\"Missing Authorization header\", 401);\n\t}\n\n\tconst parts = authHeader.split(\" \");\n\tif (parts.length !== 2 || parts[0] !== \"Bearer\" || !parts[1]) {\n\t\tthrow new GandalfAuthError(\n\t\t\t\"Invalid Authorization header format. Expected: Bearer <token>\",\n\t\t\t401,\n\t\t);\n\t}\n\n\treturn parts[1];\n}\n\n/**\n * Verify a Gandalf access token.\n *\n * Verifies the JWT signature and claims, and returns an `AuthContext` on success.\n *\n * @param token - The raw JWT string (not the full Authorization header).\n * @param opts - Optional verification overrides.\n * @throws GandalfAuthError on any verification failure.\n */\nexport async function verifyAccessToken(\n\ttoken: string,\n\topts: VerifyOpts = {},\n): Promise<AuthContext> {\n\t// Get JWKS\n\tconst jwks = getJWKS(opts.jwksUrl);\n\tconst issuer = opts.issuer ?? DEFAULT_ISSUER;\n\n\t// Verify JWT\n\ttry {\n\t\tconst { payload } = await jwtVerify(token, jwks, {\n\t\t\tissuer,\n\t\t\tclockTolerance: opts.clockTolerance ?? 30,\n\t\t});\n\n\t\t// Extract installId from `sub` claim (format: \"install:<id>\")\n\t\tconst sub = payload.sub;\n\t\tif (!sub?.startsWith(\"install:\")) {\n\t\t\tthrow new GandalfAuthError(\"Invalid sub claim format\", 401);\n\t\t}\n\t\tconst installId = sub.slice(\"install:\".length);\n\n\t\treturn { installId, claims: payload };\n\t} catch (err) {\n\t\tif (err instanceof GandalfAuthError) throw err;\n\n\t\tconst message =\n\t\t\terr instanceof Error ? err.message : \"Token verification failed\";\n\t\tthrow new GandalfAuthError(message, 401);\n\t}\n}\n\n/** Error thrown during Gandalf auth verification */\nexport class GandalfAuthError extends Error {\n\tpublic readonly status: number;\n\n\tconstructor(message: string, status: number) {\n\t\tsuper(message);\n\t\tthis.name = \"GandalfAuthError\";\n\t\tthis.status = status;\n\t}\n}\n"],"mappings":";AAAA,SAAS,0BAA0B;AAEnC,IAAM,mBAAmB;AAGzB,IAAM,YAAY,oBAAI,IAGpB;AAEF,IAAM,eAAe,IAAI,KAAK;AAQvB,SAAS,QACf,UAAkB,kBACsB;AACxC,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,SAAS,UAAU,IAAI,OAAO;AAEpC,MAAI,UAAU,MAAM,OAAO,YAAY,cAAc;AACpD,WAAO,OAAO;AAAA,EACf;AAEA,QAAM,OAAO,mBAAmB,IAAI,IAAI,OAAO,CAAC;AAChD,YAAU,IAAI,SAAS,EAAE,MAAM,WAAW,IAAI,CAAC;AAE/C,SAAO;AACR;;;AChCA,SAAS,iBAAiB;AAI1B,IAAM,iBAAiB;AAShB,SAAS,mBACf,YACS;AACT,MAAI,CAAC,YAAY;AAChB,UAAM,IAAI,iBAAiB,gCAAgC,GAAG;AAAA,EAC/D;AAEA,QAAM,QAAQ,WAAW,MAAM,GAAG;AAClC,MAAI,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM,YAAY,CAAC,MAAM,CAAC,GAAG;AAC7D,UAAM,IAAI;AAAA,MACT;AAAA,MACA;AAAA,IACD;AAAA,EACD;AAEA,SAAO,MAAM,CAAC;AACf;AAWA,eAAsB,kBACrB,OACA,OAAmB,CAAC,GACG;AAEvB,QAAM,OAAO,QAAQ,KAAK,OAAO;AACjC,QAAM,SAAS,KAAK,UAAU;AAG9B,MAAI;AACH,UAAM,EAAE,QAAQ,IAAI,MAAM,UAAU,OAAO,MAAM;AAAA,MAChD;AAAA,MACA,gBAAgB,KAAK,kBAAkB;AAAA,IACxC,CAAC;AAGD,UAAM,MAAM,QAAQ;AACpB,QAAI,CAAC,KAAK,WAAW,UAAU,GAAG;AACjC,YAAM,IAAI,iBAAiB,4BAA4B,GAAG;AAAA,IAC3D;AACA,UAAM,YAAY,IAAI,MAAM,WAAW,MAAM;AAE7C,WAAO,EAAE,WAAW,QAAQ,QAAQ;AAAA,EACrC,SAAS,KAAK;AACb,QAAI,eAAe,iBAAkB,OAAM;AAE3C,UAAM,UACL,eAAe,QAAQ,IAAI,UAAU;AACtC,UAAM,IAAI,iBAAiB,SAAS,GAAG;AAAA,EACxC;AACD;AAGO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EAC3B;AAAA,EAEhB,YAAY,SAAiB,QAAgB;AAC5C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AAAA,EACf;AACD;","names":[]}
|