@supabase/ssrを使うときは、ブラウザ側の操作にも利用しよう
Supabaseのサーバー側での認証状況確認に@supabase/ssrパッケージを使用し、ログアウト処理での問題点や解決方法を紹介。@supabase/subapase-jsのログアウト処理でCookieが削除されず、@supabase/ssrを使用することで解決可能。ログアウト時にcreateBrowserClientを使用することで正常にCookieを削除。SSR用のライブラリを統一することが望ましい。
目次
Supabaseでは、サーバー側で認証状況を確認する処理を実行したい場合に専用のパッケージ@supabase/ssr
を使います。これを使っている際のログアウト処理でちょっとハマったことがあったので、メモします。
やろうとしたこと
マイページにて、サーバー側でログイン状況の確認を行う実装を行いました。ログイン状況を確認し、ログイン状態ではない場合はログインページにリダイレクトさせるような機能を作ることが目的です。
@supabase/ssrでサーバー側の認証制御をおこなう
サーバー側で認証状況を確認する場合、SSR用のパッケージを利用します。ドキュメントに従って、Cloudflare / Remixで利用するクライアントを作成しました。
import { createServerClient, parseCookieHeader, serializeCookieHeader } from '@supabase/ssr'
export const createSupabaseServerClient = ({context, request}: {
context: {
cloudflare: {
env: Pick<Env, 'SUPABASE_KEY' | 'SUPABASE_URL'>
}
},
request: Request
}, headers: Headers) => {
const supabaseClient = createServerClient(
context.cloudflare.env.SUPABASE_URL,
context.cloudflare.env.SUPABASE_KEY, {
cookies: {
getAll() {
return parseCookieHeader(request.headers.get('Cookie') ?? '')
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) => {
headers.append('Set-Cookie', serializeCookieHeader(name, value, options))
})
}
},
})
return supabaseClient
}
この処理をマイページ側のloader
で動かし、ユーザーのセッションが確認でいない場合はログイン画面に戻します。
export async function loader(props: LoaderFunctionArgs) {
const {context, request} = props;
const supabaseClient = createSupabaseServerClient({context, request}, request.headers)
const response = await supabaseClient.auth.getSession()
if (!response.data.session) {
throw redirect('/signin')
}
return null
}
@supabase/subapase-js
のログアウト処理だと、Cookieが消えない
しかしこの後ちょっとハマったことがありました。それはログアウト処理を実装した時のことです。シンプルな例として、ボタンをクリックした時にログアウトする処理を実行するとしましょう。ドキュメントを見ながら実装すると、このようなコードになる・・・と思います。
import { createClient } from '@supabase/supabase-js';
export const supabaseClient = createClient(
import.meta.env.VITE_PUBLIC_SUPABASE_URL,
import.meta.env.VITE_PUBLIC_SUPABASE_ANON_KEY
)
<button
onClick={async e => {
e.preventDefault()
await supabaseClient.auth.signOut()
}}
>
Sign out
</button>
しかしここでちょっとした問題が発生しました。それはこの処理でログアウトした場合、その後にアクセスしたページのloader
で@supabase/ssr
を利用してユーザー情報を取得すると、ログアウトしたはずのユーザー情報が取得できてしまったのです。デバッグを進めたところ、@supabase/supabase-js
を使ったログアウト処理では、@supabase/ssr
側で設定したCookieが削除できていない様子でした。
ログアウト処理もSSR用パッケージを利用する
解決方法はシンプルで、Cookieを消すことができればOKです。@supabase/supabase-js
のcreateClient
側でも明示的にCookie周りの設定を行うことができる・・・かもなのですが、SDKコードを見ると@supabase/ssr
のcreateBrowserClient
が使えそうでした。
import { createBrowserClient } from '@supabase/ssr';
export const supabaseClient = createBrowserClient(
import.meta.env.VITE_PUBLIC_SUPABASE_URL,
import.meta.env.VITE_PUBLIC_SUPABASE_ANON_KEY
)
こちらを使ったログアウト処理を実行してみます。クライアントを作成するパッケージと関数が違いますが、どうやら利用できるAPI自体は同じ様子です。
<button
onClick={async e => {
e.preventDefault()
await supabaseClient.auth.signOut()
}}
>
Sign out
</button>
この方法でログアウトさせた場合は、Cookieが消えました。SSR用のライブラリにブラウザ側のクライアント作成処理が含まれていることからも、どうやら@supabase/ssr
を使うときはこちらで統一する方法がよいのかもしれません。