[GH-ISSUE #1352] forget password endpoint returning always 200 OK #17335

Closed
opened 2026-04-15 15:27:20 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @devve2kccc on GitHub (Feb 4, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/1352

Is this suited for github?

To Reproduce

Im having an strange bug, i have my code base well coded, using next js and hono for api, everthing works, sign in, sign up, without any issue, and i can handle the errors, but on forgetPassword, something is happen, i pass an non existent email, on server logs shows the error, but returns an 200 OK, so i can't handle the error, because is always NULL.
Anyone have the same problem? Im doing something wrong?

My auth

export const auth = betterAuth({
  database: prismaAdapter(prisma, {
    provider: "postgresql",
  }),
  emailAndPassword: {
    enabled: true,
    sendResetPassword: async ({ user, url }) => {
      await sendEmail({
        to: user.email,
        subject: "Reset your password.",
        text: `Click the link to reset your password: ${url}`,
      });
    },
  },
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    },
  },
});

Handle forget password

async function onSubmit(values: z.infer<typeof forgetPasswordSchema>) {
    setLoading(true);
    const { error } = await authClient.forgetPassword(
      {
        email: values.email,
        redirectTo: "/reset-password",
      },
      {
        onError: (ctx) => {
          alert(ctx.error.message);
        },
      }
    );

    setLoading(false);
  }

My current backend

import { auth } from "@/lib/auth";
import { Hono } from "hono";
import { handle } from "hono/vercel";
import { cors } from "hono/cors";

const app = new Hono<{
  Variables: {
    user: typeof auth.$Infer.Session.user | null;
    session: typeof auth.$Infer.Session.session | null;
  };
}>().basePath("/api");

app.use(
  "/auth/**", // or replace with "*" to enable cors for all routes
  cors({
    origin: "http://localhost:3000", // replace with your origin
    allowHeaders: ["Content-Type", "Authorization"],
    allowMethods: ["POST", "GET", "OPTIONS"],
    exposeHeaders: ["Content-Length"],
    maxAge: 600,
    credentials: true,
  })
);

app.use("*", async (c, next) => {
  const session = await auth.api.getSession({ headers: c.req.raw.headers });

  if (!session) {
    c.set("user", null);
    c.set("session", null);
    return next();
  }

  c.set("user", session.user);
  c.set("session", session.session);
  return next();
});

app.on(["POST", "GET"], "/auth/**", (c) => {
  return auth.handler(c.req.raw);
});

app.get("/", async (c) => {
  const session = c.get("session");
  const user = c.get("user");

  if (!user) return c.json({ error: "Unauthenticated" }, 401);

  return c.json({
    user,
    session,
  });
});

export const GET = handle(app);
export const POST = handle(app);

Returning me 200 OK, even with error

2025-02-04T10:48:54.639Z ERROR [Better Auth]: Reset Password: User not found { email: 'dasdsada@gmad.com' }
 POST /api/auth/forget-password 200 in 2032ms

I also tested like this, but always pass to else, so is always success, beucase error is NULL

async function onSubmit(values: z.infer<typeof forgetPasswordSchema>) {
    setLoading(true);
    const { error } = await authClient.forgetPassword(
      {
        email: values.email,
        redirectTo: "/reset-password",
      }
    );

	if (error){
		alert(error)
	} else {
		alert("Success, email sent")
	}

    setLoading(false);
  }

Current vs. Expected behavior

Image Image Image

What version of Better Auth are you using?

1.1.15

Provide environment information

- MAC M2

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

Client

Auth config (if applicable)

import { sendEmail } from "@/app/actions";
import { PrismaClient } from "@prisma/client";
import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";

const prisma = new PrismaClient();

export const auth = betterAuth({
  database: prismaAdapter(prisma, {
    provider: "postgresql",
  }),
  emailAndPassword: {
    enabled: true,
    sendResetPassword: async ({ user, url }) => {
      await sendEmail({
        to: user.email,
        subject: "Reset your password.",
        text: `Click the link to reset your password: ${url}`,
      });
    },
  },
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    },
  },
});

Additional context

No response

