記事一覧に戻る

Supabase.auth完全ガイド:認証メソッドの使い方と実践的な活用法

12分で読めます
Supabase認証JavaScriptTypeScriptバックエンド

Supabase.auth完全ガイド:認証メソッドの使い方と実践的な活用法

Supabase.authとは

Supabase.authは、Supabaseが提供する認証システムの中核となるJavaScriptライブラリです。このライブラリを使用することで、ユーザー登録、ログイン、セッション管理、パスワードリセットなど、Webアプリケーションに必要な認証機能を簡単に実装できます。

なぜSupabase.authが重要なのか

現代のWebアプリケーション開発において、認証システムは最も重要な機能の一つです。しかし、セキュリティ要件を満たしながら認証システムを一から構築するには、多大な時間と専門知識が必要です。Supabase.authは、これらの課題を解決し、開発者が迅速かつ安全に認証機能を実装できるソリューションを提供します。

Supabase.authの主な特徴は以下の通りです:

  • 包括的な認証機能:メールアドレス、パスワード、OAuth、マジックリンクなど、様々な認証方式をサポート
  • セキュリティ重視:業界標準のセキュリティプラクティスに準拠
  • 簡単な統合:シンプルなAPIで既存のアプリケーションに簡単に統合
  • リアルタイム機能:セッション状態の変更をリアルタイムで監視
  • 豊富なカスタマイズ:企業のニーズに合わせた柔軟な設定

基本的なセットアップ

Supabase.authを使用するためには、まず環境のセットアップが必要です。以下は基本的なセットアップ手順です。

インストール

bash
npm install @supabase/supabase-js

このコマンドにより、Supabase JavaScriptクライアントがプロジェクトにインストールされます。@supabase/supabase-jsパッケージには、認証機能だけでなく、データベース操作やリアルタイム機能も含まれています。

初期化

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

const supabaseUrl = 'https://your-project.supabase.co'
const supabaseAnonKey = 'your-anon-key'

const supabase = createClient(supabaseUrl, supabaseAnonKey)

この初期化コードでは、SupabaseプロジェクトのURLと匿名キーを使用してクライアントを作成しています。supabaseUrlはプロジェクト固有のエンドポイントを指し、supabaseAnonKeyはクライアントサイドでの安全な操作を可能にする公開キーです。

匿名キーは、その名前にもかかわらず、適切なRow Level Security(RLS)ポリシーが設定されている限り、安全にクライアントサイドで使用できます。このキーを使用することで、認証されていないユーザーでもログインやユーザー登録などの基本的な認証操作を実行できます。

認証メソッドの詳細解説

ユーザー登録メソッド

signUp()

signUp()メソッドは、新しいユーザーをアプリケーションに登録するための基本的な方法です。このメソッドは、メールアドレスとパスワードの組み合わせ、または外部プロバイダーを使用してユーザーを作成できます。

javascript
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password'
})

この例では、メールアドレスとパスワードを使用してユーザーを登録しています。signUp()メソッドは非同期関数であり、Promise を返します。戻り値はdataerrorの2つのプロパティを持つオブジェクトです。

dataオブジェクトには、成功時に以下の情報が含まれます:

  • user: 作成されたユーザー情報(ID、メールアドレス、作成日時など)
  • session: 認証セッション情報(アクセストークン、リフレッシュトークンなど)

メールアドレス確認が有効な場合、ユーザーは登録後に確認メールを受け取ります。この確認プロセスが完了するまで、ユーザーのアカウントは未確認状態となります。

追加情報付きの登録

javascript
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password',
  options: {
    data: {
      firstName: '太郎',
      lastName: '田中',
      age: 30
    }
  }
})

この例では、options.dataプロパティを使用して、ユーザーの追加情報を登録時に保存しています。これらの情報はauth.usersテーブルのraw_user_meta_dataカラムに保存され、後でユーザープロフィールとして活用できます。

