Nest Authbeta

Pre-built React auth forms

Copy-paste login, signup, MFA challenge, and forgot-password forms.

The library is headless — no UI ships with it. These are starting points; restyle as you see fit.

Login

import { FormEvent, useState } from 'react';
import { useNestAuth } from '@ackplus/nest-auth-react';
 
export function LoginForm({ onSuccess }: { onSuccess: () => void }) {
  const { login, error, isLoading } = useNestAuth();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [mfaRequired, setMfaRequired] = useState(false);
  const [otp, setOtp] = useState('');
 
  async function handleSubmit(e: FormEvent) {
    e.preventDefault();
    const resp = await login({ credentials: { email, password } });
    if (resp.isRequiresMfa) setMfaRequired(true);
    else onSuccess();
  }
 
  if (mfaRequired) return <MfaChallenge onSuccess={onSuccess} />;
 
  return (
    <form onSubmit={handleSubmit}>
      <input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="email" />
      <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="password" />
      <button disabled={isLoading}>Sign in</button>
      {error && <p style={{ color: 'red' }}>{error.message}</p>}
    </form>
  );
}

Signup

export function SignupForm({ onSuccess }) {
  const { signup, error, isLoading } = useNestAuth();
  const [values, setValues] = useState({ email: '', password: '', firstName: '' });
 
  return (
    <form
      onSubmit={async (e) => {
        e.preventDefault();
        await signup(values);
        onSuccess();
      }}
    >
      <input placeholder="first name" value={values.firstName}
             onChange={(e) => setValues({ ...values, firstName: e.target.value })} />
      <input placeholder="email" value={values.email}
             onChange={(e) => setValues({ ...values, email: e.target.value })} />
      <input type="password" placeholder="password" value={values.password}
             onChange={(e) => setValues({ ...values, password: e.target.value })} />
      <button disabled={isLoading}>Sign up</button>
      {error && <p style={{ color: 'red' }}>{error.message}</p>}
    </form>
  );
}

MFA challenge

export function MfaChallenge({ onSuccess }: { onSuccess: () => void }) {
  const { send2fa, verify2fa, error } = useNestAuth();
  const [otp, setOtp] = useState('');
  const [trustDevice, setTrustDevice] = useState(false);
 
  // Auto-send the email/SMS code on mount
  useState(() => { send2fa('email'); });
 
  return (
    <form
      onSubmit={async (e) => {
        e.preventDefault();
        await verify2fa({ otp, method: 'email', trustDevice });
        onSuccess();
      }}
    >
      <input value={otp} onChange={(e) => setOtp(e.target.value)} placeholder="6-digit code" />
      <label>
        <input type="checkbox" checked={trustDevice} onChange={(e) => setTrustDevice(e.target.checked)} />
        Trust this device for 14 days
      </label>
      <button>Verify</button>
      {error && <p style={{ color: 'red' }}>{error.message}</p>}
    </form>
  );
}

Forgot password

export function ForgotPasswordFlow() {
  const { forgotPassword, verifyForgotPasswordOtp, resetPassword } = useNestAuth();
  const [step, setStep] = useState<'email' | 'code' | 'reset' | 'done'>('email');
  const [email, setEmail] = useState('');
  const [code, setCode] = useState('');
  const [token, setToken] = useState('');
  const [newPassword, setNewPassword] = useState('');
 
  if (step === 'email') {
    return (
      <form onSubmit={async (e) => {
        e.preventDefault();
        await forgotPassword({ email });
        setStep('code');
      }}>
        <input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="email" />
        <button>Send code</button>
      </form>
    );
  }
 
  if (step === 'code') {
    return (
      <form onSubmit={async (e) => {
        e.preventDefault();
        const res = await verifyForgotPasswordOtp({ email, code });
        setToken(res.resetToken!);
        setStep('reset');
      }}>
        <input value={code} onChange={(e) => setCode(e.target.value)} placeholder="code from email" />
        <button>Verify</button>
      </form>
    );
  }
 
  if (step === 'reset') {
    return (
      <form onSubmit={async (e) => {
        e.preventDefault();
        await resetPassword({ token, newPassword });
        setStep('done');
      }}>
        <input type="password" value={newPassword} onChange={(e) => setNewPassword(e.target.value)} placeholder="new password" />
        <button>Reset</button>
      </form>
    );
  }
 
  return <p>Done — please log in.</p>;
}

On this page