Features
Authentication

Authentication

Sunday provides a complete authentication system with email verification and password reset.

Features

  • Email/Password Registration - Secure signup with password hashing
  • Email Verification - Confirm user email addresses
  • JWT Tokens - Secure session management
  • Password Reset - Email-based password recovery
  • Rate Limiting - Protection against brute force attacks
  • CAPTCHA - Spam prevention on auth forms

Sign Up Flow

Signup Request

POST /api/auth/signup
Content-Type: application/json
 
{
  "name": "John Doe",
  "email": "john@example.com",
  "password": "securePassword123",
  "captchaToken": "cap_..." // Optional CAPTCHA token
}

Response

{
  "success": true,
  "message": "Account created. Please check your email to verify."
}

Sign In Flow

Signin Request

POST /api/auth/signin
Content-Type: application/json
 
{
  "email": "john@example.com",
  "password": "securePassword123"
}

Response

{
  "success": true,
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "user": {
    "id": "507f1f77bcf86cd799439011",
    "name": "John Doe",
    "email": "john@example.com",
    "avatar": "/default-avatar.png"
  }
}

Password Reset Flow

Request Reset

POST /api/auth/forgot-password
Content-Type: application/json
 
{
  "email": "john@example.com"
}

Reset Password

POST /api/auth/reset-password
Content-Type: application/json
 
{
  "token": "reset-token-from-email",
  "password": "newSecurePassword456"
}

Security Features

Password Hashing

Passwords are hashed using bcrypt with a cost factor of 12:

import { hash, compare } from "bcryptjs"
 
// Hash password on signup
const hashedPassword = await hash(password, 12)
 
// Verify password on signin
const isValid = await compare(password, hashedPassword)

JWT Tokens

Tokens are signed with HS256 and expire after 7 days:

import { sign, verify } from "jsonwebtoken"
 
// Generate token
const token = sign(
  { userId, email },
  process.env.JWT_SECRET,
  { expiresIn: "7d" }
)
 
// Verify token
const decoded = verify(token, process.env.JWT_SECRET)

Rate Limiting

Authentication endpoints are rate limited:

EndpointLimitWindow
/api/auth/signin5 requests1 minute
/api/auth/signup5 requests1 minute
/api/auth/forgot-password5 requests1 minute

Email Verification Tokens

Verification tokens:

  • Generated using crypto.randomBytes(32)
  • Hashed with SHA-256 before storage
  • Expire after 24 hours
import crypto from "crypto"
 
// Generate token
const token = crypto.randomBytes(32).toString("hex")
 
// Hash for storage
const hashedToken = crypto
  .createHash("sha256")
  .update(token)
  .digest("hex")

Email Templates

Verification and password reset emails use Apple-inspired designs:

  • Clean, minimal layout
  • Clear call-to-action button
  • Mobile-responsive
  • Both HTML and plain text versions

Client-Side Usage

Store Authentication State

import { useAppStore } from '@/lib/store'
 
function AuthProvider({ children }) {
  const { currentUser, setCurrentUser } = useAppStore()
  
  useEffect(() => {
    // Check for stored token
    const token = localStorage.getItem('authToken')
    if (token) {
      // Verify and fetch user
      fetch('/api/auth/me', {
        headers: { Authorization: `Bearer ${token}` }
      })
      .then(res => res.json())
      .then(user => setCurrentUser(user))
    }
  }, [])
  
  return children
}

Protected Routes

function ProtectedPage() {
  const currentUser = useAppStore((s) => s.currentUser)
  const router = useRouter()
  
  useEffect(() => {
    if (!currentUser) {
      router.push('/signin')
    }
  }, [currentUser])
  
  if (!currentUser) return null
  
  return <div>Protected content</div>
}