🙃

SupabaseのEdge FunctionsとFlutterを使用してAuth userの削除機能を実装してみる

2023/06/24に公開
6

背景

FlutterとSupabaseを使用したアプリを開発している時、ユーザーの退会機能を実装が必要になりました。(よくあるケース)

事前準備

Edge Functions側の実装

SupabseにあるFlutterのドキュメントなどを確認していましたが、Auth userの削除処理について明記されていませんでした。(自分の確認不足でしたらすみません)

そこでEdge Functionsを利用します。

https://supabase.com/docs/guides/functions

ここではさまざまなユースケースがあり、コードも公開されています。

またTypeScriptのサポートされ、Denoが使用されています。

Edge Functionsの作成

supabase functions new delete-user

./functions/delete-user/index.tsが生成されます。

生成されたコードを変更します。(エラー処理の部分はきちんと書いていません)

import { serve } from 'https://deno.land/std@0.177.0/http/server.ts';
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2.25.0';

serve(async (req: Request) => {
  const reqJson = await req.json();
  
  try {
   const supabaseClient = createClient(
      Deno.env.get('SUPABASE_URL') ?? '',
      Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '',
        auth: {
          autoRefreshToken: false,
          persistSession: false,
        },
      }
    );
    
    
    const jwt = req.headers.get('Authorization')!.split(' ')[1]
    const { data } = await supabaseClient.auth.getUser(jwt);
    
    await supabaseClient.auth.admin.deleteUser(
      data.user.id
    );

    return new Response(JSON.stringify({ success: true  }), {
      headers: { 'Content-Type': 'application/json' },
      status: 200,
    });
  } catch (error) {
    return new Response(JSON.stringify({ error: error.message }), {
      headers: { 'Content-Type': 'application/json' },
      status: 400,
    });
  }
});

deleteUserの第二引数でtrueを設定すると、論理削除がされます。デフォルトはfalseです。

https://supabase.com/docs/reference/javascript/auth-admin-deleteuser

デプロイ

以下をSupabseプロジェクトのルートで実行するとSupabseにデプロイがされます。

supabase functions deploy delete-user

管理画面を確認するとデプロイされていることを確認できます。

Flutter側の実装

Flutter側の実装としては、基本的に上記で作成したEdge Functionsを呼び出すだけで終わります。

  void _deleteAccount(BuildContext context) async {
    try {
      await supabase.functions.invoke("delete-user");
      // 削除後の処理など
    } on AuthException catch (error, stackTrace) {
      context.showErrorSnackBar(message: error.message);
    }
  }

まとめ

以上でSupabase のEdge FunctionsとFlutterを使用したユーザーの削除処理ができました。デプロイなども簡単ですしサクッと実装することができました。

ローカル環境での開発もとても簡単にできます。

https://supabase.com/docs/guides/functions/local-development

Discussion

タイラータイラー

素敵な記事ありがとうございます!Supabaseコミュニティ内でもこちらの記事に助けていただいている話をよく聞きます!

一点ユーザーさんが気づいてくれた点があるのですが、上記のDeno functionだと誰でも好きなユーザーIDを引数として投げることができて、好きなユーザーを削除することができることになってしまいます。もしよろしければこちらのガイドにあるようにAuth headerからfunctionsをコールしているユーザーを特定してそのユーザーを削除する形に編集していただくことって可能でしょうか?
https://supabase.com/docs/guides/functions/auth#fetching-the-user

chaichai

コメントありがとうございます!
こちら修正します🙏

chaichai

遅くなってしまいました🙏
こちら修正しました!

タイラータイラー

こちら修正ありがとうございます!ただ、createClientにauth headerを渡してしまうとservice role keyが上書きされユーザーが削除できない気がします。なのでcreateClientではheaderを渡さずに、

const supabaseClient = createClient(
      Deno.env.get('SUPABASE_URL') ?? '',
      Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? '',
        auth: {
          autoRefreshToken: false,
          persistSession: false,
        },
      }
    );

getUserにこのようにJWTを渡すことで service role keyの権限を失うことなくユーザー情報を引っ張ってくることができます!

const jwt = req.headers.get('Authorization')!.split(' ')[1]
const { data } = await supabaseClient.auth.getUser(jwt);
chaichai

確かにservice role keyが上書きされユーザーが削除できないですね!
こちら反映してあります!