UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

107 lines (105 loc) 4.05 kB
import { generateRandomString } from "./crypto/random.mjs"; import { symmetricDecrypt, symmetricEncrypt } from "./crypto/index.mjs"; import { expireCookie } from "./cookies/index.mjs"; import { BetterAuthError } from "@better-auth/core/error"; import * as z from "zod"; //#region src/state.ts const stateDataSchema = z.looseObject({ callbackURL: z.string(), codeVerifier: z.string(), errorURL: z.string().optional(), newUserURL: z.string().optional(), expiresAt: z.number(), link: z.object({ email: z.string(), userId: z.coerce.string() }).optional(), requestSignUp: z.boolean().optional() }); var StateError = class extends BetterAuthError { code; details; constructor(message, options) { super(message, options); this.code = options.code; this.details = options.details; } }; async function generateGenericState(c, stateData, settings) { const state = generateRandomString(32); if (c.context.oauthConfig.storeStateStrategy === "cookie") { const encryptedData = await symmetricEncrypt({ key: c.context.secret, data: JSON.stringify(stateData) }); const stateCookie$1 = c.context.createAuthCookie(settings?.cookieName ?? "oauth_state", { maxAge: 600 }); c.setCookie(stateCookie$1.name, encryptedData, stateCookie$1.attributes); return { state, codeVerifier: stateData.codeVerifier }; } const stateCookie = c.context.createAuthCookie(settings?.cookieName ?? "state", { maxAge: 300 }); await c.setSignedCookie(stateCookie.name, state, c.context.secret, stateCookie.attributes); const expiresAt = /* @__PURE__ */ new Date(); expiresAt.setMinutes(expiresAt.getMinutes() + 10); const verification = await c.context.internalAdapter.createVerificationValue({ value: JSON.stringify(stateData), identifier: state, expiresAt }); if (!verification) throw new StateError("Unable to create verification. Make sure the database adapter is properly working and there is a verification table in the database", { code: "state_generation_error" }); return { state: verification.identifier, codeVerifier: stateData.codeVerifier }; } async function parseGenericState(c, state, settings) { const storeStateStrategy = c.context.oauthConfig.storeStateStrategy; let parsedData; if (storeStateStrategy === "cookie") { const stateCookie = c.context.createAuthCookie(settings?.cookieName ?? "oauth_state"); const encryptedData = c.getCookie(stateCookie.name); if (!encryptedData) throw new StateError("State mismatch: auth state cookie not found", { code: "state_mismatch", details: { state } }); try { const decryptedData = await symmetricDecrypt({ key: c.context.secret, data: encryptedData }); parsedData = stateDataSchema.parse(JSON.parse(decryptedData)); } catch (error) { throw new StateError("State invalid: Failed to decrypt or parse auth state", { code: "state_invalid", details: { state }, cause: error }); } expireCookie(c, stateCookie); } else { const data = await c.context.internalAdapter.findVerificationValue(state); if (!data) throw new StateError("State mismatch: verification not found", { code: "state_mismatch", details: { state } }); parsedData = stateDataSchema.parse(JSON.parse(data.value)); const stateCookie = c.context.createAuthCookie(settings?.cookieName ?? "state"); const stateCookieValue = await c.getSignedCookie(stateCookie.name, c.context.secret); if (!c.context.oauthConfig.skipStateCookieCheck && (!stateCookieValue || stateCookieValue !== state)) throw new StateError("State mismatch: State not persisted correctly", { code: "state_security_mismatch", details: { state } }); expireCookie(c, stateCookie); await c.context.internalAdapter.deleteVerificationValue(data.id); } if (parsedData.expiresAt < Date.now()) throw new StateError("Invalid state: request expired", { code: "state_mismatch", details: { expiresAt: parsedData.expiresAt } }); return parsedData; } //#endregion export { StateError, generateGenericState, parseGenericState }; //# sourceMappingURL=state.mjs.map