UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

256 lines (255 loc) 8.41 kB
import { Session, User } from "../../types/models.mjs"; import { InferOptionSchema } from "../../types/plugins.mjs"; import { schema } from "./schema.mjs"; import { Awaitable, GenericEndpointContext } from "@better-auth/core"; import { JWTPayload } from "jose"; //#region src/plugins/jwt/types.d.ts interface JwtOptions { /** * Use the JWT plugin's locally managed signing keys for JWT session cookie * cache values. * * Requires `session.cookieCache.strategy` to be set to `"jwt"`. * * @default false */ sessionCookieCache?: boolean | undefined; jwks?: { /** * Disables the /jwks endpoint and uses this endpoint in discovery. * * Useful if jwks are not managed at /jwks or * if your jwks are signed with a certificate and placed on your CDN. */ remoteUrl?: string | undefined; /** * Key pair configuration * @description A subset of the options available for the generateKeyPair function * * @see https://github.com/panva/jose/blob/main/src/runtime/node/generate.ts * * @default { alg: 'EdDSA', crv: 'Ed25519' } */ keyPairConfig?: JWKOptions | undefined; /** * Additional key pair configurations to provision so per-resource * `signingAlgorithm` overrides (see the OAuth provider plugin's * resource entity) can resolve a key for algorithms other than * {@link keyPairConfig}. * * Lazily minted: the first `signJWT` call that pins an algorithm * present here mints the row on demand. Subsequent calls reuse it. * This avoids needing to coordinate with migration ordering at * plugin init — entries here are only materialized after the * `jwks` table exists. * * Algorithms not present in either {@link keyPairConfig} or this * list will throw with a descriptive error message identifying * the configured default, so misconfiguration is diagnosable. * * @example * ```ts * jwt({ * jwks: { * keyPairConfig: { alg: "EdDSA" }, * keyPairConfigs: [{ alg: "ES256" }, { alg: "RS256" }], * }, * }) * ``` */ keyPairConfigs?: readonly JWKOptions[] | undefined; /** * Disable private key encryption * @description Disable the encryption of the private key in the database * * @default false */ disablePrivateKeyEncryption?: boolean | undefined; /** * The key rotation interval in seconds. * * @default undefined (disabled) */ rotationInterval?: number | undefined; /** * The grace period in seconds. * * @default 2592000 (30 days) */ gracePeriod?: number | undefined; /** * The path of the endpoint exposing the JWKS. * When set, this replaces the default /jwks endpoint. * The old endpoint will return 404. * * @default /jwks * @example "/.well-known/jwks.json" */ jwksPath?: string | undefined; } | undefined; jwt?: { /** * The issuer of the JWT */ issuer?: string | undefined; /** * The audience of the JWT */ audience?: string | string[] | undefined; /** * Set the "exp" (Expiration Time) Claim. * * - If a `number` is passed as an argument it is used as the claim directly. * - If a `Date` instance is passed as an argument it is converted to unix timestamp and used as the * claim. * - If a `string` is passed as an argument it is resolved to a time span, and then added to the * current unix timestamp and used as the claim. * * Format used for time span should be a number followed by a unit, such as "5 minutes" or "1 * day". * * Valid units are: "sec", "secs", "second", "seconds", "s", "minute", "minutes", "min", "mins", * "m", "hour", "hours", "hr", "hrs", "h", "day", "days", "d", "week", "weeks", "w", "year", * "years", "yr", "yrs", and "y". It is not possible to specify months. 365.25 days is used as an * alias for a year. * * If the string is suffixed with "ago", or prefixed with a "-", the resulting time span gets * subtracted from the current unix timestamp. A "from now" suffix can also be used for * readability when adding to the current unix timestamp. * * @default 15m */ expirationTime?: number | string | Date | undefined; /** * A function that is called to define the payload of the JWT */ definePayload?: (session: { user: User & Record<string, any>; session: Session & Record<string, any>; }) => Awaitable<Record<string, any>> | undefined; /** * A function that is called to get the subject of the JWT * * @default session.user.id */ getSubject?: (session: { user: User & Record<string, any>; session: Session & Record<string, any>; }) => Awaitable<string> | undefined; /** * A custom function to remote sign the jwt payload. * * All headers, such as `alg` and `kid`, * MUST be defined within this function. * You can safely define the header `typ: 'JWT'`. * * The optional `header` argument carries extra protected-header * parameters the caller requires (e.g. `typ: "logout+jwt"` for * OIDC Back-Channel Logout). Merge them into the signed header so * such profiles stay conformant; `alg`/`kid` remain yours to set. * * The optional `signingConfig` third argument carries the * per-call signing overrides (e.g. an OAuth protected resource pinned * to a specific kid/alg). Implementations are free to ignore * it for backward compatibility, but remote KMS integrations * SHOULD honor it so per-resource pinning works end-to-end. * * @requires jwks.remoteUrl * @invalidates other jwt.* options */ sign?: ((payload: JWTPayload, header?: { typ?: string; cty?: string; }, signingConfig?: { signingKeyId?: string | undefined; signingAlgorithm?: JWSAlgorithms | undefined; }) => Awaitable<string>) | undefined; } | undefined; /** * Disables setting JWTs through middleware. * * Recommended to set `true` when using an oAuth provider plugin * like OIDC or MCP where session payloads should not be signed. * * @default false */ disableSettingJwtHeader?: boolean | undefined; /** * Custom schema for the admin plugin */ schema?: InferOptionSchema<typeof schema> | undefined; /** * Custom adapter for the jwt plugin * * This will override the default adapter * * @default adapter from the database */ adapter?: { /** * A custom function to get the JWKS from the database or * other source * * This will override the default getJwks from the database * * @param ctx - The context of the request * @returns The JWKS */ getJwks?: (ctx: GenericEndpointContext) => Promise<Jwk[] | null | undefined>; /** * A custom function to create a new key in the database or * other source * * This will override the default createJwk from the database * * @param data - The key to create * @returns The created key */ createJwk?: (data: Omit<Jwk, "id">, ctx: GenericEndpointContext) => Promise<Jwk>; }; } /** * Asymmetric (JWS) Supported. * * @see https://github.com/panva/jose/issues/210 */ type JWKOptions = { alg: "EdDSA"; crv?: "Ed25519" | undefined; } | { alg: "ES256"; crv?: never | undefined; } | { alg: "ES512"; crv?: never | undefined; } | { alg: "PS256"; modulusLength?: number | undefined; } | { alg: "RS256"; modulusLength?: number | undefined; }; type JWSAlgorithms = JWKOptions["alg"]; interface Jwk { id: string; publicKey: string; privateKey: string; createdAt: Date; expiresAt?: Date; alg?: JWSAlgorithms | undefined; crv?: ("Ed25519" | "P-256" | "P-521") | undefined; } /** * A fully resolved signing key ready for JWT signing. * Produced by `resolveSigningKey`, consumed by `signJWT`. * Separates key resolution from signing so callers can * read the `alg` before constructing the JWT payload * (required for OIDC hash claims like at_hash). */ interface ResolvedSigningKey { alg: string; kid: string; privateKey: CryptoKey | Uint8Array; } //#endregion export { JWKOptions, JWSAlgorithms, Jwk, JwtOptions, ResolvedSigningKey };