UNPKG

@zpg6-test-pkgs/better-auth

Version:

The most comprehensive authentication library for TypeScript.

419 lines (410 loc) 14.8 kB
'use strict'; const schema$1 = require('../../shared/better-auth.DI0OyFsZ.cjs'); const jose = require('jose'); const index = require('../../shared/better-auth.ANpbi45u.cjs'); const crypto_index = require('../../crypto/index.cjs'); const betterCall = require('better-call'); require('../../shared/better-auth.afydZyFs.cjs'); const session = require('../../shared/better-auth.DmBU2Klq.cjs'); require('zod/v4'); require('../../shared/better-auth.B6fIklBU.cjs'); require('@better-auth/utils/base64'); require('@better-auth/utils/hmac'); require('../../shared/better-auth.B3274wGK.cjs'); require('@better-auth/utils/binary'); const schema = require('../../shared/better-auth.BIMq4RPW.cjs'); const z = require('zod'); require('@better-auth/utils'); const cookies_index = require('../../shared/better-auth.l2-e84v_.cjs'); require('@better-auth/utils/hash'); require('@noble/ciphers/chacha'); require('@noble/ciphers/utils'); require('@noble/ciphers/webcrypto'); require('@noble/hashes/scrypt'); require('@better-auth/utils/hex'); require('@noble/hashes/utils'); require('../../shared/better-auth.CYeOI8C-.cjs'); require('@better-auth/utils/random'); require('../../shared/better-auth.C1hdVENX.cjs'); require('@better-fetch/fetch'); require('../../shared/better-auth.DRmln2Nr.cjs'); require('jose/errors'); require('../../shared/better-auth.vPQBmXQL.cjs'); require('../../shared/better-auth.Bg6iw3ig.cjs'); require('defu'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; } const z__default = /*#__PURE__*/_interopDefaultCompat(z); const getJwksAdapter = (adapter) => { return { getAllKeys: async () => { return await adapter.findMany({ model: "jwks" }); }, getLatestKey: async () => { const key = await adapter.findMany({ model: "jwks", sortBy: { field: "createdAt", direction: "desc" }, limit: 1 }); return key[0]; }, createJwk: async (webKey) => { const jwk = await adapter.create({ model: "jwks", data: { ...webKey, createdAt: /* @__PURE__ */ new Date() } }); return jwk; } }; }; function toExpJWT(expirationTime, iat) { if (typeof expirationTime === "number") { return expirationTime; } else if (expirationTime instanceof Date) { return Math.floor(expirationTime.getTime() / 1e3); } else { return iat + cookies_index.joseSecs(expirationTime); } } async function signJWT(ctx, config) { const { options, payload } = config; const nowSeconds = Math.floor(Date.now() / 1e3); const iat = payload.iat; let exp = payload.exp; const defaultExp = toExpJWT( options?.jwt?.expirationTime ?? "15m", iat ?? nowSeconds ); exp = exp ?? defaultExp; const nbf = payload.nbf; const iss = payload.iss; const defaultIss = options?.jwt?.issuer ?? ctx.context.options.baseURL; const aud = payload.aud; const defaultAud = options?.jwt?.audience ?? ctx.context.options.baseURL; if (options?.jwt?.sign) { const jwtPayload = { ...payload, iat, exp, nbf, iss: iss ?? defaultIss, aud: aud ?? defaultAud }; return options.jwt.sign(jwtPayload); } const adapter = getJwksAdapter(ctx.context.adapter); let key = await adapter.getLatestKey(); const privateKeyEncryptionEnabled = !options?.jwks?.disablePrivateKeyEncryption; if (key === void 0) { const alg2 = options?.jwks?.keyPairConfig?.alg || "EdDSA"; const { publicWebKey, privateWebKey: privateWebKey2 } = await generateExportedKeyPair(options); const stringifiedPrivateWebKey = JSON.stringify(privateWebKey2); let jwk = { publicKey: JSON.stringify({ alg: alg2, ...publicWebKey }), privateKey: privateKeyEncryptionEnabled ? JSON.stringify( await crypto_index.symmetricEncrypt({ key: ctx.context.secret, data: stringifiedPrivateWebKey }) ) : stringifiedPrivateWebKey, createdAt: /* @__PURE__ */ new Date() }; key = await adapter.createJwk(jwk); } let privateWebKey = privateKeyEncryptionEnabled ? await crypto_index.symmetricDecrypt({ key: ctx.context.secret, data: JSON.parse(key.privateKey) }).catch(() => { throw new index.BetterAuthError( "Failed to decrypt private private key. Make sure the secret currently in use is the same as the one used to encrypt the private key. If you are using a different secret, either cleanup your jwks or disable private key encryption." ); }) : key.privateKey; const alg = options?.jwks?.keyPairConfig?.alg ?? "EdDSA"; const privateKey = await jose.importJWK(JSON.parse(privateWebKey), alg); const jwt = new jose.SignJWT(payload).setProtectedHeader({ alg, kid: key.id }).setExpirationTime(exp).setIssuer(iss ?? defaultIss).setAudience(aud ?? defaultAud); if (iat) jwt.setIssuedAt(iat); if (payload.sub) jwt.setSubject(payload.sub); if (payload.nbf) jwt.setNotBefore(payload.nbf); if (payload.jti) jwt.setJti(payload.jti); return await jwt.sign(privateKey); } async function getJwtToken(ctx, options) { const payload = !options?.jwt?.definePayload ? ctx.context.session.user : await options?.jwt.definePayload(ctx.context.session); return await signJWT(ctx, { options, payload: { ...payload, sub: await options?.jwt?.getSubject?.(ctx.context.session) ?? ctx.context.session.user.id } }); } async function generateExportedKeyPair(options) { const { alg, ...cfg } = options?.jwks?.keyPairConfig ?? { alg: "EdDSA", crv: "Ed25519" }; const keyPairConfig = { ...cfg, extractable: true }; const { publicKey, privateKey } = await jose.generateKeyPair(alg, keyPairConfig); const publicWebKey = await jose.exportJWK(publicKey); const privateWebKey = await jose.exportJWK(privateKey); return { publicWebKey, privateWebKey }; } const jwt = (options) => { if (options?.jwt?.sign && !options.jwks?.remoteUrl) { throw new index.BetterAuthError( "jwks_config", "jwks.remoteUrl must be set when using jwt.sign" ); } if (options?.jwks?.remoteUrl && !options.jwks?.keyPairConfig?.alg) { throw new index.BetterAuthError( "jwks_config", "must specify alg when using the oidc plugin and jwks.remoteUrl" ); } return { id: "jwt", options, endpoints: { getJwks: session.createAuthEndpoint( "/jwks", { method: "GET", metadata: { openapi: { description: "Get the JSON Web Key Set", responses: { "200": { description: "JSON Web Key Set retrieved successfully", content: { "application/json": { schema: { type: "object", properties: { keys: { type: "array", description: "Array of public JSON Web Keys", items: { type: "object", properties: { kid: { type: "string", description: "Key ID uniquely identifying the key, corresponds to the 'id' from the stored Jwk" }, kty: { type: "string", description: "Key type (e.g., 'RSA', 'EC', 'OKP')" }, alg: { type: "string", description: "Algorithm intended for use with the key (e.g., 'EdDSA', 'RS256')" }, use: { type: "string", description: "Intended use of the public key (e.g., 'sig' for signature)", enum: ["sig"], nullable: true }, n: { type: "string", description: "Modulus for RSA keys (base64url-encoded)", nullable: true }, e: { type: "string", description: "Exponent for RSA keys (base64url-encoded)", nullable: true }, crv: { type: "string", description: "Curve name for elliptic curve keys (e.g., 'Ed25519', 'P-256')", nullable: true }, x: { type: "string", description: "X coordinate for elliptic curve keys (base64url-encoded)", nullable: true }, y: { type: "string", description: "Y coordinate for elliptic curve keys (base64url-encoded)", nullable: true } }, required: ["kid", "kty", "alg"] } } }, required: ["keys"] } } } } } } } }, async (ctx) => { if (options?.jwks?.remoteUrl) { throw new betterCall.APIError("NOT_FOUND"); } const adapter = getJwksAdapter(ctx.context.adapter); const keySets = await adapter.getAllKeys(); if (keySets.length === 0) { const { alg, ...cfg } = options?.jwks?.keyPairConfig ?? { alg: "EdDSA", crv: "Ed25519" }; const keyPairConfig = { ...cfg, extractable: true }; const { publicKey, privateKey } = await jose.generateKeyPair( alg, keyPairConfig ); const publicWebKey = await jose.exportJWK(publicKey); const privateWebKey = await jose.exportJWK(privateKey); const stringifiedPrivateWebKey = JSON.stringify(privateWebKey); const privateKeyEncryptionEnabled = !options?.jwks?.disablePrivateKeyEncryption; let jwk = { publicKey: JSON.stringify({ alg, ...publicWebKey }), privateKey: privateKeyEncryptionEnabled ? JSON.stringify( await crypto_index.symmetricEncrypt({ key: ctx.context.secret, data: stringifiedPrivateWebKey }) ) : stringifiedPrivateWebKey, createdAt: /* @__PURE__ */ new Date() }; await adapter.createJwk(jwk); return ctx.json({ keys: [ { ...publicWebKey, alg, kid: jwk.id } ] }); } return ctx.json({ keys: keySets.map((keySet) => ({ ...JSON.parse(keySet.publicKey), kid: keySet.id })) }); } ), getToken: session.createAuthEndpoint( "/token", { method: "GET", requireHeaders: true, use: [session.sessionMiddleware], metadata: { openapi: { description: "Get a JWT token", responses: { 200: { description: "Success", content: { "application/json": { schema: { type: "object", properties: { token: { type: "string" } } } } } } } } } }, async (ctx) => { const jwt2 = await getJwtToken(ctx, options); return ctx.json({ token: jwt2 }); } ), signJWT: session.createAuthEndpoint( "/sign-jwt", { method: "POST", metadata: { SERVER_ONLY: true, $Infer: { body: {} } }, body: z__default.object({ payload: z__default.record(z__default.string(), z__default.any()), overrideOptions: z__default.record(z__default.string(), z__default.any()).optional() }) }, async (c) => { const jwt2 = await signJWT(c, { options: { ...options, ...c.body.overrideOptions }, payload: c.body.payload }); return c.json({ token: jwt2 }); } ) }, hooks: { after: [ { matcher(context) { return context.path === "/get-session"; }, handler: session.createAuthMiddleware(async (ctx) => { if (options?.disableSettingJwtHeader) { return; } const session = ctx.context.session || ctx.context.newSession; if (session && session.session) { const jwt2 = await getJwtToken(ctx, options); const exposedHeaders = ctx.context.responseHeaders?.get( "access-control-expose-headers" ) || ""; const headersSet = new Set( exposedHeaders.split(",").map((header) => header.trim()).filter(Boolean) ); headersSet.add("set-auth-jwt"); ctx.setHeader("set-auth-jwt", jwt2); ctx.setHeader( "Access-Control-Expose-Headers", Array.from(headersSet).join(", ") ); } }) } ] }, schema: schema.mergeSchema(schema$1.schema, options?.schema) }; }; exports.generateExportedKeyPair = generateExportedKeyPair; exports.getJwtToken = getJwtToken; exports.jwt = jwt;