Next.jsでsupabaseのAnonymous Sign-Insを使う
Next.jsでSupabaseのAnonymous Sign-Insを試してみたので実装についてまとめます。
Anonymous Sign-Insについて
Anonymous Sign-Insを有効にすることでユーザーにメールアドレスやパスワード入力やOAuthプロバイダーなどの個人を特定できる情報を要求せずに認証機能を構築することができます。
今回は使い捨てアカウントでメッセージを投稿する機能を実装したかったので、supabaseのAnonymous Sign-Insを使いました。
ドキュメントに記載もありますが、Anonymous Sign-Insは下記のようなことに使うことができます。
- チェックアウト前のショッピングカートなどのEコマース・アプリケーション
- 個人情報を収集しないフル機能のデモ
- 一時的または使い捨てアカウント
やらないこと
- Supabaseプロジェクトのセットアップ、Next.jsアプリの作成は取り扱いません。
詳細は公式ドキュメントをご確認ください。
- 匿名ユーザーにメールアドレス/電話番号やOAuth IDを紐づけることで永続的なユーザーに変換することができますが、今回はそこまで行いません。
詳細は公式ドキュメントをご確認ください。
実装
signInAnonymously()
を呼び出すための関数を作成
'use server'
export async function anonymousSignIn() {
const supabase = createClient();
const { error } = await supabase.auth.signInAnonymously();
if (error) {
// エラー時の処理
}
revalidatePath('/', 'layout');
redirect('/');
}
サインインページでactionsを呼び出す
anonymousSignIn
をformのactionに渡します。
import { anonymousSignIn } from '~/actions';
export default async function SignIn() {
return (
<form action={anonymousSignIn}>
<button type='submit'>SignIn</button>
</form>
);
}
ボタンを押すことで関数が実行されていることが確認できました。
※supabaseのEnable Captcha protectionを有効化しているためこの時点では失敗しています。
下記ドキュメントに記載されていますが、匿名サインインの悪用を防ぐためにEnable Captcha protectionを有効にすることが強く推奨されています。
Abuse prevention and rate limits
Cloudflare Turnstileのセットアップ
-
Cloudflare Webサイトにアクセスしてサインアップ後、新しいサイトを作成してSite Key、Secret Keyを取得します。
-
ダッシュボードのProject Setting/Authenticationに移動します。
Enable Captcha protectionを有効化、Captcha ProviderとしてCloudflare Turnstileを選択して、Captcha secretに先ほど取得したSecret Keyを設定します。
-
Captchaコンポーネントを追加します
以下コマンドを実行してTurnstile Reactコンポーネントをインストールします。
npm install @marsidev/react-turnstile
Turnstile
コンポーネントにsiteKey
、コールバック関数を渡します。
<Turnstile
siteKey={env.NEXT_PUBLIC_TURNSTILE_SITE_KEY}
onSuccess={(token) => {
setCaptchaToken(token);
}}
/>
SignInを押すことでsupabaseにユーザーが作成されることが確認できました。
hCaptcha、Cloudflare Turnstileの設定については以下ドキュメントに詳細がありますのでこちらをご確認ください。
この時点でのactions.ts
、page.tsx
の実装は以下の通りです。
export async function anonymousSignInAction(captchaToken: string, _: FormData) {
const supabase = createClient();
const { error } = await supabase.auth.signInAnonymously({
options: {
captchaToken: captchaToken,
},
});
if (error) {
// エラー時の処理
}
revalidatePath('/', 'layout');
redirect('/signin');
}
'use client';
import { Turnstile } from '@marsidev/react-turnstile';
import { useState } from 'react';
import { anonymousSignInAction } from '~/actions';
import { env } from '~/env.mjs';
export default function SignIn() {
const [captchaToken, setCaptchaToken] = useState('');
const anonymousSignIn = anonymousSignInAction.bind(null, captchaToken);
return (
<div className='flex flex-col items-center justify-center'>
<Turnstile
siteKey={env.NEXT_PUBLIC_TURNSTILE_SITE_KEY}
onSuccess={(token) => {
setCaptchaToken(token);
}}
/>
<form action={anonymousSignIn}>
<button type='submit'>SignIn</button>
</form>
</div>
);
}
captchaToken
を保持するためにuseStateを使用しているため、いったんpage.tsx
に対して'use client'
を付けています。
また、Server ActionsにcaptchaToken
を渡すためにbind
を使っています。
bind以外の方法
bind
を使わずにcaptchaToken
を渡す方法として非表示の入力フィールドを使うこともできます。
<form action={anonymousSignIn}>
<input className='hidden' name='captchaToken' value={captchaToken} />
<button type='submit'>SignIn</button>
</form>
formData.get('captchaToken')
とすることでServer ActionsでcaptchaTokenを使うことができます。
const { error } = await supabase.auth.signInAnonymously({
options: {
captchaToken: formData.get('captchaToken'),
},
});
まとめ
signInAnonymously
を使ってユーザー作成できるところまでざっくりとまとめました。
今回の方法ではcaptchaToken
の保持にuseStateを使っていますが、useActionStateとconformを使った実装でも動作することが確認できました。
captchaToken
が取得される前にボタンが押されたときにクライアント側でバリデーションを行ったり、サインイン処理が完了するまでisPending
でローディングを表示するといったことができるため、useActionStateとconformを使った実装を行うのが良さそうでした。
参考
Discussion