Forget Password Flow doesn't work. #959

Closed
opened 2026-03-13 08:12:11 -05:00 by GiteaMirror · 0 comments
Owner

Originally created by @sosweetham on GitHub (Apr 2, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Try to implement forget password flow as described in the docs

Current vs. Expected behavior

The problem is that forget password function creates a verification schema entry in which,

df72736d36/packages/better-auth/src/api/routes/forget-password.ts (L111)

this is supposed to be a record id type but it is instead being converted to a string

which further leads to reset password not being able to find the account

df72736d36/packages/better-auth/src/api/routes/forget-password.ts (L263)

and ends up creating a new one instead which is totally not what we want

To fix, we simply remove the .toString() method call

What version of Better Auth are you using?

v1.2.5-1.2.6-beta3

Provide environment information

- OS: MacOS Sierra
- Browser: Zen Browser (Firefox)

(Irrelevant to our issue, filled for template's sake)

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

import { TimeUnit } from "@valkey/valkey-glide";
import { betterAuth } from "better-auth";
import { captcha, username } from "better-auth/plugins";
import { surrealAdapter } from "surreal-better-auth";
import { db } from "../../stores/db";
import { kvsClient } from "../../stores/kvs";
import { emailTransporter } from "../email";

export const auth = betterAuth({
    database: surrealAdapter(db),
    plugins:
        Bun.env.NODE_ENV == "dev"
            ? [username()]
            : [
                  captcha({
                      provider: "cloudflare-turnstile",
                      secretKey: Bun.env.TURNSTILE_SECRETKEY!,
                  }),
                  username(),
              ],
    emailAndPassword: {
        enabled: true,
        // requireEmailVerification: true,
        autoSignIn: false,
        sendResetPassword: async ({ user, url }) => {
            const extendedUser: typeof user & {
                username?: string;
                displayUsername?: string;
            } = user; // The plugin adds the username to the user object, but its not typed rn so this is a workaround.
            const html = `Hi ${user.name},
            You requested a password reset.
            If you did not request this, please ignore this email.
            ${
                extendedUser.username
                    ? `As a reminder, your username is: ${extendedUser.username}`
                    : ""
            }
            Click the link to reset your password: ${url}
            `;
            await emailTransporter.sendMail({
                from: "ham@mail.kodski.com",
                to: user.email,
                subject: "Anime-API - Reset your password",
                html,
            });
        },
        resetPasswordTokenExpiresIn: 3600, // 1 hour
        password: {
            hash: async (password: string) => {
                return await Bun.password.hash(password, {
                    algorithm: "argon2id",
                    memoryCost: 4,
                    timeCost: 3,
                });
            },
            verify: async (data: { hash: string; password: string }) => {
                return await Bun.password.verify(data.password, data.hash);
            },
        },
    },
    emailVerification: {
        sendVerificationEmail: async ({ user, url }) => {
            const html = `Click the link to verify your email: ${url}`;

            await emailTransporter.sendMail({
                from: "ham@mail.kodski.com",
                to: user.email,
                subject: "Anime-API - Verify your email",
                html,
            });
        },
    },
    trustedOrigins: ["http://localhost:5173"],
    secondaryStorage: {
        get: async (key: string) => {
            const value = (await kvsClient.get(key))?.toString();
            return value ? value : null;
        },
        set: async (key, value, ttl) => {
            if (ttl)
                await kvsClient.set(key, value, {
                    expiry: { type: TimeUnit.Seconds, count: ttl },
                });
            else await kvsClient.set(key, value);
        },
        delete: async (key) => {
            await kvsClient.del([key]);
        },
    },
    basePath: "/api/v1/auth",
    user: {
        deleteUser: {
            enabled: true,
            sendDeleteAccountVerification: async ({ user, url }) => {
                const html = `Click the link to verify your email: ${url}`;

                await emailTransporter.sendMail({
                    from: "ham@mail.kodski.com",
                    to: user.email,
                    subject: "Test Email",
                    html,
                });
            },
        },
    },
});

Additional context

No response

Originally created by @sosweetham on GitHub (Apr 2, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce Try to implement forget password flow as described in the docs ### Current vs. Expected behavior The problem is that forget password function creates a verification schema entry in which, https://github.com/better-auth/better-auth/blob/df72736d36c64747155b80d16d3fb9ea62ff7f25/packages/better-auth/src/api/routes/forget-password.ts#L111 this is supposed to be a record id type but it is instead being converted to a string which further leads to reset password not being able to find the account https://github.com/better-auth/better-auth/blob/df72736d36c64747155b80d16d3fb9ea62ff7f25/packages/better-auth/src/api/routes/forget-password.ts#L263 and ends up creating a new one instead which is totally not what we want To fix, we simply remove the .toString() method call ### What version of Better Auth are you using? v1.2.5-1.2.6-beta3 ### Provide environment information ```bash - OS: MacOS Sierra - Browser: Zen Browser (Firefox) (Irrelevant to our issue, filled for template's sake) ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { TimeUnit } from "@valkey/valkey-glide"; import { betterAuth } from "better-auth"; import { captcha, username } from "better-auth/plugins"; import { surrealAdapter } from "surreal-better-auth"; import { db } from "../../stores/db"; import { kvsClient } from "../../stores/kvs"; import { emailTransporter } from "../email"; export const auth = betterAuth({ database: surrealAdapter(db), plugins: Bun.env.NODE_ENV == "dev" ? [username()] : [ captcha({ provider: "cloudflare-turnstile", secretKey: Bun.env.TURNSTILE_SECRETKEY!, }), username(), ], emailAndPassword: { enabled: true, // requireEmailVerification: true, autoSignIn: false, sendResetPassword: async ({ user, url }) => { const extendedUser: typeof user & { username?: string; displayUsername?: string; } = user; // The plugin adds the username to the user object, but its not typed rn so this is a workaround. const html = `Hi ${user.name}, You requested a password reset. If you did not request this, please ignore this email. ${ extendedUser.username ? `As a reminder, your username is: ${extendedUser.username}` : "" } Click the link to reset your password: ${url} `; await emailTransporter.sendMail({ from: "ham@mail.kodski.com", to: user.email, subject: "Anime-API - Reset your password", html, }); }, resetPasswordTokenExpiresIn: 3600, // 1 hour password: { hash: async (password: string) => { return await Bun.password.hash(password, { algorithm: "argon2id", memoryCost: 4, timeCost: 3, }); }, verify: async (data: { hash: string; password: string }) => { return await Bun.password.verify(data.password, data.hash); }, }, }, emailVerification: { sendVerificationEmail: async ({ user, url }) => { const html = `Click the link to verify your email: ${url}`; await emailTransporter.sendMail({ from: "ham@mail.kodski.com", to: user.email, subject: "Anime-API - Verify your email", html, }); }, }, trustedOrigins: ["http://localhost:5173"], secondaryStorage: { get: async (key: string) => { const value = (await kvsClient.get(key))?.toString(); return value ? value : null; }, set: async (key, value, ttl) => { if (ttl) await kvsClient.set(key, value, { expiry: { type: TimeUnit.Seconds, count: ttl }, }); else await kvsClient.set(key, value); }, delete: async (key) => { await kvsClient.del([key]); }, }, basePath: "/api/v1/auth", user: { deleteUser: { enabled: true, sendDeleteAccountVerification: async ({ user, url }) => { const html = `Click the link to verify your email: ${url}`; await emailTransporter.sendMail({ from: "ham@mail.kodski.com", to: user.email, subject: "Test Email", html, }); }, }, }, }); ``` ### Additional context _No response_
GiteaMirror added the bug label 2026-03-13 08:12:11 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#959