better-auth
Version:
The most comprehensive authentication framework for TypeScript.
1 lines • 3.21 kB
Source Map (JSON)
{"version":3,"file":"verify.mjs","names":[],"sources":["../../../src/plugins/jwt/verify.ts"],"sourcesContent":["import type { GenericEndpointContext } from \"@better-auth/core\";\nimport { getCurrentAuthContext } from \"@better-auth/core/context\";\nimport { base64 } from \"@better-auth/utils/base64\";\nimport type { JWTPayload } from \"jose\";\nimport { importJWK, jwtVerify } from \"jose\";\nimport { getJwksAdapter } from \"./adapter\";\nimport type { JwtOptions } from \"./types\";\n\n/**\n * Verify a JWT token using the JWKS public keys\n * Returns the payload if valid, null otherwise\n */\nexport async function verifyJWT<T extends JWTPayload = JWTPayload>(\n\ttoken: string,\n\toptions?: JwtOptions,\n): Promise<(T & Required<Pick<JWTPayload, \"sub\" | \"aud\">>) | null> {\n\tconst ctx = await getCurrentAuthContext();\n\ttry {\n\t\tconst parts = token.split(\".\");\n\t\tif (parts.length !== 3) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst headerStr = new TextDecoder().decode(base64.decode(parts[0]!));\n\t\tconst header = JSON.parse(headerStr);\n\t\tconst kid = header.kid;\n\n\t\tif (!kid) {\n\t\t\tctx.context.logger.debug(\"JWT missing kid in header\");\n\t\t\treturn null;\n\t\t}\n\n\t\t// Get all JWKS keys\n\t\tconst adapter = getJwksAdapter(ctx.context.adapter, options);\n\t\tconst keys = await adapter.getAllKeys(ctx as GenericEndpointContext);\n\n\t\tif (!keys || keys.length === 0) {\n\t\t\tctx.context.logger.debug(\"No JWKS keys available\");\n\t\t\treturn null;\n\t\t}\n\n\t\tconst key = keys.find((k) => k.id === kid);\n\t\tif (!key) {\n\t\t\tctx.context.logger.debug(`No JWKS key found for kid: ${kid}`);\n\t\t\treturn null;\n\t\t}\n\n\t\tconst publicKey = JSON.parse(key.publicKey);\n\t\tconst alg = key.alg ?? options?.jwks?.keyPairConfig?.alg ?? \"EdDSA\";\n\t\tconst cryptoKey = await importJWK(publicKey, alg);\n\n\t\tconst { payload } = await jwtVerify(token, cryptoKey, {\n\t\t\tissuer: options?.jwt?.issuer ?? ctx.context.options.baseURL,\n\t\t\taudience: options?.jwt?.audience ?? ctx.context.options.baseURL,\n\t\t});\n\n\t\tif (!payload.sub || !payload.aud) {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn payload as T & Required<Pick<JWTPayload, \"sub\" | \"aud\">>;\n\t} catch (error) {\n\t\tctx.context.logger.debug(\"JWT verification failed\", error);\n\t\treturn null;\n\t}\n}\n"],"mappings":";;;;;;;;;;AAYA,eAAsB,UACrB,OACA,SACkE;CAClE,MAAM,MAAM,MAAM,uBAAuB;AACzC,KAAI;EACH,MAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,MAAI,MAAM,WAAW,EACpB,QAAO;EAGR,MAAM,YAAY,IAAI,aAAa,CAAC,OAAO,OAAO,OAAO,MAAM,GAAI,CAAC;EAEpE,MAAM,MADS,KAAK,MAAM,UAAU,CACjB;AAEnB,MAAI,CAAC,KAAK;AACT,OAAI,QAAQ,OAAO,MAAM,4BAA4B;AACrD,UAAO;;EAKR,MAAM,OAAO,MADG,eAAe,IAAI,QAAQ,SAAS,QAAQ,CACjC,WAAW,IAA8B;AAEpE,MAAI,CAAC,QAAQ,KAAK,WAAW,GAAG;AAC/B,OAAI,QAAQ,OAAO,MAAM,yBAAyB;AAClD,UAAO;;EAGR,MAAM,MAAM,KAAK,MAAM,MAAM,EAAE,OAAO,IAAI;AAC1C,MAAI,CAAC,KAAK;AACT,OAAI,QAAQ,OAAO,MAAM,8BAA8B,MAAM;AAC7D,UAAO;;EAOR,MAAM,EAAE,YAAY,MAAM,UAAU,OAFlB,MAAM,UAFN,KAAK,MAAM,IAAI,UAAU,EAC/B,IAAI,OAAO,SAAS,MAAM,eAAe,OAAO,QACX,EAEK;GACrD,QAAQ,SAAS,KAAK,UAAU,IAAI,QAAQ,QAAQ;GACpD,UAAU,SAAS,KAAK,YAAY,IAAI,QAAQ,QAAQ;GACxD,CAAC;AAEF,MAAI,CAAC,QAAQ,OAAO,CAAC,QAAQ,IAC5B,QAAO;AAGR,SAAO;UACC,OAAO;AACf,MAAI,QAAQ,OAAO,MAAM,2BAA2B,MAAM;AAC1D,SAAO"}