追加情報の設定により、ユーザー体験の向上が期待できます。例えば、ユーザーが初回ログイン時に基本情報を再入力する必要がなくなり、よりスムーズなオンボーディング体験を提供できます。

ログインメソッド

signInWithPassword()

signInWithPassword()メソッドは、メールアドレスとパスワードを使用してユーザーを認証するための標準的な方法です。

javascript
const { data, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'secure-password'
})

このメソッドは、提供されたメールアドレスとパスワードを検証し、正しい場合は認証セッションを作成します。戻り値のdataオブジェクトには、認証されたユーザー情報とセッション情報が含まれます。

ログインが成功すると、Supabaseは自動的にJWTトークンを発行し、ローカルストレージに保存します。このトークンは、その後のAPI呼び出しで自動的に使用され、ユーザーの認証状態を維持します。

電話番号ログイン

javascript
const { data, error } = await supabase.auth.signInWithPassword({
  phone: '+81901234567',
  password: 'secure-password'
})

Supabaseは電話番号を使用したログインもサポートしています。この機能を使用するには、Supabaseプロジェクトの設定で電話番号認証を有効にする必要があります。電話番号は国際フォーマット(+81で始まる日本の番号など)で指定する必要があります。

OAuth認証メソッド

signInWithOAuth()

OAuth認証は、Google、GitHub、Facebook、Twitterなどの外部プロバイダーを使用してユーザーを認証する方法です。

javascript
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: {
    redirectTo: 'https://your-app.com/auth/callback'
  }
})

この例では、Googleアカウントを使用してユーザーを認証しています。providerパラメータには、使用したいOAuthプロバイダーの名前を指定します。options.redirectToは、認証完了後にユーザーがリダイレクトされるURLを指定します。

OAuth認証の流れは以下の通りです:

  1. ユーザーがsignInWithOAuth()を呼び出す
  2. ユーザーがOAuthプロバイダーのページにリダイレクトされる
  3. ユーザーがプロバイダーでアカウントを認証する
  4. プロバイダーがユーザーを指定されたリダイレクトURLに送り返す
  5. Supabaseが認証情報を処理し、セッションを作成する

複数のプロバイダーサポート

javascript
const signInWithProvider = async (providerName) => {
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider: providerName,
    options: {
      redirectTo: window.location.origin + '/dashboard'
    }
  })
}

// 使用例
await signInWithProvider('github')
await signInWithProvider('discord')
await signInWithProvider('spotify')

この実装では、複数のOAuthプロバイダーに対応した汎用的なログイン関数を作成しています。window.location.originを使用することで、現在のドメインに基づいた動的なリダイレクトURLを生成できます。

マジックリンク認証

signInWithOtp()

マジックリンク認証は、パスワードを使用せずにメールアドレスのみでユーザーを認証する方法です。

javascript
const { data, error } = await supabase.auth.signInWithOtp({
  email: 'user@example.com',
  options: {
    emailRedirectTo: 'https://your-app.com/auth/callback'
  }
})

この例では、指定されたメールアドレスに認証用のマジックリンクが送信されます。ユーザーがそのリンクをクリックすると、自動的にログインされ、指定されたURLにリダイレクトされます。

マジックリンク認証の利点は以下の通りです:

  • パスワード不要:ユーザーはパスワードを覚える必要がない
  • 高いセキュリティ:メールアドレスへのアクセスが必要
  • ユーザビリティ:複雑なパスワード要件を満たす必要がない

電話番号でのOTP認証

javascript
const { data, error } = await supabase.auth.signInWithOtp({
  phone: '+81901234567',
  options: {
    channel: 'sms'
  }
})

この例では、電話番号にSMSでワンタイムパスコード(OTP)を送信しています。ユーザーは受信したOTPを入力してログインを完了します。

セッション管理メソッド

セッション取得メソッド

getSession()

getSession()メソッドは、現在のユーザーセッションを取得するために使用します。

javascript
const { data: { session }, error } = await supabase.auth.getSession()

