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:
| Property | Type | Description |
|---|---|---|
Minimum length | 8 chars | Default minimum password length |
Uppercase | optional | At least one uppercase letter |
Lowercase | optional | At least one lowercase letter |
Number | optional | At least one number |
Special char | optional | At 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>
)
}