前回の記事では、ローカルLLM対応のAI Overview診断ツールのDB保存先を、SQLiteからSupabaseに変更しました。
これにより、診断履歴をローカルPC内の dev.db ではなく、クラウド上のSupabase PostgreSQLに保存できるようになりました。
現在の構成は、次の流れとなっています。
AI Overview診断ツール
↓
Next.js API Route
↓
Supabase REST API
↓
Supabase PostgreSQL前回の実装では、Prismaを使わず、Next.jsのAPI RouteからSupabase REST APIへ直接リクエストして保存する構成にしています。
ただし、現時点ではログイン機能がないため、診断履歴は「誰の履歴か」を区別できません。
そこで今回は、Supabase Authを使ってユーザー認証を追加し、ログインしたユーザーごとに診断履歴を保存できるようにします。
Supabase Authとは?
Supabase Authは、Supabaseが提供している認証機能です。
簡単にいうと、アプリにログイン・新規登録・ログアウトなどの機能を追加できる仕組みです。
Supabase Authはユーザーの作成・管理に使えるSDKやAPIエンドポイントを提供し、パスワード、Magic Link、OTP、ソーシャルログイン、SSOなどに対応しています。
具体的には、Supabase Authでは、次のような認証方法を利用できます。
| 認証方法 | 内容 |
|---|---|
| メールアドレス + パスワード | 一般的なログイン方式 |
| Magic Link | メールに届くリンクでログイン |
| Phone Auth (OTP) | SMSを使用した電話番号認証 |
| Social Login | Google, GitHub, Facebook, Appleなどでログイン |
| SAML 2.0(SSO) | 企業向けのシングルサインオン |
| Web3ウォレット認証 | MetaMaskやPhantomといった暗号資産ウォレットを使う |
今回のAI Overview診断ツールでは、まず一番分かりやすい メールアドレス + パスワード認証
Supabaseの認証機能
現在の診断履歴保存では、Supabaseの diagnosis_histories テーブルに診断結果を保存できます。
ただし、ログイン機能がない場合、次のような問題があります。
| 課題 | 内容 |
|---|---|
| 誰の履歴か分からない | 複数ユーザーで使うと履歴が混ざる |
| 自分の履歴だけ表示できない | 一覧表示時に全ユーザーのデータが対象になる |
| SaaS化しにくい | ユーザー単位の管理ができない |
| セキュリティ面で弱い | 他人の履歴が見える設計になりやすい |
そこで、ログインしたユーザーのIDを user_id として診断履歴に保存します。
現状でも、Supabase側のテーブルには user_id カラムを用意しており、ログイン機能のために利用できるようになっています。
今回の実装では、この user_id にSupabase AuthのユーザーIDを入れるようにしています。
メールアドレス + パスワード認証の基本フロー
ユーザーがアプリにログインする際の動きは、大きく分けて以下のステップで行われます。
1. サインアップ(ユーザー登録)
- リクエスト:
ユーザーがメールアドレスとパスワードを入力して送信。 - ハッシュ化:
Supabaseがパスワードを暗号化(ハッシュ化)します。 - 保存:
管理用テーブルに保存されます。 - 確認メール:
設定が有効であれば、Supabaseから「本人確認メール」が自動送信されます。
ユーザーがリンクをクリックするまで、アカウントは「保留(unconfirmed)」状態になります。
2. サインイン(ログイン)
- 照合:
送信されたパスワードと、保存されているハッシュ化された値を照合します。 - JWT(トークン)の発行:
認証が成功すると、SupabaseはJWT(JSON Web Token)を発行します。- Access Token:
データベースへのアクセス許可証(短期間で失効)。 - Refresh Token:
期限が切れたときに新しいアクセス券をもらうための引換券(長期間有効)。
- Access Token:
- セッション保持:
ブラウザのlocalStorageやCookieにこれらのトークンが自動的に保存されます。
セキュリティの仕組み:RLSとの連携
Supabaseの認証が便利なのは、データベースのRow Level Security (RLS) との連携です。
ログインが完了すると、それ以降のデータベースへのリクエストには自動的に「JWT(アクセス権)」が添付されます。
PostgreSQL側では auth.uid() という関数を使って、参照するデータが、ログインしているユーザーのものかを一行ごとに判定できます。
AI Overview診断ツールユーザー認証追加作業