Originally created by @devve2kccc on GitHub (Feb 4, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/1352 ### Is this suited for github? - [x] #1353 ### To Reproduce Im having an strange bug, i have my code base well coded, using next js and hono for api, everthing works, sign in, sign up, without any issue, and i can handle the errors, but on forgetPassword, something is happen, i pass an non existent email, on server logs shows the error, but returns an 200 OK, so i can't handle the error, because is always NULL. Anyone have the same problem? Im doing something wrong? #### My auth ```ts export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "postgresql", }), emailAndPassword: { enabled: true, sendResetPassword: async ({ user, url }) => { await sendEmail({ to: user.email, subject: "Reset your password.", text: `Click the link to reset your password: ${url}`, }); }, }, socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, }, }, }); ``` #### Handle forget password ```ts async function onSubmit(values: z.infer<typeof forgetPasswordSchema>) { setLoading(true); const { error } = await authClient.forgetPassword( { email: values.email, redirectTo: "/reset-password", }, { onError: (ctx) => { alert(ctx.error.message); }, } ); setLoading(false); } ``` #### My current backend ```ts import { auth } from "@/lib/auth"; import { Hono } from "hono"; import { handle } from "hono/vercel"; import { cors } from "hono/cors"; const app = new Hono<{ Variables: { user: typeof auth.$Infer.Session.user | null; session: typeof auth.$Infer.Session.session | null; }; }>().basePath("/api"); app.use( "/auth/**", // or replace with "*" to enable cors for all routes cors({ origin: "http://localhost:3000", // replace with your origin allowHeaders: ["Content-Type", "Authorization"], allowMethods: ["POST", "GET", "OPTIONS"], exposeHeaders: ["Content-Length"], maxAge: 600, credentials: true, }) ); app.use("*", async (c, next) => { const session = await auth.api.getSession({ headers: c.req.raw.headers }); if (!session) { c.set("user", null); c.set("session", null); return next(); } c.set("user", session.user); c.set("session", session.session); return next(); }); app.on(["POST", "GET"], "/auth/**", (c) => { return auth.handler(c.req.raw); }); app.get("/", async (c) => { const session = c.get("session"); const user = c.get("user"); if (!user) return c.json({ error: "Unauthenticated" }, 401); return c.json({ user, session, }); }); export const GET = handle(app); export const POST = handle(app); ``` #### Returning me 200 OK, even with error ```bash 2025-02-04T10:48:54.639Z ERROR [Better Auth]: Reset Password: User not found { email: 'dasdsada@gmad.com' } POST /api/auth/forget-password 200 in 2032ms ``` #### I also tested like this, but always pass to else, so is always success, beucase error is NULL ```ts async function onSubmit(values: z.infer<typeof forgetPasswordSchema>) { setLoading(true); const { error } = await authClient.forgetPassword( { email: values.email, redirectTo: "/reset-password", } ); if (error){ alert(error) } else { alert("Success, email sent") } setLoading(false); } ``` ### Current vs. Expected behavior <img width="808" alt="Image" src="https://github.com/user-attachments/assets/984a9094-ce73-4c2e-b398-b6744b278271" /> <img width="808" alt="Image" src="https://github.com/user-attachments/assets/bbda0680-ad76-4a75-ae9a-bb9063b5265d" /> <img width="639" alt="Image" src="https://github.com/user-attachments/assets/8c3d5e22-02a1-48e0-a496-ebf6c0ae3e1f" /> ### What version of Better Auth are you using? 1.1.15 ### Provide environment information ```bash - MAC M2 ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript import { sendEmail } from "@/app/actions"; import { PrismaClient } from "@prisma/client"; import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; const prisma = new PrismaClient(); export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "postgresql", }), emailAndPassword: { enabled: true, sendResetPassword: async ({ user, url }) => { await sendEmail({ to: user.email, subject: "Reset your password.", text: `Click the link to reset your password: ${url}`, }); }, }, socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, }, }, }); ``` ### Additional context _No response_
GiteaMirror added the lockedbug labels 2026-04-15 15:27:20 -05:00
Author
Owner

@Bekacru commented on GitHub (Feb 4, 2025):

see this please: https://github.com/better-auth/better-auth/issues/884

<!-- gh-comment-id:2634439325 --> @Bekacru commented on GitHub (Feb 4, 2025): see this please: https://github.com/better-auth/better-auth/issues/884
Author
Owner

@blurskye commented on GitHub (Feb 6, 2025):

I think that is standard practice to avoid leaking who has a account in your service

<!-- gh-comment-id:2639055619 --> @blurskye commented on GitHub (Feb 6, 2025): I think that is standard practice to avoid leaking who has a account in your service
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#17335