🍆

@supabase/ssrを使ってローカル環境のSupabaseでクライアントサイドでTwitter(X)認証する

2023/11/01に公開

2023年10月下旬にauth-helpersが非推奨となってしまいました。
代わりに @supabase/ssr の使用をするようにアナウンスされています。

現在公開されているドキュメントだけでも十分に動かせるぐらいの情報量はあるので、それに従いながら進めていきます。

※この投稿は2023年11月1日時点の情報です。
 Supabaseは急速に進化しているため、必ず公式ドキュメントに目を通すことをおすすめします。

公式ドキュメント

▼認証まわり
https://supabase.com/docs/guides/auth/server-side/oauth-with-pkce-flow-for-ssr

0.事前準備

  • Dockerをインストールしておくこと。
  • Next.js(App Router)が動く状態にしておく
  • Supabase CLIをインストールしておくこと。
  • Twitter Developer Portalにサインインできる状態。(審査が無くなった?っぽいので気軽に使えるかと思います!)
Next.js環境でnpmパッケージをインストールしておこう
npm install @supabase/ssr @supabase/supabase-js

0. 環境変数を設定しよう

ここはすごく単純なことなので下記ページをご覧ください。

https://supabase.com/docs/guides/auth/server-side/creating-a-client?environment=route-handler#set-environment-variables

1. Twitter Developer Portalで API Key と API Key Secretを取得しよう。

Twitter Developer Portalで API KeyAPI Key Secret を取得する必要があります。

Keys and tokens タブの「Consumer Keys」から生成できます。
生成された2つのAPIキーをメモしておきましょう。

2. Twitter(X)認証の設定をする。

Settingsタブの「User authentication settings」の「User authentication set up」のEditをクリックします。

【App Permissions】
上段のRead / Write権限はプロジェクトに応じて選択すれば良いですが、
必ず「Request email from users」を有効にしておきましょう。(Supabaseはメールアドレスが取得できないとサービスプロバイダ認証ができないため。)

【Type of App】
ご自身のアプリケーションの種類で選んでください。
Webサービスであれば、「Web App, Automated App or Bot」を選択すればOKです。

【App info】

項目名
Callback URI
RedirectURL
http://127.0.0.1:54321/auth/v1/callback
Website URL 公開予定のドメインを入れておけばOK
Terms of service 公開予定のドメイン + /terms を入れておけばOK
Privacy policy 公開予定のドメイン + /privacy を入れておけばOK

これでTwitter(X)での設定は完了です。

次にローカルで Supabase が動くようにしておきましょう。

3. ローカルでSupabaseを立ち上げる

どこでもいいので、Supabaseを入れるようのフォルダを作成してください。
作成したフォルダまで ターミナル等で移動しておきましょう。

仮に sample-supabase というフォルダを作ったのであれば、 sample-supabase フォルダの中に入っている状態で下記を進めてください。

Supabaseをインストールする

ターミナル
supabase init

このコマンドを実行するとSupabaseのインストールが開始します。
終了したら、

Docker Desktopを起動した後で、

ターミナル
supabase start

を実行してSupabaseを立ち上げましょう。

ブラウザで http://127.0.0.1:54323/ にアクセスしてみてください。
Supabaseのダッシュボードが開けば成功です!

4. SupabaseでTwitter(X)認証を有効化する

Supabase をインストールしたフォルダを開くと、supabaseディレクトリの中に config.toml というファイルがあるかと思います。

このファイルの末尾に下記を追記してください。

supabase/config.toml
[auth.external.twitter]
enabled = true
client_id = "【ここにAPI Key】"
secret = "【ここにAPI Key Secret】"

記事冒頭の(1)で取得したTwitter(X)のAPIキーを入力しましょう。
仮にAPIキーが「hogehogehogehoge」だとしたら、下記のように書きます。

client_id = "hogehogehogehoge"

ダブルクォーテーションは必要なので注意してください。

この設定ファイルを反映させるには Supabase を再起動させなければなりません。

ターミナル
supabase stop

で一旦Supabaseを停止し、 supabase start でもう一度起動させましょう。

これで Supabase側の準備も完了しました!

