開発・個人開発

Supabase Authでメール認証ログインを実装する 管理ページの保護に使える

Supabase Authでメール認証ログインを実装する 管理ページの保護に使える

この記事の要点

Supabase Authのメール+パスワード認証を使って、個人開発の管理ページを保護する実装方法を解説する。ログインフォーム・セッション管理・保護ページの作り方をAstroでのコード例とともに示す。

結論

Supabase Authのメール認証は、signInWithPassword()getSession()の2つの関数を覚えるだけで基本的な実装が完成する。管理ページをシンプルなパスワード保護にしたいなら、環境変数とBasic認証で十分なケースもあるが、Supabase Authを使うと将来的な機能拡張(複数管理者・ロール管理等)が容易になる。

Supabase Authの設定

1. Authプロバイダーの確認

Supabaseダッシュボード → Authentication → Providers で「Email」が有効になっていることを確認する(デフォルトで有効)。

2. メール確認の設定(管理者用途は無効推奨)

Authentication → Email Templates でメール確認の設定が確認できる。管理者1人のみが使うサイトでは、新規登録時のメール確認を無効にすると手間が減る。

Authentication → Settings → 「Confirm email」をオフにすると、登録直後から使える状態になる。

3. 管理者アカウントの作成

ダッシュボード → Authentication → Users → 「Invite user」または「Add user」で管理者アカウントを直接作成できる。

SupabaseクライアントとAuthの初期化

src/lib/supabase.tsでクライアントを設定する。

import { createClient } from '@supabase/supabase-js';

const supabaseUrl = import.meta.env.PUBLIC_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.PUBLIC_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
  auth: {
    // セッションをlocalStorageに保持(ブラウザリロード後も維持)
    persistSession: true,
    autoRefreshToken: true,
  },
});

ログインフォームの実装

src/pages/login.astroを作成する。

---
// ビルド時のサーバーサイドでは認証できないためフロントエンドで処理
---

<html lang="ja">
<body>
  <form id="login-form">
    <h1>管理者ログイン</h1>
    <div>
      <label>メールアドレス</label>
      <input type="email" id="email" required />
    </div>
    <div>
      <label>パスワード</label>
      <input type="password" id="password" required />
    </div>
    <button type="submit">ログイン</button>
    <p id="error-msg" hidden style="color: red;"></p>
  </form>

  <script>
    import { supabase } from '../lib/supabase';
    
    // すでにログイン済みなら管理画面にリダイレクト
    const { data: { session } } = await supabase.auth.getSession();
    if (session) window.location.href = '/admin/';
    
    document.getElementById('login-form').addEventListener('submit', async (e) => {
      e.preventDefault();
      const email = document.getElementById('email').value;
      const password = document.getElementById('password').value;
      
      const { error } = await supabase.auth.signInWithPassword({ email, password });
      
      if (error) {
        const msg = document.getElementById('error-msg');
        msg.textContent = 'ログインに失敗しました。メールアドレスとパスワードを確認してください。';
        msg.hidden = false;
      } else {
        window.location.href = '/admin/';
      }
    });
  </script>
</body>
</html>

保護ページの実装

管理ページに認証チェックを追加する。src/pages/admin/index.astroを作成する。

---
// このページは静的生成(サーバーサイドではセッション確認できない)
---

<html lang="ja">
<body>
  <!-- ログインしていない場合は何も表示しない(JSが確認後に表示) -->
  <div id="admin-content" hidden>
    <h1>管理ダッシュボード</h1>
    <!-- 管理コンテンツ -->
  </div>

  <script>
    import { supabase } from '../../lib/supabase';
    
    // セッション確認
    const { data: { session } } = await supabase.auth.getSession();
    
    if (!session) {
      // 未認証ならログインページにリダイレクト
      window.location.href = '/login';
    } else {
      // 認証済みならコンテンツを表示
      document.getElementById('admin-content').hidden = false;
    }
  </script>
</body>
</html>

注意点:静的生成されたページでは、ページのHTMLが一瞬表示される可能性がある。重要な機密コンテンツは、JavaScriptでセッション確認後に動的にロードする設計にすることを推奨する。

ログアウトの実装

import { supabase } from '../lib/supabase';

async function logout() {
  await supabase.auth.signOut();
  window.location.href = '/login';
}

document.getElementById('logout-btn').addEventListener('click', logout);

セッションの確認とユーザー情報の取得

// 現在のセッションを取得
const { data: { session } } = await supabase.auth.getSession();

// セッションがある場合はユーザー情報を取得
if (session) {
  const user = session.user;
  console.log('ログイン中のユーザー:', user.email);
  console.log('ユーザーID:', user.id);
}

// セッションの変化を監視(ログイン・ログアウトの検知)
supabase.auth.onAuthStateChange((event, session) => {
  if (event === 'SIGNED_OUT') {
    window.location.href = '/login';
  }
});

AstroのSSRモードでのより確実な保護

AstroをSSRモード(output: 'server')で動かすと、ページのリクエスト時にサーバーサイドでセッションを確認できる。静的サイトよりも安全に保護ページを作れる。

---
// SSRモードの場合、サーバーサイドでセッション確認が可能
import { createServerClient } from '@supabase/ssr';

const supabase = createServerClient(
  import.meta.env.SUPABASE_URL,
  import.meta.env.SUPABASE_ANON_KEY,
  {
    cookies: {
      get: (key) => Astro.cookies.get(key)?.value,
      set: (key, value, options) => Astro.cookies.set(key, value, options),
      remove: (key) => Astro.cookies.delete(key),
    },
  }
);

const { data: { session } } = await supabase.auth.getSession();

if (!session) {
  return Astro.redirect('/login');
}
---

<h1>管理画面(認証済みのみ表示)</h1>

SSRモードではHTMLが生成される前にリダイレクトできるため、コンテンツが一瞬見える問題がなくなる。

Supabaseの基本セットアップはSupabaseの始め方を、RLSの設定はSupabaseの行レベルセキュリティ(RLS)入門で解説している。

まとめ

Supabase Authのメール認証は、signInWithPassword()でログインしてgetSession()でセッション確認する、この2つが基本だ。静的生成のAstroサイトでは、JavaScriptでセッション確認後にコンテンツを表示するパターンで管理ページを保護できる。より確実な保護が必要な場合はSSRモードへの切り替えを検討する。

よくある質問

Supabase Authは無料で使えますか

Freeプランで月5万人のMAU(月間アクティブユーザー)まで無料です。個人開発の管理ページなら自分1人しか使わないため、無料枠で十分です。

Supabase AuthはSNSログイン(GoogleやGitHub)にも対応していますか

対応しています。Google・GitHub・Twitter・Facebook・Apple等のOAuthプロバイダーを設定できます。各プロバイダーでOAuthアプリを作成してクライアントIDとシークレットをSupabaseに設定するだけで使えます。

Astroの静的サイトでログイン状態の保護はどう実装しますか

AstroのサーバーサイドレンダリングまたはクライアントサイドのJavaScriptでセッションを確認して、未認証ならログインページにリダイレクトする方法で実装します。静的ページの場合、表示した後にJavaScriptでセッション確認してリダイレクトします。

パスワードを忘れたときのリセット機能も実装できますか

Supabase Authは標準でパスワードリセット機能を持っています。resetPasswordForEmail()を呼び出すと、リセットメールが自動送信されます。Resendとの連携でカスタムドメインからメールを送ることもできます。