【supabase】ユーザー招待機能とオプション情報の持たせ方
投稿日: 2024年10月17日
カリキュラム内でも使用し、ポートフォリオ作成でも必ず使用されていると思われるsupabaseですが、昨夜、サインアップ時にユーザー名を入力させて、その情報を持って初回ログイン時にデータベースのUserテーブルにユーザー名を登録したいということで首を突っ込んで一緒にやっていたら、私がユーザー招待機能を作った時とやり方が同じでできたので、招待機能の紹介と同時にオプション情報の持たせ方もまとめてみたいと思います。
赤ちゃんの睡眠アプリでは、同じ赤ちゃんを保育する人同士で情報を共有したいことがあると思い、メインアカウントからサブアカウントを発行する機能を持たせたいと思っていました。
赤ちゃんの情報を共有するので、babyIdで紐づけるイメージです。
そのためには、babyIdを持ったサインアップ時に送信されるメールに付与されるログイン情報にbabyIdを持たせる必要があります。
また、supabaseにはinviteUserByEmail
というメソッドがあるのでこちらを使ったら良さそうだと思い、主に公式を参考に作りました。
service_roleの設定を行います。
inviteUserByEmailは管理者権限が必要な操作で、service_roleという特別なキーが必要になります。
そして、こちらはフロント側に渡してはいけないのでバックエンドで処理する必要があります。
supabase管理画面の設定→API→Project API Keys→service_roleの値をコピーしてきて.env.localにSUPABASE_SERVICE_ROLE=で設定します。
フロントで使わないし見えたらいけないのでNEXT_PUBLICはつけません。
import { createClient } from "@supabase/supabase-js";
import { type NextRequest } from "next/server";
import { getUserAndBabyIds } from "../../_utils/getUserAndBabyIds";
import { ApiResponse } from "@/app/_types/apiRequests/apiResponse";
import { PostRequests } from "@/app/_types/apiRequests/dashboard/subSignup/postRequest";
export const POST = async (req: NextRequest) => {
const token = req.headers.get("Authorization") ?? "";
try {
const body: PostRequests = await req.json();
const { babyId } = await getUserAndBabyIds(token);
const { email } = body;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseServiceRole = process.env.SUPABASE_SERVICE_ROLE;
if (typeof email === "string" && supabaseUrl && supabaseServiceRole) {
const supabaseAdmin = createClient(supabaseUrl, supabaseServiceRole);
const { error } = await supabaseAdmin.auth.admin.inviteUserByEmail(
email,
{
data: { babyId },
redirectTo: `${process.env.NEXT_PUBLIC_APP_URL}/resetPassword/sendEmail/`,
}
);
if (error) {
throw error;
} else {
return Response.json(<ApiResponse>{ status: 200, message: "success" });
}
}
} catch (e) {
if (e instanceof Error) {
if (e.message.includes("Unauthorized")) {
return Response.json({ status: 401, error: e.message });
}
return Response.json(<ApiResponse>{ status: 400, message: e.message });
}
}
};
const supabaseAdmin = createClient(supabaseUrl, supabaseServiceRole);
ここで環境変数から取得したSupabaseのURLとservice_role_keyを使ってクライアントを作成します。
このクライアントは管理者権限のある処理が可能になります。
const { error } = await supabaseAdmin.auth.admin.inviteUserByEmail(
email,
{
data: { babyId },
redirectTo: `${process.env.NEXT_PUBLIC_APP_URL}/resetPassword/sendEmail/`,
}
);
ここでinviteUserByEmailが登場します。
管理者権限のあるクライアント「supabaseAdmin」を使っていないとできません。
渡す値ですが、email
と、option情報です。
{
data: { babyId },
redirectTo: `${process.env.NEXT_PUBLIC_APP_URL}/resetPassword/sendEmail/`,
}
redirectTo: `${process.env.NEXT_PUBLIC_APP_URL}/resetPassword/sendEmail/`,
これはよく見るsupabaseからemailに指定したメールアドレスあてに送信されるメールに添付されるURLです。
招待メールを受け取った人はパスワードの設定をする必要があるので、パスワードを設定するページに飛ばします。
パスワードの再設定と同じメソッド使えるので、今回はページ共有してしまいました。
こちらが他のメソッドでも使える(全てではないでしょうけどサインアップでは使えました)オプション情報です!
公式のページにoptionの持たせ方書いています。下記はinviteUserByEmailのページです。
redirectTo
と一緒に渡せるdataというプロパティがあるので、そこに持たせたい値を持たせるとauth.users.user_metadataに値が入りますと書いてます。
今回はbabyIdを持たせたかったので
{
data: { babyId },
redirectTo: `${process.env.NEXT_PUBLIC_APP_URL}/resetPassword/sendEmail/`,
}
これでOKです。
招待機能の場合は初回ログイン時にUserテーブルに登録したいので、babyIdを持っていたらSUBアカウント、もっていなければMAINのアカウントということでロールを分けています。
const userPostResp = await prisma.user.create({
data: {
supabaseUserId: data.user.id,
role: data.user.user_metadata.babyId ? "SUB" : "MAIN",
babyId: data.user.user_metadata.babyId,
},
});
ポートフォリオ作成を始めて比較的最初の方に実装した機能で、一番最初に訪れた山場で死ぬほど悩みながら実装した記憶あります。
結構忘れていたので復習になってよかったです。
息子昼寝中は時間との闘いなので簡単にではありますが、、、説明足りないところあればご指摘ください。
どなたかの役に立てたらいいなと思います・・!