AuthFlow
Sign in

Components

Import the styles once in your root layout — import "@authflowkit/ui/styles.css" — and put the provider from the quickstart above every component. Every link and redirect is a prop, and every piece of text can be replaced through labels:

<SignIn labels={{ heading: "Welcome back", submit: "Log in" }} />

<SignIn />

Email and password sign-in, with links to sign up and to reset a forgotten password. When the user has two-factor on, it asks for their code in place.

Live preview — type in it, press the buttons. Nothing is sent anywhere.

Sign in

The default state. With two-factor on, the code step replaces this form in place.

PropTypeDefaultWhat it does
redirectTostring"/"Where to send the user after they sign in.
forgotPasswordUrlstring"/reset"Where the forgot-password link goes. The reset email lands on /reset too, so one page serves both.
signUpUrlstring"/sign-up"Where the sign-up link goes.
onSignedIn(user: AuthUser) => void—Called instead of a full page load to redirectTo. Pass it to navigate with your router.
onTotpRequired(challenge: TotpChallengeHandoff) => void—Called when the password was right but the user has two-factor on, with the token for the code step. SignIn then leaves that step to you.
labelsPartial<SignInLabels>—Replace any of the text listed below.
classNamestring—An extra class name for styling.

Text you can replace with labels

  • heading
  • emailLabel
  • passwordLabel
  • submit
  • forgotPassword
  • noAccount
  • signUp
  • resendVerification
  • verificationSent

<SignUp />

Creates an account, then replaces the form with a check-your-email message. The message is the same whether or not the address was already registered, so the form never reveals who has an account.

Live preview — type in it, press the buttons. Nothing is sent anywhere.

Create an account

At least 8 characters.

The default state. After a successful submit the form is replaced by a check-your-email message.

PropTypeDefaultWhat it does
signInUrlstring"/sign-in"Where the sign-in link goes.
labelsPartial<SignUpLabels>—Replace any of the text listed below.
classNamestring—An extra class name for styling.

Text you can replace with labels

  • heading
  • emailLabel
  • passwordLabel
  • passwordHint
  • submit
  • haveAccount
  • signIn
  • sentHeading
  • sentBody
  • invalidEmail
  • shortPassword

<VerifyEmail />

Confirms the address from the emailed link and signs the user in. An expired, used or unknown link gets one clear message and a button to send a new one.

Mount it at /verify. AuthFlow’s emails link there, and the path is fixed.

Live preview — type in it, press the buttons. Nothing is sent anywhere.

This link is incomplete

The address in your browser is missing the part that identifies you. Copy the whole link from the email, or ask for a new one below.

The state when the link arrived without its token. With a valid token it shows progress, then success.

PropTypeDefaultWhat it does
tokenstring—The token. Read from ?token= in the URL when you leave it out.
redirectTostring"/"Where to send the user once the address is confirmed.
onVerified() => void—Called instead of navigating to redirectTo.
labelsPartial<VerifyEmailLabels>—Replace any of the text listed below.
classNamestring—An extra class name for styling.

Text you can replace with labels

  • workingHeading
  • workingBody
  • successHeading
  • successBody
  • failedHeading
  • noTokenHeading
  • noTokenBody
  • emailLabel
  • resend
  • resent
  • continueLabel

<ResetPassword />

Both halves of a password reset on one route. With no token in the URL it asks for an email address; opened from the emailed link, it asks for a new password. Resetting signs out every session and does not sign the user in.

Mount it at /reset. AuthFlow’s emails link there, and the path is fixed.

Live preview — type in it, press the buttons. Nothing is sent anywhere.

Reset your password

Enter your email and we will send you a link.

Request mode, shown when there is no token in the URL. Opened from the email it asks for a new password instead.

PropTypeDefaultWhat it does
mode"request" | "confirm"—Force one half. Worked out from the URL when you leave it out.
tokenstring—The reset token. Read from ?token= in the URL when you leave it out.
signInUrlstring"/sign-in"Where to send the user after they set a new password.
labelsPartial<ResetPasswordLabels>—Replace any of the text listed below.
classNamestring—An extra class name for styling.

Text you can replace with labels

  • requestHeading
  • requestBody
  • emailLabel
  • requestSubmit
  • sentHeading
  • sentBody
  • confirmHeading
  • passwordLabel
  • passwordHint
  • confirmSubmit
  • doneHeading
  • doneBody
  • noTokenHeading
  • noTokenBody
  • requestNewLink
  • backToSignIn
  • invalidEmail
  • shortPassword

<UserButton />

Shows who is signed in, with a sign-out action. Renders nothing when nobody is signed in, and a placeholder while the session loads.

Live preview — type in it, press the buttons. Nothing is sent anywhere.

Signed in, with the menu open to a sample address. Signed out it renders nothing at all.

PropTypeDefaultWhat it does
signOutTostring"/"Where to go after signing out.
onSignedOut() => void—Called instead of navigating to signOutTo, once the user has signed out.
labelsPartial<UserButtonLabels>—Replace any of the text listed below.
classNamestring—An extra class name for styling.

