[Auth.js v5] Implementing SMS OTP Authentication in Next.js in 5 Minutes (Zero Paperwork)

2026년 4월 28일5분 소요

developer authentication modern

Must SMS Authentication Be This Complicated?

If you are developing a side project or a startup MVP, you will eventually hit a wall where you need "Phone Number Verification." However, integrating standard telecom APIs or major platforms is often a nightmare of administrative red tape rather than actual development. You are typically asked to:

  • Submit a business registration certificate (What if you're a student or solo dev?).
  • Pre-register a verified sender ID.
  • Wait 3 to 5 business days for approval.

This breaks a developer's flow. Today, we will look at how to implement SMS OTP authentication in just 5 minutes using the recently stabilized Auth.js v5 (formerly NextAuth) in a Next.js App Router environment, powered by EasyAuth—an API that requires zero paperwork.


Why Auth.js v5 + EasyAuth?

  1. Auth.js v5: Fully compatible with the modern Next.js App Router and Edge environments. By using the Credentials provider, integrating custom OTP flows becomes incredibly easy.
  2. EasyAuth:
    • Zero Paperwork: No business registration or usage certificates required.
    • Instant Setup: Start sending messages within 5 minutes of signing up (10 free trials included).
    • Automatic Sender ID: Send SMS immediately without the hassle of pre-registering a caller ID.
    • Simple API Structure: Just two endpoints—/send and /verify.

Step 1: Configuring Auth.js v5

First, install the latest beta version of Auth.js in your Next.js project.

npm install next-auth@beta

Create an auth.ts file in the root of your project (or inside src). Here, we will link EasyAuth's /verify API inside the authorize callback.

import NextAuth from "next-auth";
import Credentials from "next-auth/providers/credentials";

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    Credentials({
      name: "SMS OTP",
      credentials: {
        phone: { label: "Phone Number", type: "text" },
        code: { label: "OTP Code", type: "text" },
      },
      async authorize(credentials) {
        if (!credentials?.phone || !credentials?.code) return null;

        // Verify the OTP via EasyAuth API
        const response = await fetch("https://api.easyauth.kr/verify", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "Authorization": `Bearer ${process.env.EASYAUTH_API_KEY}`
          },
          body: JSON.stringify({
            phone: credentials.phone,
            code: credentials.code
          })
        });

        const data = await response.json();

        // Return user session object if verification succeeds
        if (response.ok && data.success) {
          return { id: String(credentials.phone), name: String(credentials.phone) };
        }

        return null; // Return null on failure
      }
    })
  ],
  pages: {
    signIn: "/login", // Custom login page route
  }
});

Step 2: Creating the OTP Send API (Route Handler)

Exposing API keys directly on the client side is a major security risk. Let's create a Next.js Route Handler to call EasyAuth's /send endpoint securely from the server.

Create a file at app/api/send-otp/route.ts.

import { NextResponse } from "next/server";

export async function POST(req: Request) {
  try {
    const { phone } = await req.json();

    // Send OTP via EasyAuth API
    const response = await fetch("https://api.easyauth.kr/send", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${process.env.EASYAUTH_API_KEY}`
      },
      body: JSON.stringify({ phone })
    });

    if (!response.ok) throw new Error("Failed to send");

    return NextResponse.json({ success: true, message: "OTP sent" });
  } catch (error) {
    return NextResponse.json(
      { success: false, error: "Internal Server Error" }, 
      { status: 500 }
    );
  }
}

Step 3: Implementing the Client Login Form

Finally, build the UI where users can input their phone numbers, request a code, and submit it to log in.

app/login/page.tsx

"use client";

import { useState } from "react";
import { signIn } from "next-auth/react";

export default function LoginPage() {
  const [phone, setPhone] = useState("");
  const [code, setCode] = useState("");
  const [step, setStep] = useState<1 | 2>(1);

  // 1. Request OTP
  const handleSendCode = async () => {
    const res = await fetch("/api/send-otp", {
      method: "POST",
      body: JSON.stringify({ phone }),
    });
    
    if (res.ok) {
      alert("OTP has been sent!");
      setStep(2);
    } else {
      alert("Failed to send OTP. Please try again.");
    }
  };

  // 2. Verify OTP & Process NextAuth Login
  const handleVerifyCode = async () => {
    const result = await signIn("credentials", {
      phone,
      code,
      redirect: true,
      redirectTo: "/dashboard", // Route to redirect after successful login
    });
  };

  return (
    <div>
      <h1>SMS Authentication</h1>
      
      {step === 1 ? (
        <div>
           setPhone(e.target.value)} 
            placeholder="Phone Number (e.g. 01012345678)" 
            className="border p-3 rounded"
          /&gt;
          
            Send Code
          
        </div>
      ) : (
        <div>
           setCode(e.target.value)} 
            placeholder="6-digit OTP" 
            className="border p-3 rounded"
          /&gt;
          
            Verify &amp; Login
          
        </div>
      )}
    </div>
  );
}

💡 Tips & Best Practices

  • Strict Environment Variables: Make sure EASYAUTH_API_KEY is strictly kept in .env.local for server-side use only. Never add the NEXT_PUBLIC_ prefix, as it will leak your key to the browser!
  • Robust Error Handling: In a production environment, add regex validation for phone numbers and provide clear feedback if the OTP is incorrect or expired.

Conclusion: The Easiest Way to Authenticate

We’ve explored how to cleanly implement an SMS OTP system using Next.js App Router and Auth.js v5.

While traditional methods would leave you stuck in administrative limbo for days just waiting for approvals, EasyAuth empowers you to bypass all the paperwork and finish your core business logic in just 5 minutes. If you are a solo developer, freelancer, or startup rushing to launch an MVP without the enterprise hassle, sign up for EasyAuth today and test the code above with your 10 free credits!

SMS 인증을 쉽게 시작하세요

서류 없이 가입 즉시 API Key를 발급받고 바로 시작할 수 있습니다.
건당 25원, 가입 시 10건 무료!