🔑

個人的に思う NextAuth のベストプラクティス

2021/01/07に公開

こんにちは。 @yuyaaaar です。

昨日、loggnog.com をリリースしました。所感もろもろ という記事を投稿しました。

その中で、NextAuth についてちょっと詳しくなったので、個人的に思う二つのベストプラクティスを書いていきたいと思います。

余談ですが、 NextAuth の記事が最近多くなってきた気がします。いいこといいこと。この記事は NextAuth とは何かというのを理解している前提で読んでください。

1. なるべく NextAuth のイベントハンドラを使おう

NextAuth にはいろんなオプションをつけることが可能です。今回はその内の一つである events を紹介。これは何かというと、何かのイベントをトリガーに関数を実行することが可能となる優れもの。

以下オフィシャルサイトから抜粋

events: {
   signIn: async (message) => { /* on successful sign in */ },
   signOut: async (message) => { /* on signout */ },
   createUser: async (message) => { /* user created */ },
   linkAccount: async (message) => { /* account linked to a user */ },
   session: async (message) => { /* session is active */ },
   error: async (message) => { /* error in authentication flow */ }
}

いろんなイベントをトリガーにできますね。

loggnog では、 stripe のカスタマーを createUser を使って作りました。

こんな感じ:

events: {
   createUser: async (user) => {
      const customer = await stripe.customers.create({
         email: user.email
      })
      
      // customer.id をデータベースに保存。prisma だと、こんな風に
      await prisma.user.update({
        where: {
          id: user.id,
        },
        data: {
          stripeCustomerId: customer.id,
          subscriptionStatus: "FREE",
        },
      });
   }
}

簡単ですね。これで毎回ユーザーサインアップがあると、 stripe 側にもカスタマーを作ってくれます。わざわざカスタマーを作る API Route を作る必要もなく、スッキリ。

2. session にいろいろ情報を詰め込もう

nextAuth には callbacks というオプションがあり、その中で session というのがあります。このコールバックは session のチェックが走るたび呼ばれます。 session の中身は NextAuth を使う醍醐味であろう getSession()useSession() ですね。

このセッション情報は拡張可能で、例えばデフォルトで入ってる email などに加え、ユーザーIDなどを紐づけることも可能です。

こんな風に

session: async (session, user) => {
  // user is a prisma object. passing
  // it to session id so we can verify if user exists or not
  session.user.id = user.id;
  
  // ここは promise を返す必要がある
  return Promise.resolve(session);
}

これで、フロント側でもサーバー側でもユーザーのIDを簡単に参照することが可能です。loggnog では、ユーザーのサブスクリプション情報やプロジェクトIDもセッションに入れています。

session: async (session, user) => {
  // user is a prisma object. passing
  // it to session id so we can verify if user exists or not
  session.user.id = user.id;
  session.user.projectId = user.projectId;
  session.user.subscriptionStatus = user.subscriptionStatus;
  return Promise.resolve(session);
}

こうすることによって、API Routes ではユーザーのサブスク状況を毎度データベースに確認しに行く必要がなくなるので、負担がかからないようになっています。

Discussion