Skip to content
v3.0.1 — MCP OAuth, organizations, passkeys, SSR adaptersChangelog
Convex Auth
Esc
↑↓navigate↵open⌘Jpreview
On this page

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.

Was this page helpful?