Authentication ensures users are who they claim to be. It includes:
Create a form that submits to a Server Action:
// app/ui/signup-form.tsx
import { signup } from '@/app/actions/auth'
export function SignupForm() {
return (
<form action={signup}>
<input name="name" placeholder="Name" />
<input name="email" type="email" placeholder="Email" />
<input name="password" type="password" />
<button type="submit">Sign Up</button>
</form>
)
}Use Zod to define a schema:
// app/lib/definitions.ts
import * as z from 'zod'
export const SignupFormSchema = z.object({
name: z.string().min(2).trim(),
email: z.email().trim(),
password: z
.string()
.min(8)
.regex(/[a-zA-Z]/)
.regex(/[0-9]/)
.regex(/[^a-zA-Z0-9]/)
.trim(),
})Validate and return errors early:
// app/actions/auth.ts
import { SignupFormSchema } from '@/app/lib/definitions'
export async function signup(_, formData) {
const validated = SignupFormSchema.safeParse({
name: formData.get('name'),
email: formData.get('email'),
password: formData.get('password'),
})
if (!validated.success) {
return {
errors: validated.error.flatten().fieldErrors,
}
}
// Proceed to create user...
}Use useActionState to show errors in the form:
// app/ui/signup-form.tsx
'use client'
import { signup } from '@/app/actions/auth'
import { useActionState } from 'react'
export default function SignupForm() {
const [state, action, pending] = useActionState(signup, undefined)
return (
<form action={action}>
<input name="name" />
{state?.errors?.name && <p>{state.errors.name}</p>}
<input name="email" />
{state?.errors?.email && <p>{state.errors.email}</p>}
<input name="password" type="password" />
{state?.errors?.password && (
<ul>
{state.errors.password.map((e) => <li key={e}>{e}</li>)}
</ul>
)}
<button disabled={pending}>Sign Up</button>
</form>
)
}Hash the password and insert the user into your database:
// app/actions/auth.ts
import bcrypt from 'bcrypt'
export async function signup(_, formData) {
// Validate...
const { name, email, password } = validated.data
const hashedPassword = await bcrypt.hash(password, 10)
const user = await db.insert(users).values({
name,
email,
password: hashedPassword,
}).returning({ id: users.id })
if (!user) {
return { message: 'Error creating account.' }
}
// TODO: Create session and redirect
}Authentication in Next.js is secure and scalable with Server Actions, form validation, and session management. While custom solutions are possible, using a trusted Auth Library can simplify the process and enhance security.