SupabaseのローカルエミュレーターでOAuthプロバイダーによるログインを行う手順
公式ドキュメントがなかったので備忘録です。
1. OAuthクライアントIDを作成する
各種サービスでOAuthクライアントIDを作成します。
その際、リダイレクトURLを http://localhost:54321/auth/v1/callback
とします。
自分がググった限り特にドキュメントなどで言及されておらず、実際のHTTPリクエストの内容を見て知りました。
Twitterの例
https://developer.twitter.com/en/portal/apps/{アプリID}/auth-settings
Facebookの例
※Facebookの場合はアプリが開発モードならリダイレクトURLは設定不要。
https://developers.facebook.com/apps/{アプリID}/fb-login/settings/
Googleの例
https://console.cloud.google.com/apis/credentials/oauthclient/{クライアントID}?project={プロジェクトID}
2. 実装
Next.jsプロジェクトで @supabase/auth-helpers を使用している場合の例 で説明します。
まずは、例えば Twitterでログイン
のようなボタンをクリックした際に以下のコードを実行します。
import {supabaseClient} from '@supabase/auth-helpers-nextjs'
supabaseClient.auth.signIn(
{provider: 'twitter'}, // 例えばTwitterの場合
{redirectTo: `${window.location.origin}/auth/callback`}, // 例えばこんなURL
)
そして、認証完了後のリダイレクト先として指定した /auth/callback
ページを実装します。
このページに、アクセストークンなどがURLのフラグメントに付加された状態でリダイレクトされてきて、@supabase/auth-helpers
の <UserProvider>
によってそれがパースされてユーザーがログインした状態になるので、 useUser()
によってログインしたユーザーの情報を取得できます。
// pages/auth/callback.tsx
// 例えばこんな感じの処理
import {useUser} from '@supabase/auth-helpers-react'
import {useRouter} from 'next/router'
import {useEffect} from 'react'
const Callback: NextPage = () => {
const {user} = useUser()
const router = useRouter()
useEffect(() => {
if (user) {
// 「ソーシャルアカウントでログインしました」などのtoastを表示したり
// auth.usersテーブルとは別にpublic.profilesテーブルなどがある場合はユーザーデータを取得して状態を保存したり
// をここで行う
router.replace('/')
}
}, [user, router])
return <></>
}
export default Callback
supabaseClient.auth.signIn()
の第2引数でredirectTo
を指定して専用のコールバック処理ページを設けるのは必須の対応ではないですが、こうしないと、ログイン後はトップページに#access_token=xxxxx&...
のような長〜いフラグメントが付加されたURLで戻ってくるので体験が良くないのと、専用のページに戻ってくるようにしておけば「ソーシャルアカウントでログインしました」といったtoastを表示するなどログイン手法ごとの処理を気軽に追加できるのでおすすめです。
ちなみに
ちなみに、Supabase AuthではOAuthプロバイダーからユーザーのメールアドレスを受け取り、メールアドレスによってユーザーの一意性を決定します。
なので、例えばユーザーが
- まずメールアドレスとパスワードでサインアップ
-
1.
で登録したメールアドレスと同じメールアドレスが登録されているSNSアカウントでOAuthログイン
などの手順を踏んだ場合、2.
では新しいユーザーは作成されず、1.
で作られたユーザーがログインしたものと見なされます。
supabase/config.toml
を設定
3. 最後に、ローカルのSupabaseエミュレーターの設定ファイルである supabase/config.toml
に以下のような設定を追記します。
OAuthクライアントID・シークレットは秘匿情報なので .env.local
などに記載して .gitignore
します。
[auth.external.twitter]
enabled = true
client_id = "env(DEV_TWITTER_CLIENT_ID)"
secret = "env(DEV_TWITTER_SECRET)"
[auth.external.facebook]
enabled = true
client_id = "env(DEV_FACEBOOK_CLIENT_ID)"
secret = "env(DEV_FACEBOOK_SECRET)"
[auth.external.google]
enabled = true
client_id = "env(DEV_GOOGLE_CLIENT_ID)"
secret = "env(DEV_GOOGLE_SECRET)"
# .env.local
DEV_TWITTER_CLIENT_ID={TwitterのクライアントID}
DEV_TWITTER_SECRET={Twitterのクライアントシークレット}
DEV_FACEBOOK_CLIENT_ID={FacebookのクライアントID}
DEV_FACEBOOK_SECRET={Facebookのクライアントシークレット}
DEV_GOOGLE_CLIENT_ID={GoogleのクライアントID}
DEV_GOOGLE_SECRET={Googleのクライアントシークレット}
その上で、例えば dotenv-cli を導入して以下のようなnpm scriptsを定義しておきます。
{
"scripts": {
"supabase": "node_modules/supabase/bin/supabase",
"supabase:start": "dotenv -c local -- bash -c 'yarn supabase start'",
"supabase:stop": "dotenv -c local -- bash -c 'yarn supabase stop'"
},
"devDependencies": {
"supabase": "^1.4.5"
}
}
これで、
$ yarn supabase:start
$ yarn supabase:stop
で、OAuthプロバイダーからのリダイレクトを正しく捌ける状態のエミュレーターを起動・終了できます。
4. おまけ:CIでマイグレーションをしている場合はCI環境にも環境変数を適当に設定
CIで supabase db push
を行っている場合は、config.toml
に env()
を書いてしまったために、設定ファイルの読み込みエラー回避のためにCIのプロセスにも環境変数を適当な内容で与えておく必要があります。
例えばGitHub Actionsの場合なら、以下のような感じで設定しておくことになるかと思います。
name: CI
on: push
jobs:
test:
# ...
deploy:
if: ${{ github.ref == 'refs/heads/main' }}
needs: test
# ...
deploy-supabase:
if: ${{ github.ref == 'refs/heads/main' }}
needs: deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v3.0.0
with:
node-version: 16
check-latest: true
- run: yarn
- run: |
yarn yarn supabase link --project-ref $SUPABASE_PROJECT_REF
yarn yarn supabase db push
shell: bash
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
SUPABASE_DB_PASSWORD: ${{ secrets.SUPABASE_DB_PASSWORD }}
SUPABASE_PROJECT_REF: ${{ secrets.SUPABASE_PROJECT_REF }}
+ DEV_TWITTER_CLIENT_ID: nop
+ DEV_TWITTER_SECRET: nop
+ DEV_FACEBOOK_CLIENT_ID: nop
+ DEV_FACEBOOK_SECRET: nop
+ DEV_GOOGLE_CLIENT_ID: nop
+ DEV_GOOGLE_SECRET: nop
参考:https://supabase.com/docs/guides/cli/cicd-workflow#deploying-a-migration
Discussion