better-auth
Version:
The most comprehensive authentication library for TypeScript.
323 lines (317 loc) • 12 kB
JavaScript
'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');
require('better-call');
const socialProviders_index = require('../../shared/better-auth.Bafolo-S.cjs');
require('zod/v4');
require('../../shared/better-auth.DiSjtgs9.cjs');
require('@better-auth/utils/base64');
require('@better-auth/utils/hmac');
require('@better-auth/utils/binary');
const schema = require('../../shared/better-auth.BIMq4RPW.cjs');
require('../../shared/better-auth.CXhVNgXP.cjs');
require('defu');
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');
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.C-R0J0n1.cjs');
require('../../cookies/index.cjs');
require('../../shared/better-auth.D3mtHEZg.cjs');
require('jose/errors');
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;
}
};
};
async function getJwtToken(ctx, options) {
const adapter = getJwksAdapter(ctx.context.adapter);
let key = await adapter.getLatestKey();
const privateKeyEncryptionEnabled = !options?.jwks?.disablePrivateKeyEncryption;
if (key === void 0) {
const alg = options?.jwks?.keyPairConfig?.alg || "EdDSA";
const { publicWebKey, privateWebKey: privateWebKey2 } = await generateExportedKeyPair(options);
const stringifiedPrivateWebKey = JSON.stringify(privateWebKey2);
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()
};
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 privateKey = await jose.importJWK(
JSON.parse(privateWebKey),
options?.jwks?.keyPairConfig?.alg ?? "EdDSA"
);
const payload = !options?.jwt?.definePayload ? ctx.context.session.user : await options?.jwt.definePayload(ctx.context.session);
const jwt = await new jose.SignJWT(payload).setProtectedHeader({
alg: options?.jwks?.keyPairConfig?.alg ?? "EdDSA",
kid: key.id
}).setIssuedAt().setIssuer(options?.jwt?.issuer ?? ctx.context.options.baseURL).setAudience(options?.jwt?.audience ?? ctx.context.options.baseURL).setExpirationTime(options?.jwt?.expirationTime ?? "15m").setSubject(
await options?.jwt?.getSubject?.(ctx.context.session) ?? ctx.context.session.user.id
).sign(privateKey);
return jwt;
}
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) => {
return {
id: "jwt",
endpoints: {
getJwks: socialProviders_index.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) => {
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: socialProviders_index.createAuthEndpoint(
"/token",
{
method: "GET",
requireHeaders: true,
use: [socialProviders_index.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
});
}
)
},
hooks: {
after: [
{
matcher(context) {
return context.path === "/get-session";
},
handler: socialProviders_index.createAuthMiddleware(async (ctx) => {
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;