2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Supabaseの認証をFirebaseのトークンでやってみようとして挫折した

Posted at

挫折しました。。。

セキュアなアプリを目指して、Supabaseの RLS (Row Level Security) を活用し、ログイン状態に応じてデータベースの参照・操作を制限しようとしました。

Supabase RLSは、Supabase Auth を使えば簡単に設定できるようですが、今回のアプリではすでに Firebase Auth を導入済み。
そこで、Firebaseの認証を維持したまま、Supabase RLS を適用する方法を模索しました。

環境

  • フレームワーク: Nuxt.js (Vue)
  • 認証: Firebase Authentication
  • データベース: Supabase

開発の流れ

まずはSupabase RLSを使用して一番簡単であろう"ログイン中のユーザーはSELECTでデータを参照できる"を目指します。

  1. rls_testというテーブルにユーザーがログインしていたらSELECTできるというポリシーを作成
  2. supabaseの認証にfirebaseを追加
  3. firebaseに対してcloud functionでrole: "authenticated"を設定
  4. SupabaseClient初期化時にfirebase tokenを設定する
  5. テーブルをSELECTしてみる

結果、SupabaseClient初期化時にfirebase tokenを渡せず失敗

1,2,3はうまく設定できました。
公式ドキュメントを参考にしつつ、実装のイメージが全くついていなかったFirebaseのcloud functionでのroleの設定も確認できました。
4も実装し5で対象テーブルのデータを取ろうとしましたが、取れていない、、、。
RLSポリシーを無効化するとテーブルのデータが取得できるので問題は4にありそうです。

残念ながら、今回達成できたのはここまででした。
Firebaseのcloud functionでrole: "authenticated"を設定後、4のSupabaseClientをfirebase token付きで初期化しようとしましたが、ログで確認したところ、初期化したSupabaseClientの中にaccessTokenがありませんでした。

実際のコード

const supabase = createClient<Database>(
  runtimeConfig.public.supabaseUrl,
  runtimeConfig.public.supabaseKey,
  {
    accessToken: async () => {
      return await user.value?.getIdToken(true) ?? null
    },
  }
)

console.log(supabase)

console.logしている箇所の出力結果

SupabaseClient {supabaseUrl: 'https://xxxx.supabase.co', supabaseKey: 'xxx', realtimeUrl: ...
accessToken: async () => {}
    length: 0
    name: "accessToken"
    arguments: (...)
    caller: (...)

accessTokenのlengthが0なので設定ができていないようです。
argumentsとcallerにはこのようなエラー分が出力されていました。

arguments
: 
[Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them at accessToken.invokeGetter (<anonymous>:3:28)]

上記のことから、accessTokenが設定できていなそうだと予想しております。

ちなみに今回設定したSupabase RLSのポリシーは以下です。

using trueとすることで、RLS を有効にしつつ、とりあえず「誰でも参照できる」状態にしてテストしています。

alter policy "Enable read access for all users"
on "public"."rls_test"
to public
using (
  true
);

(※RLSを無効にするとテーブルは参照できることを確認済み)

エラー解決に向けて試したこと

  1. Supabaseのバージョンを確認・更新
    @supabase/supabase-js のバージョンが古い可能性があるため、最新版へ更新してみる
    npm update @supabase/supabase-js
    → 変化なし。エラーは依然発生。

  2. SupabaseClientのaccessToken オプションの指定方法を変更
    createClientのaccessTokenオプションが非同期関数 (async () => {})のままで期待通りに動作していない可能性を考え、明示的にawaitで取得したトークンを渡すように変更しました。

const token = await user.value?.getIdToken(true);
const supabase = createClient<Database>(
  runtimeConfig.public.supabaseUrl,
  runtimeConfig.public.supabaseKey,
  { accessToken: token }
);

→ 依然として accessToken が設定されていない。

  1. Supabase の auth.exchangeCodeForSession を試す
    AIに相談すると違う方法でaccessTokenを付与する方法も提案されました。
    auth.signInWithIdToken を使用することで、FirebaseのtokenをSupabaseに渡す方法もあるみたいです。
const token = await user.value?.getIdToken(true);
const { error } = await supabase.auth.signInWithIdToken(token);
if (error) console.error("Sign-in error:", error);

他にもsetTimeoutで数秒待ってみたり、エラーでググりまくりましたが有効な解決手段が見つからず断念。

今後どうしていくか

今回はNuxt.jsのアプリの中で全てのコードを書いていました。
Firebaseのcloud functionを使用し、完全に別サーバーで試してみようかとも思っています。

最後に

今回の挑戦は、悔しい結果に終わりました。
公式サイトもあり仕組み的にもアンチパターンでないため悔しいですね。
調査スキルを上げていきたい次第です。
公式ドキュメントの読み込みや、Cloud Function の設定方法を学べたことは大きな収穫でした。

最後まで読んでいただきありがとうございました。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?