Text you can replace with labels

  • signOut
  • menuLabel

<TotpChallenge />

The six-digit code step, with a switch to a recovery code. SignIn shows it for you; use it on its own only in a sign-in form of your own.

Live preview — type in it, press the buttons. Nothing is sent anywhere.

Enter your code

Open your authenticator app and enter the 6-digit code.

The default state. SignIn shows this for you; mount it yourself only inside a sign-in form of your own.

PropTypeDefaultWhat it does
challengeTokenstringrequiredThe token from a correct password. Held in memory for this step only.
redirectTostring"/"Where to send the user once the code is accepted.
signInUrlstring"/sign-in"Where to start again if the step expires.
onVerified(user: AuthUser) => void—Called instead of navigating to redirectTo.
labelsPartial<TotpChallengeLabels>—Replace any of the text listed below.
classNamestring—An extra class name for styling.

Text you can replace with labels

  • heading
  • body
  • codeLabel
  • recoveryLabel
  • submit
  • useRecovery
  • useAuthenticator
  • expiredHeading
  • expiredBody
  • backToSignIn
  • lowCodes

<EnableTotp />

Lets a signed-in user turn two-factor on or off: a QR code and a manual key, a confirmation code, and ten recovery codes shown exactly once. Renders nothing when nobody is signed in.

Live preview — type in it, press the buttons. Nothing is sent anywhere.

Two-factor authentication

Add a second step to signing in. You will need an authenticator app such as Google Authenticator, Authy or 1Password.

Two-factor off, for a signed-in user. Turning it on reveals a QR code, then the recovery codes, once.

PropTypeDefaultWhat it does
labelsPartial<EnableTotpLabels>—Replace any of the text listed below.
classNamestring—An extra class name for styling.

Text you can replace with labels

  • offHeading
  • offBody
  • start
  • scanHeading
  • scanBody
  • manualLabel
  • codeLabel
  • confirmSubmit
  • codesHeading
  • codesWarning
  • copyCodes
  • copied
  • downloadCodes
  • codesDone
  • onHeading
  • onBody
  • disable
  • disableHeading
  • disableWhy
  • passwordLabel
  • disableSubmit
  • cancel

Hooks and helpers

NameImport fromWhat it does
<AuthFlowProvider>@authflowkit/sdkWrap your app with it once, in app/layout.tsx. It reads the session and shares it with every component and hook below it.
useUser()@authflowkit/sdkFor Client Components. Returns status ("loading", "signedIn" or "signedOut"), user, error and refresh(). A failed read is signedOut with error set, so an outage never looks like a sign-out.
currentUser()@authflowkit/sdk/serverFor Server Components and route handlers. Returns the signed-in user or null, and throws only when AuthFlow could not be reached. It checks the session with AuthFlow, so it is the check to rely on.
createAuthFlowHandler()@authflowkit/sdk/serverBuilds the proxy route. Reads AUTHFLOW_API_URL and AUTHFLOW_SECRET_KEY from the environment; pass { apiUrl, secretKey, cookieName } only to override them.
authMiddleware()@authflowkit/sdk/middlewareSends visitors without a session cookie to sign-in. Options: publicRoutes (exact paths, or a prefix ending in *), signInUrl (default "/sign-in"), cookieName, and returnToParam (default "returnTo"). A routing helper, not the security boundary.
AuthFlowError, isAuthFlowError()@authflowkit/sdkThe only error any call rejects with. It has a stable code, and retryAfterSeconds when the code is RATE_LIMITED. Test with isAuthFlowError(e) rather than instanceof.

Theming

Dark is the default and a light theme ships alongside it. To let visitors switch, set the theme before the first paint and add a toggle:

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

export default function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <ThemeScript />
      </head>
      <body>
        <ThemeProvider>
          <AuthFlowProvider>{children}</AuthFlowProvider>
        </ThemeProvider>
      </body>
    </html>
  );
}
NameImport fromWhat it does
<ThemeScript />@authflowkit/uiGoes in <head>. Sets the theme before the first paint, so the page never flashes the wrong one. Add suppressHydrationWarning to <html>, because the script changes an attribute there before React loads.
<ThemeProvider>@authflowkit/uiWraps your app. defaultTheme is "dark", "light" or "system" (default "dark") and applies until the visitor chooses; their choice is remembered and always wins. Give ThemeScript the same defaultTheme.
<ThemeToggle />@authflowkit/uiA button that switches between dark and light. Props: label (default "Light theme") and className.
useTheme()@authflowkit/uiThe current theme ("dark", "light", or null until the page has mounted), plus setTheme() and toggleTheme(), for building your own control.

Every colour, radius and spacing value is a CSS custom property whose name starts with --af-. Set any of them on :root after importing the styles to restyle every component. If you change --af-primary, check that --af-primary-ink still reads clearly on top of it.