Email and password
Configure email/password sign-up, sign-in, email verification, and password reset.
convex-auth implements email/password authentication as a native Convex provider. No external auth service is needed.
Enable
// convex/auth.ts
import { components } from "./_generated/api";
import { convexAuth, type EmailDraft } from "@vortex-api/convex-auth/convex";
const siteUrl = process.env.CONVEX_SITE_URL?.replace(/\/$/, "");
export const auth = convexAuth({
component: components.convexAuth,
emailAndPassword: {
enabled: true,
email: {
from: process.env.EMAIL_FROM_ADDRESS ?? "auth@example.com",
appOrigin: siteUrl,
sendEmail: async (draft: EmailDraft) => {
// production: call Resend/Postmark/SES
console.log(draft);
return "email-id";
},
sendOnSignUp: true,
sendOnSignIn: false,
},
},
});
sendEmail receives an EmailDraft with from, to, subject, html, and text. It must return an id or any truthy string.
Sign up
import { useAuthActions } from "@vortex-api/convex-auth/react";
export function SignUp() {
const { signUp, isLoading } = useAuthActions();
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
const formData = new FormData(event.currentTarget);
await signUp({
email: formData.get("email") as string,
password: formData.get("password") as string,
name: formData.get("name") as string,
});
}
return (
<form onSubmit={handleSubmit}>
<input name="name" placeholder="Name" />
<input name="email" type="email" required />
<input name="password" type="password" required minLength={8} />
<button type="submit" disabled={isLoading}>
Sign up
</button>
</form>
);
}
By default the user is signed in immediately. If requireVerifiedEmail is true, the session is still created but sign-in can be rejected on the next request.
Sign in
import { useAuthActions } from "@vortex-api/convex-auth/react";
export function SignIn() {
const { signIn, isLoading } = useAuthActions();
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
const formData = new FormData(event.currentTarget);
await signIn({
email: formData.get("email") as string,
password: formData.get("password") as string,
rememberMe: true,
});
}
// ...
}
rememberMe controls whether the refresh token is persisted across browser restarts.
Email verification
If sendOnSignUp: true, a verification email is sent automatically. The verification link should point to /api/auth/verify-email on your Convex site. The user clicks the link, the token is validated, and the browser is redirected to callbackURL.
You can also request a new email from the client:
const client = useConvexAuthClient();
await client.sendVerificationEmail({ email });
await client.verifyEmail({ query: { token } });
Password reset
Request the reset:
const client = useConvexAuthClient();
await client.forgetPassword({ email, redirectTo: "https://your-app/reset" });
The email links to /api/auth/reset-password/:token. On your reset page, complete the reset:
const client = useConvexAuthClient();
await client.resetPassword({ token, newPassword });
Options
See docs/configuration.mdx for the full emailAndPassword table, including requireVerifiedEmail, checkBreach, rateLimit, and revokeSessionsOnPasswordReset.