5. Next.js に記述をしてクライアントサイドでTwitter(X)認証できるようにする

認証時のコールバックAPIを用意しておきましょう。
app/auth/callback/route.ts ファイルを作成してください。

/src/app/auth/callback/route.ts
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const code = searchParams.get('code')
  const next = searchParams.get('next') ?? '/'

  if (code) {
    const cookieStore = cookies()

    const supabase = createServerClient(
      process.env.NEXT_PUBLIC_SUPABASE_URL!,
      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
      {
        cookies: {
          get(name: string) {
            return cookieStore.get(name)?.value
          },
          set(name: string, value: string, options: CookieOptions) {
            cookieStore.set({ name, value, ...options })
          },
          remove(name: string, options: CookieOptions) {
            cookieStore.delete({ name, ...options })
          },
        },
      }
    )

    const { error } = await supabase.auth.exchangeCodeForSession(code)
    if (!error) {
      return NextResponse.redirect(new URL(`/${next.slice(1)}`, request.url))
    }
  }

  // return the user to an error page with instructions
  return NextResponse.redirect(new URL('/auth/auth-code-error', request.url))
}

上記は完全コピペで大丈夫です。
※認証ミスると /auth/auth-code-error にリダイレクトされるので、それ用のページは別途用意しておく必要があります。(認証成功パターンとしては使わないので、この記事では割愛します。)

最後に認証を実行開始するためのボタンをクライアントコンポーネントとして設置しておきましょう。

ButtonSignup.tsx
"use client";

import { createBrowserClient } from '@supabase/ssr'

export const ButtonSignup = () => {
  const supabase = createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
  );

  const handleSignUp = async () => {
    // signInWithOAuthという名前だが、新規会員登録もログインも全く同じ関数を使う
    // 新規なのかログインなのかはSupabaseがうまいことやってくれる。
    await supabase.auth.signInWithOAuth({
      provider: "twitter",
      options: {
        redirectTo: "http://localhost:3000/auth/callback/",
      },
    });
  };

  return (
    <button onClick={handleSignUp}>
      <span>新規登録</span>
    </button>
  );
};

<ButtonSignup /> をどこかに出力すれば完成です!

6. 認証を実行し、登録できているか確認して終わろう!

それでは実際にボタンを押して認証してみましょう。
Twitter認証画面が開いた後、 localhost:3000/ に戻ってくるはずです。

戻ってきたら、 http://127.0.0.1:54323/project/default/auth/users にアクセスしましょう。

ここにユーザーが追加されていれば成功です!
お疲れ様でした!

データ取得とかどうするのか?

'@supabase/ssr' を使ったデータ取得をする際は、
クライアントサイドでリクエストしたい場合は createBrowserClient
サーバーサイドでリクエストしたい場合は createServerClient を使うことになります。

引数には環境変数で設定したSupabase URLやAnon Keyを入れたり、cookieの中身を渡すなどすることで使えるようになります。詳しくは下記の公式ドキュメントをご覧ください。

https://supabase.com/docs/guides/auth/server-side/creating-a-client?environment=client-component

おまけ【個人的に詰まったところ紹介】

hosts で localhost-sample.com:3000 として試していました。
コイツが原因で認証が成功しないというミスを犯していました。

hosts使うと辛いことになるので大人しく localhost でやっておいた方が楽です...!!!

おまけ2【Supabase CLIのデータベース周り】

https://zenn.dev/masa5714/scraps/bb092ef909ff06

コマンド適当にやってるとえらい目喰らうので先にある程度触ってDB壊したり、PC再起動するとどういう挙動になるかとか確認しておくと吉です!

おまけ3【クライアントサイドでSELECTでデータ取得することの是非を考える】

https://zenn.dev/masa5714/articles/40883d972ab2c7

クライアントサイドでデータ取得する場合、 supabase.from('hoge').select(``) でデータ取得するとヤバそうな予感がしたので上記の記事書きました。まだ正解が見えていませんが、現時点ではポリシーでSELECTのリクエストを FALSE にしておき、 .rpc() で関数呼び出して制御できる範囲でデータ取得するべきかなと落ち着いています。

この件については皆さんのご意見も聞いてみたいところです。

Discussion