mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-07 20:37:44 +00:00
fix(better-auth): moved email verification check after password check (#4835)
This commit is contained in:
@@ -286,43 +286,6 @@ export const username = (options?: UsernameOptions) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
ctx.context.options?.emailAndPassword?.requireEmailVerification &&
|
|
||||||
!user.emailVerified
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
!ctx.context.options?.emailVerification?.sendVerificationEmail
|
|
||||||
) {
|
|
||||||
throw new APIError("FORBIDDEN", {
|
|
||||||
message: ERROR_CODES.EMAIL_NOT_VERIFIED,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.context.options?.emailVerification?.sendOnSignIn) {
|
|
||||||
const token = await createEmailVerificationToken(
|
|
||||||
ctx.context.secret,
|
|
||||||
user.email,
|
|
||||||
undefined,
|
|
||||||
ctx.context.options.emailVerification?.expiresIn,
|
|
||||||
);
|
|
||||||
const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${
|
|
||||||
ctx.body.callbackURL || "/"
|
|
||||||
}`;
|
|
||||||
await ctx.context.options.emailVerification.sendVerificationEmail(
|
|
||||||
{
|
|
||||||
user: user,
|
|
||||||
url,
|
|
||||||
token,
|
|
||||||
},
|
|
||||||
ctx.request,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new APIError("FORBIDDEN", {
|
|
||||||
message: ERROR_CODES.EMAIL_NOT_VERIFIED,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const account = await ctx.context.adapter.findOne<Account>({
|
const account = await ctx.context.adapter.findOne<Account>({
|
||||||
model: "account",
|
model: "account",
|
||||||
where: [
|
where: [
|
||||||
@@ -360,6 +323,44 @@ export const username = (options?: UsernameOptions) => {
|
|||||||
message: ERROR_CODES.INVALID_USERNAME_OR_PASSWORD,
|
message: ERROR_CODES.INVALID_USERNAME_OR_PASSWORD,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
ctx.context.options?.emailAndPassword?.requireEmailVerification &&
|
||||||
|
!user.emailVerified
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!ctx.context.options?.emailVerification?.sendVerificationEmail
|
||||||
|
) {
|
||||||
|
throw new APIError("FORBIDDEN", {
|
||||||
|
message: ERROR_CODES.EMAIL_NOT_VERIFIED,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.context.options?.emailVerification?.sendOnSignIn) {
|
||||||
|
const token = await createEmailVerificationToken(
|
||||||
|
ctx.context.secret,
|
||||||
|
user.email,
|
||||||
|
undefined,
|
||||||
|
ctx.context.options.emailVerification?.expiresIn,
|
||||||
|
);
|
||||||
|
const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${
|
||||||
|
ctx.body.callbackURL || "/"
|
||||||
|
}`;
|
||||||
|
await ctx.context.options.emailVerification.sendVerificationEmail(
|
||||||
|
{
|
||||||
|
user: user,
|
||||||
|
url,
|
||||||
|
token,
|
||||||
|
},
|
||||||
|
ctx.request,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new APIError("FORBIDDEN", {
|
||||||
|
message: ERROR_CODES.EMAIL_NOT_VERIFIED,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const session = await ctx.context.internalAdapter.createSession(
|
const session = await ctx.context.internalAdapter.createSession(
|
||||||
user.id,
|
user.id,
|
||||||
ctx,
|
ctx,
|
||||||
|
|||||||
@@ -533,3 +533,44 @@ describe("post normalization flow", async (it) => {
|
|||||||
expect(session?.user.displayUsername).toBe("Test Username");
|
expect(session?.user.displayUsername).toBe("Test Username");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("username email verification flow (no info leak)", async (it) => {
|
||||||
|
const { client } = await getTestInstance(
|
||||||
|
{
|
||||||
|
emailAndPassword: { enabled: true, requireEmailVerification: true },
|
||||||
|
plugins: [username()],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
clientOptions: {
|
||||||
|
plugins: [usernameClient()],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
it("returns INVALID_USERNAME_OR_PASSWORD for wrong password even if email is unverified", async () => {
|
||||||
|
await client.signUp.email({
|
||||||
|
email: "unverified-user@example.com",
|
||||||
|
username: "unverified_user",
|
||||||
|
password: "correct-password",
|
||||||
|
name: "Unverified User",
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await client.signIn.username({
|
||||||
|
username: "unverified_user",
|
||||||
|
password: "wrong-password",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.error?.status).toBe(401);
|
||||||
|
expect(res.error?.code).toBe("INVALID_USERNAME_OR_PASSWORD");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns EMAIL_NOT_VERIFIED only after a correct password for an unverified user", async () => {
|
||||||
|
const res = await client.signIn.username({
|
||||||
|
username: "unverified_user",
|
||||||
|
password: "correct-password",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res.error?.status).toBe(403);
|
||||||
|
expect(res.error?.code).toBe("EMAIL_NOT_VERIFIED");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user