もうログインシステムを一から作るのはやめよう!
フルスタックプロジェクトでNode.jsを使って会員登録・ログインシステムを自作したことがあるなら、その苦労はよくわかるはずです。 パスワードにソルトを加えてbcryptでハッシュ化したり、JWTトークンを発行したり、トークンの有効期限やリフレッシュトークンの処理、「パスワード忘れ」時のメール送信ロジックの実装... さらに面倒なOAuth(Google/GitHubログイン)も考慮しなければなりません。 しかも一箇所でも間違えると、システムの会員データがハッカーに丸ごと盗まれる可能性があります。
2026年の現代的なフルスタック開発では、プロのエンジニアは車輪の再発明をしません。私たちはBASS(Backend as a Service)を利用します。そしてSupabaseこそが、Vibe Tutorが選び抜いた最強の脳なのです。
なぜSupabaseを選ぶのか?
多くの人がFirebaseと比較します。Firebaseも便利ですが、その基盤はNoSQLデータベースです。私たちのように「会員」と「複数の注文」を厳密に関連付ける必要があるEC/知識課金システムにとって、リレーショナルデータベース(RDBMS)の方が確実に安全な選択です。 Supabaseの基盤は世界最強のオープンソースRDBMSであるPostgreSQLです。超高速なAPIを提供するだけでなく、完璧なAuth(認証)システムも内蔵しています。
Next.js 15でのSupabase Auth実装
Next.jsのApp Routerアーキテクチャでは、環境がサーバーサイドとクライアントサイドに分離されているため、両方に対応する2種類のSupabase Clientを準備する必要があります。
これらはすべて、私たちのプロジェクトのsrc/utils/supabase/フォルダ内に設定済みです。
1. client.ts(フロントエンドブラウザ用)
これは主に"use client"があるコンポーネント(例えばログインフォームコンポーネント)で使用します。
import { createBrowserClient } from '@supabase/ssr'
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}
2. server.ts(バックエンドサーバー用)
これはServer ComponentsやAPI Routesで使用し、ユーザーの認証を行います。Cookieの読み書きを処理する必要があります。
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export async function createClient() {
const cookieStore = await cookies()
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll()
},
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) => {
cookieStore.set(name, value, options)
})
} catch (error) {
// Server Componentでは直接Cookieを設定できない場合があるため、例外を捕捉
}
},
},
}
)
}
メールマジックリンク(Magic Link)ログイン実践
Vibe Tutorのログインページ(src/app/login/page.tsx)では、現在最も流行している摩擦のないMagic Linkログイン方式を採用しています。
ユーザーはパスワードを設定・記憶する必要があ���ません。メールアドレスを入力するだけで、システムが安全なリンク付きのメールを送信し、クリックすれば即座にログインできます。
ログイン処理のコアロジックを見てみましょう:
"use client";
import { createClient } from "@/utils/supabase/client";
const handleLogin = async (e) => {
e.preventDefault();
const supabase = createClient();
// SupabaseのsignInWithOtpメソッドを呼び出してマジックリンクを送信
const { error } = await supabase.auth.signInWithOtp({
email,
options: {
// ログイン成功後、会員センターにリダイレクト
emailRedirectTo: `${location.origin}/auth/callback?next=/dashboard`,
},
});
if (error) {
alert("メール送信に失敗しました。しばらくしてから再試行してください。");
} else {
alert("マジックリンクをメールで送信しました!メールを確認してください。");
}
};
たったこれだけ!SMTPサーバーの設定など一切不要で、Supabaseが内部ですべて処理してくれます。
Authコールバック処理メカニズム
ユーザーがメール内のマジックリンク(https://あなたのサイト.com/auth/callback?code=xxxのようなURL)をクリックすると、
src/app/auth/callback/route.tsというAPI Routeでこれをインターセプトし、URLのcodeを実際のログイン認証情報(Session Cookie)と交換する必要があります。
import { NextResponse } from 'next/server'
import { createClient } from '@/utils/supabase/server'
export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url)
const code = searchParams.get('code')
const next = searchParams.get('next') ?? '/dashboard' // デフォルトは会員センターへ
if (code) {
const supabase = await createClient()
// codeをユーザーのログインセッションと交換
const { error } = await supabase.auth.exchangeCodeForSession(code)
if (!error) {
return NextResponse.redirect(`${origin}${next}`)
}
}
// 認証失敗時の処理
return NextResponse.redirect(`${origin}/login?error=InvalidToken`)
}
この交換プロセスが完了すると、ブラウザにはHttpOnly属性を持つ安全なCookieが書き込まれます。
これ以降、ユーザーがどのページを閲覧しても、サーバーサイドでawait supabase.auth.getUser()を呼び出すだけで瞬時に誰なのかを識別し、その注文権限を読み取ることができます。
強力でエレガントなSupabase認証をマスターしたところで、次はシステムの核心であるPostgreSQLデータベース設計に進み、神秘的なvt_purchases注文テーブルがどのように設計されているのかを見ていきましょう。