Skip to main content

Email/Password Authentication

Auth

Traditional authentication with email verification and password reset flows.

Email Verification

Secure signup with email confirmation

Password Reset

Self-service password recovery

Strong Passwords

Configurable password requirements

Session Management

Secure token-based sessions

Sign Up

Register a new user with email and password:

Two Approaches

Sylphx supports two authentication approaches: Hosted UI (redirect-based) and Custom UI (headless hooks). Choose the one that fits your needs.
Hosted UI (Redirect-based)
'use client'

import { useAuth } from '@sylphx/sdk/react'

export function SignUpButton() {
  const { signUp } = useAuth()

  return (
    <button onClick={() => signUp({ redirectUrl: '/dashboard' })}>
      Sign Up
    </button>
  )
}

// This redirects to Sylphx's hosted signup page.
// After signup, user is redirected back to your app.
Custom UI (Headless Hook)
'use client'

import { useSignUpForm } from '@sylphx/sdk/react'

export function SignUpForm() {
  const {
    form,
    setName,
    setEmail,
    setPassword,
    handleSubmit,
    isLoading,
    error,
    passwordValid,
  } = useSignUpForm({ afterSignUpUrl: '/dashboard' })

  return (
    <form onSubmit={handleSubmit}>
      {error && <div className="text-red-500">{error}</div>}
      <input
        type="text"
        placeholder="Name"
        value={form.name}
        onChange={e => setName(e.target.value)}
      />
      <input
        type="email"
        placeholder="Email"
        value={form.email}
        onChange={e => setEmail(e.target.value)}
      />
      <input
        type="password"
        placeholder="Password (min 8 characters)"
        value={form.password}
        onChange={e => setPassword(e.target.value)}
      />
      {!passwordValid && form.password && (
        <p className="text-amber-500 text-sm">Password must be at least 8 characters</p>
      )}
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Signing up...' : 'Sign Up'}
      </button>
    </form>
  )
}

Sign In

Authenticate an existing user:

Hosted UI (Redirect-based)
'use client'

import { useAuth } from '@sylphx/sdk/react'

export function SignInButton() {
  const { signIn } = useAuth()

  return (
    <button onClick={() => signIn({ redirectUrl: '/dashboard' })}>
      Sign In
    </button>
  )
}

// This redirects to Sylphx's hosted login page.
// After login, user is redirected back to your app.
Custom UI (Headless Hook)
'use client'

import { useSignInForm } from '@sylphx/sdk/react'

export function SignInForm() {
  const {
    form,
    setEmail,
    setPassword,
    handlePasswordSubmit,
    isLoading,
    error,
    pendingTwoFactor,
    setOtp,
    handleTwoFactorVerify,
  } = useSignInForm({ afterSignInUrl: '/dashboard' })

  // Handle 2FA if required
  if (pendingTwoFactor) {
    return (
      <form onSubmit={handleTwoFactorVerify}>
        <h2>Enter Authentication Code</h2>
        <input
          type="text"
          placeholder="000000"
          value={form.otp}
          onChange={e => setOtp(e.target.value)}
          maxLength={6}
        />
        <button type="submit" disabled={isLoading}>
          {isLoading ? 'Verifying...' : 'Verify'}
        </button>
      </form>
    )
  }

  return (
    <form onSubmit={handlePasswordSubmit}>
      {error && <div className="text-red-500">{error}</div>}
      <input
        type="email"
        placeholder="Email"
        value={form.email}
        onChange={e => setEmail(e.target.value)}
      />
      <input
        type="password"
        placeholder="Password"
        value={form.password}
        onChange={e => setPassword(e.target.value)}
      />
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Signing in...' : 'Sign In'}
      </button>
    </form>
  )
}

Email Verification Flow

1

User Signs Up

User submits email and password. A verification email is automatically sent.

2

Check Email

User clicks the verification link in their email.

3

Verify & Redirect

Your verify page validates the token and redirects to the dashboard.

app/verify-email/page.tsx
'use client'

import { useAuth } from '@sylphx/sdk/react'
import { useSearchParams, useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'

export default function VerifyEmailPage() {
  const { verifyEmail } = useAuth()
  const searchParams = useSearchParams()
  const router = useRouter()
  const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading')

  useEffect(() => {
    const token = searchParams.get('token')
    if (!token) {
      setStatus('error')
      return
    }

    verifyEmail({ token })
      .then(() => {
        setStatus('success')
        setTimeout(() => router.push('/dashboard'), 2000)
      })
      .catch(() => setStatus('error'))
  }, [searchParams, verifyEmail, router])

  if (status === 'loading') return <div>Verifying your email...</div>
  if (status === 'success') return <div>Email verified! Redirecting...</div>
  return <div>Invalid or expired verification link.</div>
}

Resend Verification Email

Users can request a new verification email if the original expired. Use the resendVerificationEmail method:
await resendVerificationEmail({ email: user.email })

Password Reset

Allow users to reset their password via email:

Hosted UI (useAuth)
'use client'

import { useAuth } from '@sylphx/sdk/react'
import { useState } from 'react'

export function ForgotPasswordForm() {
  const { forgotPassword } = useAuth()
  const [email, setEmail] = useState('')
  const [sent, setSent] = useState(false)

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    await forgotPassword({ email })
    setSent(true)
  }

  if (sent) {
    return <div>Check your email for a reset link.</div>
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" placeholder="Enter your email" value={email} onChange={e => setEmail(e.target.value)} />
      <button type="submit">Send Reset Link</button>
    </form>
  )
}
Custom UI (Headless Hook)
'use client'