今回のユーザー認証追加の流れとは次の通りです。
- Supabase Authを使えるようにする
↓ - アプリにログイン画面を追加する
↓ - ログイン後だけ診断画面を表示する
↓ - 診断APIにログイン確認を追加する
↓ - 診断履歴を user_id 付きで保存する
↓ - 自分の履歴だけ取得する
↓ - 全体ランキングは公開許可されたURLだけ表示する
診断認証管理テーブルの変更
SQL Editorで、診断認証管理テーブル(RLS付)に変更する。
主な変更内容
1. 診断履歴テーブルを作る
2. 既存テーブルにも不足カラムを追加する
3. 検索を速くするインデックスを作る
4. RLSを有効にする
5. 本人だけ読み書きできるポリシーを作る
supabase/schema.sql
create table if not exists diagnosis_histories (
id bigint generated by default as identity primary key,
user_id uuid references auth.users(id) on delete cascade,
created_at timestamptz not null default now(),
input_preview text not null,
source_url text,
total_score integer not null check (total_score >= 0 and total_score <= 100),
summary text not null,
is_public boolean not null default false
);
alter table diagnosis_histories
add column if not exists user_id uuid references auth.users(id) on delete cascade;
alter table diagnosis_histories
add column if not exists source_url text;
alter table diagnosis_histories
add column if not exists is_public boolean not null default false;
comment on column diagnosis_histories.user_id is
'Supabase AuthのユーザーID。SaaS化に備えて診断履歴をユーザーごとに分離する。';
comment on column diagnosis_histories.is_public is
'trueの場合のみ、全体の高スコアサイト5選にURL・スコア・日時を表示する。本文プレビューやサマリーは公開しない。';
create index if not exists diagnosis_histories_user_created_at_idx
on diagnosis_histories (user_id, created_at desc);
create index if not exists diagnosis_histories_public_score_idx
on diagnosis_histories (is_public, total_score desc, created_at desc)
where source_url is not null;
alter table diagnosis_histories enable row level security;
drop policy if exists "Users can read own diagnosis histories" on diagnosis_histories;
drop policy if exists "Users can insert own diagnosis histories" on diagnosis_histories;
drop policy if exists "Users can update own diagnosis histories" on diagnosis_histories;
drop policy if exists "Users can delete own diagnosis histories" on diagnosis_histories;
drop policy if exists "Anyone can read public diagnosis ranking rows" on diagnosis_histories;
create policy "Users can read own diagnosis histories"
on diagnosis_histories
for select
to authenticated
using (auth.uid() = user_id);
create policy "Users can insert own diagnosis histories"
on diagnosis_histories
for insert
to authenticated
with check (auth.uid() = user_id);
create policy "Users can update own diagnosis histories"
on diagnosis_histories
for update
to authenticated
using (auth.uid() = user_id)
with check (auth.uid() = user_id);
create policy "Users can delete own diagnosis histories"
on diagnosis_histories
for delete
to authenticated
using (auth.uid() = user_id);
-- 注意:
-- このアプリはNext.js API Route経由で全体ランキングを取得し、
-- ブラウザにはURL・スコア・日時だけを返します。
-- 直接Supabaseクライアントから全体公開行を読む設計にする場合は、
-- 本文プレビューやサマリーを含まない公開用ビューを別途作る方が安全です。
Supabase側で Email ログインを有効化

登録確認メールの内容は、Authentication->Email->Confirm sign upから編集できます。
アプリ側では .env.local に以下を追加します。
NEXT_PUBLIC_SUPABASE_ANON_KEY=Supabaseのanon public keyログイン画面を追加
app/page.tsx にログイン / アカウント作成フォームを追加しました。
未ログインの場合は、診断画面ではなくログイン画面を表示します。
未ログイン
→ ログイン画面
ログイン済み
→ 診断画面
ログイン情報を保存
ログインに成功すると、Supabaseからアクセストークンが返ります。
そのトークンをブラウザ側に保存し、診断APIや履歴APIを呼ぶときに Authorization: Bearer <アクセストークン> ヘッダーに付けて送ります。
ユーザー認証の仕組みの流れ
① クライアントがログイン(ID/パスワード送信)
↓
② サーバーがアクセストークンを発行・返却
↓
③ 以降のリクエストは Authorization: Bearer <トークン> を付けて送信
↓
④ サーバーがトークンを検証し、正当なら処理を許可API側でログイン確認
追加したlib/supabaseAuth.tsで、ブラウザから送られてきたアクセストークンをSupabase Authに確認します。
Next.js API
→ Supabase Authにトークン確認
→ 正しいユーザーなら処理続行
→ 不正ならエラー
診断実行をログイン必須化
追加したapp/api/analyze/route.tsにより、未ログイン状態では診断実行できないようにしました。
診断履歴をユーザーごとに保存
app/api/history/route.ts で、ログインユーザーのIDを取得し、履歴保存時に user_id として保存します。
diagnosis_histories
- user_id
- created_at
- input_preview
- source_url
- total_score
- summary
- is_public
これにより、誰の診断履歴か分かるようになります。
自分の履歴だけ表示
履歴取得時は、ログイン中の user_id で絞り込みます。
そのため、他のユーザーの履歴は表示されません。
高スコアサイト5選を切り替え

高スコアサイト5選は2種類にしました。
自分だけ
→ 自分の診断履歴から高スコア順に表示
全体
→ 公開許可されたURL診断だけを高スコア順に表示
全体ランキングは、ユーザーがURL診断時に、「全体の高スコアサイト5選に含める」をオンにしたものだけが対象です。
Supabase側のRLS設定
Supabaseでは、データベースの行単位でアクセス制御を行うRLSを使えます。
今回の作業では、supabase/schema.sql にRLS設定を追加しました。
基本方針は以下です。
本人の履歴だけ読める
本人の履歴だけ保存できる
本人の履歴だけ更新・削除できる
これにより、将来的にSaaS化してもユーザーごとのデータ分離を維持しやすくなります。
今回の変更まとめ
次の様に、SaaS化しやすい土台に変えた変更となっています。
ログイン
→ user_id付きで保存
→ 自分の履歴だけ表示
→ 公開許可されたURLだけ全体ランキング表示
まとめ
Supabase Authを使って、AI Overview診断ツールにログイン機能を追加しました。
これに伴いユーザー毎の診断履歴管理が出来るようになったため、高スコア5選の診断履歴表示方法を、自分だけと全体の2パターンで表示できるように改善しました。
関連記事



