better-auth
Version:
The most comprehensive authentication framework for TypeScript.
107 lines (105 loc) • 4.05 kB
JavaScript
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