if (session) {
  console.log('ユーザーはログインしています:', session.user.email)
  console.log('セッション有効期限:', new Date(session.expires_at * 1000))
} else {
  console.log('ユーザーはログインしていません')
}

この例では、現在のセッション情報を取得し、ユーザーのログイン状態を確認しています。セッションが存在する場合、ユーザー情報と有効期限にアクセスできます。

getSession()メソッドは、アプリケーションの初期化時やページ読み込み時に使用することが一般的です。これにより、ユーザーの認証状態を正確に把握し、適切なUIを表示できます。

セッション情報には以下のような重要な情報が含まれます:

  • access_token: API呼び出しで使用されるJWTトークン
  • refresh_token: アクセストークンの更新に使用されるトークン
  • expires_at: セッションの有効期限(Unix タイムスタンプ)
  • user: ユーザーの詳細情報

getUser()

javascript
const { data: { user }, error } = await supabase.auth.getUser()

if (user) {
  console.log('ユーザーID:', user.id)
  console.log('メールアドレス:', user.email)
  console.log('最終ログイン:', user.last_sign_in_at)
}

getUser()メソッドは、現在認証されているユーザーの情報を取得します。このメソッドは、セッション情報よりもユーザー固有の情報に焦点を当てています。

セッション更新メソッド

refreshSession()

セッションの自動更新は通常Supabaseが処理しますが、手動でセッションを更新したい場合はrefreshSession()メソッドを使用します。

javascript
const { data, error } = await supabase.auth.refreshSession()

if (data.session) {
  console.log('セッションが更新されました')
  console.log('新しい有効期限:', new Date(data.session.expires_at * 1000))
}

この例では、現在のリフレッシュトークンを使用してセッションを更新しています。更新が成功すると、新しいアクセストークンと更新された有効期限が返されます。

セッションの手動更新は、以下のような場面で有用です:

  • 長時間のバックグラウンド処理の前
  • ユーザーが重要な操作を実行する前
  • セッションの有効期限が近い場合の事前更新

セッション状態監視

onAuthStateChange()

javascript
const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => {
  switch (event) {
    case 'SIGNED_IN':
      console.log('ユーザーがログインしました:', session.user.email)
      // ダッシュボードにリダイレクト
      window.location.href = '/dashboard'
      break
    case 'SIGNED_OUT':
      console.log('ユーザーがログアウトしました')
      // ホームページにリダイレクト
      window.location.href = '/'
      break
    case 'TOKEN_REFRESHED':
      console.log('トークンが更新されました')
      break
    case 'USER_UPDATED':
      console.log('ユーザー情報が更新されました')
      break
  }
})

// 監視を停止したい場合
subscription.unsubscribe()

この例では、認証状態の変化をリアルタイムで監視しています。onAuthStateChange()メソッドは、認証イベントが発生するたびにコールバック関数を呼び出します。

監視可能なイベントには以下があります:

  • SIGNED_IN: ユーザーがログインした
  • SIGNED_OUT: ユーザーがログアウトした
  • TOKEN_REFRESHED: アクセストークンが更新された
  • USER_UPDATED: ユーザー情報が更新された

ユーザー管理メソッド

ログアウトメソッド

signOut()

signOut()メソッドは、現在のユーザーセッションを終了し、ユーザーをログアウトさせます。

javascript
const { error } = await supabase.auth.signOut()

if (error) {
  console.error('ログアウトエラー:', error.message)
} else {
  console.log('ログアウトが完了しました')
  // ホームページにリダイレクト
  window.location.href = '/'
}

この例では、ユーザーをログアウトさせ、成功した場合はホームページにリダイレクトしています。ログアウト処理により、ローカルストレージからセッション情報が削除され、サーバー側のセッションも無効化されます。

全デバイスからのログアウト

javascript
const { error } = await supabase.auth.signOut({ scope: 'global' })

scope: 'global'オプションを使用することで、ユーザーの全デバイスからセッションを無効化できます。これは、セキュリティが重要な場面(パスワード変更後など)で有用です。

