비트베이크

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

2026-04-28T01:02:47.888Z

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!

Start advertising on Bitbake

Contact Us

More Articles

2026-06-04T01:04:15.823Z

The 2026 E-Commerce New Product Launch Survival Formula: Dominating Platform Search Rankings in 7 Days via Reward-Based Trials and Purchase Verification

2026-06-04T01:04:15.800Z

2026 이커머스 신제품 론칭 생존 공식: 리워드형 체험단과 구매 인증으로 7일 만에 플랫폼 검색 랭킹 장악하기

2026-06-01T01:01:58.264Z

Surviving the 2026 Cookieless Era for B2C: Building Zero-Party Data with Reward-Based Quiz Marketing

2026-06-01T01:01:58.231Z

2026 쿠키리스 시대의 B2C 생존법: 리워드 기반 퀴즈 마케팅으로 제로파티 데이터 구축하기

Services

HomeFeedFAQCustomer Service

Inquiry

Bitbake

LAEM Studio | Business Registration No.: 542-40-01042

4th Floor, 402-J270, 16 Su-ro 116beon-gil, Wabu-eup, Namyangju-si, Gyeonggi-do

TwitterInstagramNaver Blog