AuthFlow
Sign in

Quickstart

From nothing to working sign-up, sign-in, email verification and password reset in a Next.js app, in six steps.

Step 1

Create an app and copy the key

Create an account and then an app. For App URL, enter the origin your app runs on, such as https://myapp.com — every verification and reset email links back to it. The next screen shows your secret key once, with the two variables below already filled in. It cannot be shown again; if you lose it, create another app.

Step 2

Install the packages

npm install @authflowkit/sdk @authflowkit/ui

@authflowkit/sdk is the SDK — the proxy route, the provider, the hooks and the middleware. @authflowkit/ui is the ready-made screens. Both need Next.js 15 and React 18.2 or 19.

Step 3

Add the environment variables

# .env.local — server-side only. Neither may carry NEXT_PUBLIC_.
AUTHFLOW_API_URL=https://your-api.onrender.com
AUTHFLOW_SECRET_KEY=af_live_...

Step 4

Add the proxy route

// app/api/authflow/[...authflow]/route.ts
import { createAuthFlowHandler } from "@authflowkit/sdk/server";

export const { GET, POST } = createAuthFlowHandler();

Your pages only ever talk to this route, on your own domain. It adds the secret key on the server and forwards the call to AuthFlow, so the key never reaches a browser. The session cookie comes back through it as well, which makes it a first-party cookie on your domain — the kind Safari and Firefox keep.

Step 5

Add the provider and a sign-in page

// app/layout.tsx
import { AuthFlowProvider } from "@authflowkit/sdk";
import "@authflowkit/ui/styles.css";

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <AuthFlowProvider>{children}</AuthFlowProvider>
      </body>
    </html>
  );
}
// app/sign-in/page.tsx
"use client";
import { SignIn } from "@authflowkit/ui";

export default function SignInPage() {
  return <SignIn redirectTo="/dashboard" />;
}

That is a working sign-in page: validation, a loading state, a readable message for every error, and the two-factor step when a user has it turned on.

Step 6

Add the pages your emails link to

AuthFlow emails links to {App URL}/verify and {App URL}/reset. Those paths are fixed, so the pages must live exactly there. Without them a new user cannot confirm their address — and a user who has not confirmed it cannot sign in.

// app/sign-up/page.tsx
"use client";
import { SignUp } from "@authflowkit/ui";

export default function SignUpPage() {
  return <SignUp />;
}
// app/verify/page.tsx — the verification email links here
"use client";
import { VerifyEmail } from "@authflowkit/ui";

export default function VerifyPage() {
  // Confirming the address signs the user in, so send them into the app.
  return <VerifyEmail redirectTo="/dashboard" />;
}
// app/reset/page.tsx — the reset email links here
"use client";
import { ResetPassword } from "@authflowkit/ui";

export default function ResetPage() {
  return <ResetPassword />;
}

The components already link to each other: <SignIn /> points at /sign-up and /reset, and <SignUp /> and <ResetPassword /> point back at /sign-in. If your sign-in and sign-up pages live somewhere else, every link is a prop — see Components.

Reading the user

In a Client Component, from the provider’s state:

"use client";
import { useUser } from "@authflowkit/sdk";

export function Greeting() {
  const { status, user } = useUser();

  // Render for loading rather than falling through to the signed-out view,
  // or a signed-in user sees a sign-in button flash on every load.
  if (status === "loading") return null;
  return user ? <p>Hi, {user.email}</p> : null;
}

In a Server Component, where the user is in the first paint and there is no client fetch at all:

// A Server Component — the user is in the first paint, no client fetch.
import { currentUser } from "@authflowkit/sdk/server";
import { redirect } from "next/navigation";

export default async function Page() {
  const user = await currentUser();
  if (!user) redirect("/sign-in");
  return <p>Signed in as {user.email}</p>;
}

Protecting routes

// middleware.ts
import { authMiddleware } from "@authflowkit/sdk/middleware";

export default authMiddleware({
  // /verify and /reset must be public: whoever opens those links is,
  // by definition, not signed in yet.
  publicRoutes: ["/", "/sign-in", "/sign-up", "/verify", "/reset"],
});

export const config = { matcher: ["/((?!_next|.*\\..*).*)"] };

Two-factor

Optional per user, and it ships in v1. Put <EnableTotp /> on a settings page and a signed-in user can turn it on: scan a QR code, confirm a code, and save ten recovery codes. From then on <SignIn /> asks for a code automatically — no extra route and no extra code.

Light and dark

The components are dark by default and ship a light theme too. To let visitors choose, add <ThemeScript />, <ThemeProvider> and a <ThemeToggle /> — see theming.