🎃

better-authで実現する!フレームワーク非依存の爆速認証機能実装ガイド

2025/01/27に公開

背景

Node.jsで認証機能を実装する際には、一般的にはauth.jsが利用されることが多いです。oauthのサポートはほぼ完璧ですが、2要素認証(2FA)、ユーザーネームとパスワードに基づいた認証、あるいはGoogle one tapなどを実装するときは、多くのコードを書く必要があります。

また、エンジニアにより、安全ではない認証機能を実装する可能性も高くなります。(例えば、ユーザーネームとパスワードで認証する機能を実装する際に、パスワードの暗号化と難易度確認は自分で実装する必要があります)

そこで、auth.js以外のライブラリーやサービスを探してみました。

探してみた結果、今回の記事で紹介する better-auth を見つけました。

本文の目的

  • better-authの紹介。
  • better-authを実際に使って、認証機能の実装を体験する。

注意点

  • 本文は、TypeScript経験者向けのため、コアとなるコードのみを掲載しています、自分で検証したい方はGithubに公開したリポジトリの内容をチェックしてください。
  • サンプルにNext.jsを使っていますが、better-authはフレームワークに関係なく、どのフレームワークでも使えるので、Next.jsの知識が不要。
  • Google one tap機能は本番環境が必要になります。

Next.js15とbetter-authで爆速実装

例として、ユーザーネームとパスワードでの認証、Google oauthとGoogle one tapを実装する。
では、早速、実装に入ります。

実行環境

Next.jsプロジェクトの初期化

  • 新規プロジェクト作成

    npm create next-app@latest
    
  • better-authのインストール

    npm add better-auth
    

root directoryにauth.ts作成

import { betterAuth } from "better-auth";
import { oneTap } from "better-auth/plugins";
/* 
本文はPrismaを使ってデーターベースを操作しています、Prismaを使わなくても動けます、詳細は
https://www.prisma.io/
と
https://www.better-auth.com/docs/installation
にて確認してください
*/
import { prismaAdapter } from "better-auth/adapters/prisma";
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export const auth = betterAuth({
  database: prismaAdapter(prisma, {
    provider: "postgresql",
  }),
  // ユーザーネームとパスワード認証をONにする
  emailAndPassword: {
    enabled: true,
  },
  // Google oauthを設定する
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    },
  },
  // Google one tapのプラグインをONにする
  plugins: [oneTap()],
});

server api作成

/app/api/auth/[...all]/route.ts

import { auth } from "@/auth";
// Next.js専用のHandlerを導入する
// Next.js以外の場合はhttps://www.better-auth.com/docs/integrations/nodeを参照してください
import { toNextJsHandler } from "better-auth/next-js";
export const { POST, GET } = toNextJsHandler(auth);

client api作成

/lib/auth-client.ts

import { createAuthClient } from "better-auth/react"
import { oneTapClient } from "better-auth/client/plugins"
// Client side専用のapiを作成する
export const authClient = createAuthClient({
  baseURL: NEXT_PUBLIC_APP_URL!,
  plugins: [
    oneTapClient({
        clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID!
    })
]
})

認証画面実装(ロジック部分抜粋)

/components/user-auth-form.tsx
画面実装はshadcn/uiを使用している。
詳細のコードはGithubでチェックできます。

async function onSubmit(event: React.SyntheticEvent) {
  event.preventDefault()
  setIsLoading(true)
  if (!isLogin) {
    try {
     // 新規ユーザ登録処理
      const { data, error } = await authClient.signUp.email(
        {
          email,
          password,
          name: email.split("@")[0],
        },
        {
          onRequest: () => {
            setIsLoading(true)
          },
          onSuccess: () => {
            router.push("/dashboard")
          },
          onError: ctx => {
            alert(ctx.error.message)
          },
        }
      )
    } finally {
      setIsLoading(false)
    }
  } else {
    try {
      // ユーザーネームとパスワードでログイン処理
      const { data, error } = await authClient.signIn.email(
        {
          email,
          password,
        },
        {
          onRequest: () => {
            setIsLoading(true)
          },
          onSuccess: () => {
            router.push("/dashboard")
          },
          onError: ctx => {
            alert(ctx.error.message)
          },
        }
      )
    } finally {
      setIsLoading(false)
    }
  }
}

async function handleGoogleSignIn() {
  // Google oauth
  try {
    setIsLoading(true)
    await authClient.signIn.social({
      provider: "google",
      callbackURL: "/dashboard",
    })
  } catch (error) {
    console.error(error)
    alert("Failed to sign in with Google")
  } finally {
    setIsLoading(false)
  }
}

google-one-tap.tsx

// ここのcomponentはuser-auth-form.tsxに入れてあります、画面がmountされた後に実行されます
// 一点補足ですが、誤ってgoogle one tapをキャンセルしてしまった方はchrome://settings/content/federatedIdentityApiで権限をリセットしてください
"use client"
import { useEffectOnce } from 'react-use'
import { authClient } from '@/lib/auth-client'
import { useRouter } from 'next/navigation'

export const GoogleOneTap = () => {
  const router = useRouter()
  useEffectOnce(() => {
    authClient.oneTap({
      fetchOptions: {
        onSuccess: () => {
          router.push("/dashboard")
        }
      }
    })
  })
  return null
}

これで、ユーザー認証がつかえるようになる

検証

こちらは認証画面

Google one tap認証中

認証完了後、ユーザー情報取得

最後に

better-authは、モダンなWeb開発における認証機能の実装を大幅に簡素化してくれます。特に以下の場合におすすめです:

  • 短期間で認証機能を実装したい場合
    セキュリティを重視しつつ、実装の手間を減らしたい場合
  • フレームワークに依存せず、再利用可能な認証システムを構築したい場合

今回紹介した実装例は基本的な機能のみですが、better-authはより高度な認証機能(例:PasskeySSOなど)も簡単に追加することができます。興味のある方は、公式ドキュメントや本記事のGithubリポジトリを参考し、チャレンジしてみてください。

最後まで読んでいただきありがとうございました!

参考リンク

better-auth公式サイト

Discussion