パスワード管理メソッド

resetPasswordForEmail()

javascript
const { error } = await supabase.auth.resetPasswordForEmail(
  'user@example.com',
  {
    redirectTo: 'https://your-app.com/reset-password'
  }
)

if (error) {
  console.error('パスワードリセットエラー:', error.message)
} else {
  console.log('パスワードリセットメールを送信しました')
}

この例では、指定されたメールアドレスにパスワードリセット用のリンクを送信しています。ユーザーがリンクをクリックすると、指定されたURLにリダイレクトされ、新しいパスワードを設定できます。

パスワードリセット機能の実装では、以下の点に注意が必要です:

  • リセットリンクの有効期限は通常1時間です
  • リンクは一度しか使用できません
  • ユーザーがリンクをクリックした後、新しいパスワードを設定するUIを提供する必要があります

updateUser()

javascript
const { data, error } = await supabase.auth.updateUser({
  password: 'new-secure-password'
})

if (error) {
  console.error('パスワード更新エラー:', error.message)
} else {
  console.log('パスワードが更新されました')
}

この例では、現在ログイン中のユーザーのパスワードを更新しています。updateUser()メソッドは、パスワードだけでなく、メールアドレスやユーザーメタデータも更新できます。

ユーザー情報更新メソッド

メールアドレスの更新

javascript
const { error } = await supabase.auth.updateUser({
  email: 'newemail@example.com'
})

if (error) {
  console.error('メール更新エラー:', error.message)
} else {
  console.log('確認メールを送信しました')
}

メールアドレスの更新では、新しいメールアドレスに確認メールが送信されます。ユーザーが確認リンクをクリックするまで、変更は完了しません。

ユーザーメタデータの更新

javascript
const { data, error } = await supabase.auth.updateUser({
  data: {
    firstName: '次郎',
    lastName: '佐藤',
    preferences: {
      theme: 'dark',
      language: 'ja'
    }
  }
})

この例では、ユーザーの追加情報(メタデータ)を更新しています。メタデータは、ユーザープロフィール、設定、カスタマイズ情報などを保存するために使用されます。

実践的な使用例とベストプラクティス

完全な認証フロー実装

以下は、React アプリケーションでの完全な認証フロー実装例です:

javascript
import { useState, useEffect } from 'react'
import { supabase } from './supabaseClient'

const AuthComponent = () => {
  const [user, setUser] = useState(null)
  const [loading, setLoading] = useState(true)
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')

  useEffect(() => {
    // 初期セッション取得
    const getSession = async () => {
      const { data: { session } } = await supabase.auth.getSession()
      setUser(session?.user ?? null)
      setLoading(false)
    }

    getSession()

    // 認証状態の変化を監視
    const { data: { subscription } } = supabase.auth.onAuthStateChange(
      (event, session) => {
        setUser(session?.user ?? null)
        setLoading(false)
      }
    )

    return () => subscription.unsubscribe()
  }, [])

  const handleSignUp = async (e) => {
    e.preventDefault()
    const { error } = await supabase.auth.signUp({
      email,
      password,
    })
    if (error) alert(error.message)
  }

  const handleSignIn = async (e) => {
    e.preventDefault()
    const { error } = await supabase.auth.signInWithPassword({
      email,
      password,
    })
    if (error) alert(error.message)
  }

  const handleSignOut = async () => {
    const { error } = await supabase.auth.signOut()
    if (error) alert(error.message)
  }

  if (loading) return <div>読み込み中...</div>

  return (
    <div>
      {user ? (
        <div>
          <h2>ようこそ、{user.email}さん!</h2>
          <button onClick={handleSignOut}>ログアウト</button>
        </div>
      ) : (
        <form>
          <input
            type="email"
            placeholder="メールアドレス"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
          <input
            type="password"
            placeholder="パスワード"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
          />
          <button onClick={handleSignIn}>ログイン</button>
          <button onClick={handleSignUp}>新規登録</button>
        </form>
      )}
    </div>
  )
}