import { useForgotPasswordForm } from '@sylphx/sdk/react'

export function ForgotPasswordForm() {
  const {
    form,
    setEmail,
    handleSubmit,
    isLoading,
    error,
    success,
  } = useForgotPasswordForm({ redirectTo: '/reset-password' })

  if (success) {
    return <div>Check your email for a reset link.</div>
  }

  return (
    <form onSubmit={handleSubmit}>
      {error && <div className="text-red-500">{error}</div>}
      <input
        type="email"
        placeholder="Enter your email"
        value={form.email}
        onChange={e => setEmail(e.target.value)}
      />
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Sending...' : 'Send Reset Link'}
      </button>
    </form>
  )
}
Reset Password Page (useAuth)
'use client'

import { useAuth } from '@sylphx/sdk/react'
import { useSearchParams, useRouter } from 'next/navigation'
import { useState } from 'react'

export default function ResetPasswordPage() {
  const { resetPassword } = useAuth()
  const searchParams = useSearchParams()
  const router = useRouter()
  const [password, setPassword] = useState('')
  const [confirmPassword, setConfirmPassword] = useState('')
  const [error, setError] = useState('')

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()
    if (password !== confirmPassword) {
      setError('Passwords do not match')
      return
    }

    const token = searchParams.get('token')
    if (!token) {
      setError('Invalid reset link')
      return
    }

    try {
      await resetPassword({ token, newPassword: password })
      router.push('/login?reset=success')
    } catch (err) {
      setError('Failed to reset password. Link may have expired.')
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      {error && <div className="text-red-500">{error}</div>}
      <input type="password" placeholder="New password" value={password} onChange={e => setPassword(e.target.value)} />
      <input type="password" placeholder="Confirm password" value={confirmPassword} onChange={e => setConfirmPassword(e.target.value)} />
      <button type="submit">Reset Password</button>
    </form>
  )
}
Reset Password Page (Headless Hook)
'use client'

import { useResetPasswordForm } from '@sylphx/sdk/react'
import { useSearchParams } from 'next/navigation'

export default function ResetPasswordPage() {
  const searchParams = useSearchParams()
  const token = searchParams.get('token') || ''

  const {
    form,
    setPassword,
    setConfirmPassword,
    handleSubmit,
    isLoading,
    error,
    success,
    passwordsMatch,
    showPassword,
    toggleShowPassword,
  } = useResetPasswordForm({ token, afterResetUrl: '/login' })

  if (success) {
    return <div className="text-green-600">Password reset! Redirecting to login...</div>
  }

  return (
    <form onSubmit={handleSubmit}>
      {error && <div className="text-red-500">{error}</div>}
      <input
        type={showPassword ? 'text' : 'password'}
        placeholder="New password"
        value={form.password}
        onChange={e => setPassword(e.target.value)}
      />
      <input
        type={showPassword ? 'text' : 'password'}
        placeholder="Confirm password"
        value={form.confirmPassword}
        onChange={e => setConfirmPassword(e.target.value)}
      />
      {!passwordsMatch && form.confirmPassword && (
        <p className="text-amber-500 text-sm">Passwords do not match</p>
      )}
      <button type="button" onClick={toggleShowPassword}>
        {showPassword ? 'Hide' : 'Show'} Password
      </button>
      <button type="submit" disabled={isLoading}>
        {isLoading ? 'Resetting...' : 'Reset Password'}
      </button>
    </form>
  )
}

Password Requirements

Password validation is built-in with configurable requirements:

PropertyTypeDescription
Minimum length8 charsDefault minimum password length
UppercaseoptionalAt least one uppercase letter
LowercaseoptionalAt least one lowercase letter
NumberoptionalAt least one number
Special charoptionalAt least one special character

Custom Validation

Configure password requirements in your Sylphx Dashboard under App Settings → Security.

Sign Out

Sign out the current user:

'use client'

import { useAuth } from '@sylphx/sdk/react'

export function SignOutButton() {
  const { signOut } = useAuth()

  return (
    <button onClick={() => signOut()}>
      Sign Out
    </button>
  )
}