「Clerk」で認証して「Supabase」に「そのユーザー」のみがアクセス可能にする
要件
データベースに「ユーザー自身」だけ「自身のレコード」にアクセスできるようにする
事前知識
Clerkとは?
ログインシステムのSaas
Supabaseとは?
データベースのSaas
それでは、やっていく~!
事前準備
名前「todos」でテーブルを新規作成する。
デフォルトのテーブルに以下を追加する。
Name: title Type: text
Name: user_id Type: text
流れ
やることが多いのでやることをリストアップします。
- 必要事項を確認
- Supabaseの署名を入手
- Supabaseが署名したClerk の JWT token(許可証)を作成
- SupabaseでtokenからユーザーIDを抽出するSQL関数の設置
- Supabaseでユーザーだけが読み取り可能にする設定
- Supabaseでユーザーだけが書き込み可能にする設定
- アプリケーションからデータベースへアクセスをテストする
1.必要事項を確認
他人のデータをみれてしまうのはまずいことなのでユーザー自身だけがデータベースのレコードにアクセスできるようにユーザーIDだけでアクセス可能にします。
さらになりすまし防止のために、色々と考慮されているのがJWTを使った方法です。
これだけでセキュリティ対策されているのか心配になりますが公式のやり方なので、これ使っておけばセキュリティ要件は大丈夫のはずです。
必要なこと
- Supabaseの署名
- Supabaseが署名したClerk の JWT token(許可証)
- SupabaseでtokenからユーザーIDを抽出するSQL関数の設置
- Supabaseでユーザーだけが読み取り可能にする設定
- Supabaseでユーザーだけが書き込み可能にする設定
- アプリケーションからデータベースへアクセスをテストする
2. Supabaseの署名を入手
Supabaseのメニューからセッティング
API
ここのボタンを押してコピーする
署名の入手完
3. Supabaseが署名したClerk の JWT token(許可証)を作成
ClerkのメニューからJWT templates
new
supabase
supabaseの署名を貼り付け
保存
これでJWTtokenをアプリケーションで取得可能にする設定が完
4. SupabaseでtokenからユーザーIDを抽出するSQL関数の設置
tokenの中のsubにユーザーIDがデフォルトで保存されているのでSupabaseで抽出するようなSQLの関数をつくります。
SupabaseのメニューからSQLエディタ
new
コピペ
create or replace function requesting_user_id() returns text as $$ select nullif(current_setting('request.jwt.claims', true)::json->>'sub', '')::text; $$ language sql stable;
Runを押す
SQLの関数の設置が完
5. Supabaseでユーザーだけが読み取り可能にする設定
SupabaseのメニューからAuthentication
ポリシー
new
これを選択する
ここに以下を貼り付ける
requesting_user_id() = user_id
ユーザーだけが読み取り可能にする設定完
6. Supabaseでユーザーだけが書き込み可能にする設定
読み取りの書き込み版です。
new
これを選択
trueに上書きする
requesting_user_id() = user_id
ユーザーだけが書き込み可能にする設定完
7. アプリケーションからデータベースへアクセスをテストする
ここにこれを追加する
NEXT_PUBLIC_SUPABASE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.theithoehto9eihoehyohueouhoeyuh.sGFifSnYEeMbNX_RNDTIsh8PC0BDLGwaOEZMy1iCghs
NEXT_PUBLIC_SUPABASE_URL=https://iuthgiuretiurtuirtge.supabase.co
プロジェクトにパッケージをインストール
npm install @supabase/supabase-js
remixの場合
import { json,LoaderFunction } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';
import { getAuth } from "@clerk/remix/ssr.server";
import { createClient } from '@supabase/supabase-js'
export const loader: LoaderFunction = async (args) => {
const { userId,getToken } = await getAuth(args);
// トークンを取得
const token = await getToken({ template: 'supabase' });
if (!token) {
throw new Response("Token is missing", { status: 401 });
}
const supabase = createClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_KEY!, {
global: { headers: { Authorization: `Bearer ${token}` } },
})
// APIリクエストを送信
try {
//書き込み
const { data } = await supabase.from('todos').insert({ title: "aaa", user_id: userId }).select()
console.log(data);
//読み込み
const { data: todos } = await supabase.from('todos').select('*')
console.log(todos);
return json(todos); // jsonヘルパーを使用して適切に返す
} catch (error) {
console.error("Failed to fetch data:", error);
throw new Response('Failed to fetch data', { status: 500 });
}
};
export default function Hoge() {
const data = useLoaderData();
return (
<div>
<h1>Resource Data</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
npm run devして/page
にアクセスすると
できた🥳
おわり
認証してデータベースにアクセスするだけなんだけど、暗黙の了解が多くて初心者には大変でした。
JWTの認証はUse Signing algorithm: HS256なのでこれは確認しないとデコードでハマるので気をつけてね。
参考
Discussion