この実装では、以下の重要な機能を含んでいます:

  1. 初期セッション取得: アプリケーション起動時に既存のセッションを確認
  2. リアルタイム監視: 認証状態の変化を自動的に検出
  3. ユーザー登録とログイン: 基本的な認証機能
  4. 適切なクリーンアップ: コンポーネントの破棄時に監視を停止

エラーハンドリング戦略

javascript
const handleAuthError = (error) => {
  switch (error.message) {
    case 'Invalid login credentials':
      return 'メールアドレスまたはパスワードが正しくありません'
    case 'Email not confirmed':
      return 'メールアドレスの確認が完了していません'
    case 'Password should be at least 6 characters':
      return 'パスワードは6文字以上である必要があります'
    case 'User already registered':
      return 'このメールアドレスは既に登録されています'
    default:
      return 'エラーが発生しました。しばらくしてからお試しください'
  }
}

const signInWithErrorHandling = async (email, password) => {
  try {
    const { data, error } = await supabase.auth.signInWithPassword({
      email,
      password
    })
    
    if (error) {
      const friendlyMessage = handleAuthError(error)
      setErrorMessage(friendlyMessage)
      return
    }
    
    // ログイン成功時の処理
    setErrorMessage('')
    // 必要に応じてリダイレクト
    
  } catch (error) {
    setErrorMessage('予期しないエラーが発生しました')
    console.error('認証エラー:', error)
  }
}

この例では、Supabaseが返すエラーメッセージをユーザーフレンドリーな日本語メッセージに変換しています。適切なエラーハンドリングにより、ユーザー体験が大幅に向上します。

セキュリティベストプラクティス

Row Level Security (RLS) の活用

sql
-- ユーザーが自分のデータのみアクセスできるポリシー
CREATE POLICY "Users can only access their own data"
  ON profiles
  FOR ALL
  USING (auth.uid() = id);

-- 認証済みユーザーのみアクセス可能なポリシー
CREATE POLICY "Authenticated users can read posts"
  ON posts
  FOR SELECT
  USING (auth.role() = 'authenticated');

RLS(Row Level Security)は、データベースレベルでのセキュリティ制御を提供します。これにより、ユーザーは自分のデータのみにアクセスでき、不正なデータアクセスを防止できます。

セッション管理のベストプラクティス

javascript
// セッションの有効期限チェック
const checkSessionValidity = async () => {
  const { data: { session } } = await supabase.auth.getSession()
  
  if (session) {
    const expirationTime = session.expires_at * 1000
    const currentTime = Date.now()
    const timeUntilExpiry = expirationTime - currentTime
    
    // 有効期限まで5分以下の場合、セッションを更新
    if (timeUntilExpiry < 5 * 60 * 1000) {
      await supabase.auth.refreshSession()
    }
  }
}

// 定期的なセッションチェック
setInterval(checkSessionValidity, 60000) // 1分ごと

この実装では、セッションの有効期限を定期的にチェックし、期限切れ前に自動的にセッションを更新しています。これにより、ユーザーの作業が中断されることを防げます。

高度な機能とカスタマイズ

カスタムフックの作成

javascript
import { useState, useEffect } from 'react'
import { supabase } from './supabaseClient'

const useAuth = () => {
  const [user, setUser] = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)

  useEffect(() => {
    const getSession = async () => {
      try {
        const { data: { session }, error } = await supabase.auth.getSession()
        if (error) throw error
        setUser(session?.user ?? null)
      } catch (error) {
        setError(error.message)
      } finally {
        setLoading(false)
      }
    }

    getSession()

    const { data: { subscription } } = supabase.auth.onAuthStateChange(
      async (event, session) => {
        setUser(session?.user ?? null)
        setLoading(false)
      }
    )

    return () => subscription.unsubscribe()
  }, [])

  const signUp = async (email, password, options = {}) => {
    try {
      setError(null)
      const { data, error } = await supabase.auth.signUp({
        email,
        password,
        options
      })
      if (error) throw error
      return data
    } catch (error) {
      setError(error.message)
      throw error
    }
  }

  const signIn = async (email, password) => {
    try {
      setError(null)
      const { data, error } = await supabase.auth.signInWithPassword({
        email,
        password
      })
      if (error) throw error
      return data
    } catch (error) {
      setError(error.message)
      throw error
    }
  }

  const signOut = async () => {
    try {
      setError(null)
      const { error } = await supabase.auth.signOut()
      if (error) throw error
    } catch (error) {
      setError(error.message)
      throw error
    }
  }

  return {
    user,
    loading,
    error,
    signUp,
    signIn,
    signOut
  }
}

