Quickstart
Get convex-auth running in a new Convex app in 10 minutes.
This guide assumes you already installed convex-auth and set JWT_PRIVATE_KEY, JWKS, CONVEX_SITE_URL, and EMAIL_FROM_ADDRESS. If not, start with Installation.
At the end you will have email/password and Google/GitHub/Discord OAuth sign-in working in a React (Vite) app. Building with a different framework? Each has its own guide: Next.js, TanStack Start, Hono, or React Native / Expo.
Project layout
When you are done, your project will look like this:
- convex/
- auth.config.ts
- registers
convex-authas the auth provider
- registers
- convex.config.ts
- mounts the
convexAuthcomponent
- mounts the
- auth.ts
- configures
convexAuth({...})and re-exports actions
- configures
- http.ts
- mounts
/api/auth/*routes
- mounts
- auth.config.ts
- src/
- main.tsx
- wraps the app with
ConvexAuthClientProvider
- wraps the app with
- SignIn.tsx
- calls
useAuthActions
- calls
- main.tsx
Steps
Register the auth provider
convex/auth.config.ts tells Convex that convex-auth is your auth provider:
import { createConvexAuthProvider } from "@vortex-api/convex-auth/convex";
export default {
providers: [createConvexAuthProvider()],
};Mount the component
convex/convex.config.ts mounts the convex-auth component and wires the key env vars:
import { defineApp } from "convex/server";
import { v } from "convex/values";
import auth from "@vortex-api/convex-auth/convex.config";
const app = defineApp({
env: {
JWT_PRIVATE_KEY: v.string(),
JWKS: v.string(),
},
});
app.use(auth, {
env: {
JWT_PRIVATE_KEY: app.env.JWT_PRIVATE_KEY,
JWKS: app.env.JWKS,
},
});
export default app;Configure auth in convex/auth.ts
Choose which providers are enabled and how they behave:
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("Email draft", draft);
return "email-id";
},
sendOnSignUp: true,
sendOnSignIn: false,
},
},
oauth: {
github: {
clientId: process.env.GITHUB_CLIENT_ID ?? "",
clientSecret: process.env.GITHUB_CLIENT_SECRET ?? "",
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID ?? "",
clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "",
},
discord: {
clientId: process.env.DISCORD_CLIENT_ID ?? "",
clientSecret: process.env.DISCORD_CLIENT_SECRET ?? "",
},
},
});
export const {
signUp,
signIn,
signOut,
updateSession,
sendEmailVerification,
verifyEmail,
sendPasswordReset,
resetPassword,
verifyPassword,
twoFactorEnable,
twoFactorVerifyTOTP,
twoFactorVerifyBackupCode,
twoFactorDisable,
twoFactorGenerateBackupCodes,
} = auth;Wire HTTP routes
convex/http.ts exposes the OAuth redirect/callback, email verification, and password reset routes:
import { httpRouter } from "convex/server";
import { auth } from "./auth";
const http = httpRouter();
auth.addHttpRoutes(http);
export default http;Wrap the React app
src/main.tsx wraps ConvexAuthClientProvider inside ConvexProvider — the auth provider calls useConvex(), so the Convex client context must be above it:
import { ConvexReactClient, ConvexProvider } from "convex/react";
import { ConvexAuthClientProvider } from "@vortex-api/convex-auth/react";
import { api } from "../convex/_generated/api";
import App from "./App";
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);
function Root() {
return (
<ConvexProvider client={convex}>
<ConvexAuthClientProvider actions={api.auth}>
<App />
</ConvexAuthClientProvider>
</ConvexProvider>
);
}
export default Root;Sign in from a component
import { useAuthActions } from "@vortex-api/convex-auth/react";
export function SignIn() {
const { signIn, isLoading, isAuthenticated } = 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,
});
}
if (isAuthenticated) {
return <p>Already signed in.</p>;
}
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" required />
<input name="password" type="password" required />
<button type="submit" disabled={isLoading}>
{isLoading ? "Signing in..." : "Sign in"}
</button>
</form>
);
}Verify and deploy
pnpm dlx @vortex-api/convex-auth check
pnpm dlx @vortex-api/convex-auth preflight
pnpm dlx convex deployEmail verification and password reset are one-click via the /api/auth/verify-email and /api/auth/reset-password/:token HTTP routes. In production, sendEmail should call Resend, Postmark, SES, or any other email service.
Next steps
- Look up every option in
Configuration. - Read the
Reactguide for all hooks. - See the Hono guide for a working Hono server deployment.