export default useAuth

このカスタムフックにより、認証ロジックを再利用可能な形で抽象化できます。複数のコンポーネントで認証機能を使用する場合、このフックを使用することで開発効率が大幅に向上します。

パフォーマンス最適化

javascript
// メモ化によるパフォーマンス最適化
import { useMemo, useCallback } from 'react'

const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null)
  const [loading, setLoading] = useState(true)

  const authValue = useMemo(() => ({
    user,
    loading,
    isAuthenticated: !!user,
    signIn: useCallback(async (email, password) => {
      const { data, error } = await supabase.auth.signInWithPassword({
        email,
        password
      })
      if (error) throw error
      return data
    }, []),
    signOut: useCallback(async () => {
      const { error } = await supabase.auth.signOut()
      if (error) throw error
    }, [])
  }), [user, loading])

  return (
    <AuthContext.Provider value={authValue}>
      {children}
    </AuthContext.Provider>
  )
}

この実装では、useMemouseCallbackを使用して、不必要な再レンダリングを防止しています。これにより、アプリケーションのパフォーマンスが向上します。

トラブルシューティング

一般的な問題と解決策

問題 原因 解決策
"Invalid login credentials" 間違ったメールアドレスまたはパスワード 入力内容の確認、パスワードリセット機能の提供
"Email not confirmed" メールアドレスの確認が未完了 確認メールの再送信機能を実装
"Session expired" セッションの有効期限切れ 自動的なセッション更新機能を実装
"User already registered" 既に登録済みのメールアドレス ログイン画面への誘導を実装

デバッグ技法

javascript
// デバッグ用のログ出力
const debugAuth = (operation, data, error) => {
  console.group(`🔐 Auth Operation: ${operation}`)
  console.log('Data:', data)
  if (error) {
    console.error('Error:', error)
  }
  console.groupEnd()
}

// 使用例
const signIn = async (email, password) => {
  const { data, error } = await supabase.auth.signInWithPassword({
    email,
    password
  })
  debugAuth('signIn', data, error)
  return { data, error }
}

この実装により、認証関連の操作をより詳細に追跡できます。本番環境では、適切なログレベルの設定が重要です。

まとめ

Supabase.authは、現代のWebアプリケーション開発において不可欠な認証機能を包括的に提供する強力なツールです。本記事で解説した各メソッドと実装例を参考に、セキュアで使いやすい認証システムを構築してください。

キーポイント

  1. 包括的な認証機能: メールアドレス、OAuth、マジックリンクなど多様な認証方式をサポート
  2. セキュリティ重視: 業界標準のセキュリティプラクティスを標準で実装
  3. 開発効率: シンプルなAPIで迅速な開発が可能
  4. 柔軟性: カスタマイズとスケールが容易

今後の学習ロードマップ

  1. 基礎固め: 本記事のメソッドを実際のプロジェクトで実装
  2. 高度な機能: Row Level Security、カスタムクレームの活用
  3. 統合: 他のSupabaseサービス(データベース、ストレージ)との連携
  4. 最適化: パフォーマンスチューニングとセキュリティ強化

Supabase.authをマスターすることで、より安全で効率的なWebアプリケーションの開発が可能になります。継続的な学習と実践により、認証システムの専門知識を深めていきましょう。