😊

NextAuthのSessionを拡張する

2023/11/20に公開

next-auth@4.24.5

結果だけ知りたい人はここ

Sessionに id や role などを追加したい・・・!

NextAuthのSessionは、そのままではname, email, image しか取得できません。
しかし場合によってはSessionからidroleを取得したいです。

なのでSessionにidroleを追加していきたいと思います。
その方法に結構苦戦したので、残しておきます。

auth.config.ts
// 初期状態
export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prismaClient),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    }),
  ],
  session: {
    strategy: "jwt",
  },
  secret: process.env.NEXTAUTH_SECRET,
  callbacks: {},
  pages: {
    signIn: "login",
  },
};

id を付与する

まずはSessionにUser.idを付与します。
そのために options に callbacks を追加します。

auth.config.ts
export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prismaClient),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    }),
  ],
  session: {
    strategy: "jwt",
  },
  secret: process.env.NEXTAUTH_SECRET,
  callbacks: {
+    async jwt({ token, user }) {
+      if (user) {
+        token.id = user.id;
+      }
+
+      return token;
+    },
+    async session({ session, token }) {
+      if (token) {
+        session.user.id = token.id;
+      }
+      return session;
+    },
+  },
  pages: {
    signIn: "login",
  },
};

すると、「id は session.user に存在しないよ」、と警告が出ます。
なので Session を拡張しましょう

auth.config.ts
+ declare module "next-auth" {
+  interface Session extends DefaultSession {
+    user: {
+      id: string;
+    } & DefaultSession["user"];
+  }
+ }

const prismaAdapter: Adapter = {
  // ~~~
}

次は、「id は token に存在しないよ」、と警告が出るので、これにも対処します。

auth.config.ts
 declare module "next-auth" {
  interface Session extends DefaultSession {
    user: {
      id: string;
    } & DefaultSession["user"];
 }
 
+ declare module "next-auth/jwt" {
+  interface JWT {
+    id: string;
+  }
+ }

const prismaAdapter: Adapter = {
 // ~~~
}

これで Session にid が付与できました。

role を付与する

idと同じようにroleも付与しましょう。

auth.config.ts
declare module "next-auth" {
  interface Session extends DefaultSession {
    user: {
      id: string;
+     role: string;
    } & DefaultSession["user"];
  }
}

declare module "next-auth/jwt" {
  interface JWT {
    id: string;
+   role: string;
  }
}

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prismaClient),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    }),
  ],
  session: {
    strategy: "jwt",
  },
  secret: process.env.NEXTAUTH_SECRET,
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.id = user.id;
+       token.role = user.role;
      }

      return token;
    },
    async session({ session, token }) {
      if (token) {
        session.user.id = token.id;
+        session.user.role = token.role;
      }
      return session;
    },
  },
  pages: {
    signIn: "login",
  },
};

これでOK、と思いきや次のような警告が出ます。

Property 'role' does not exist on type 'AdapterUser | User'.
Property 'role' does not exist on type 'AdapterUser'.
  • 「今度はUserにroleがないよ」と言われたので追加します。
auth.config.ts
declare module "next-auth" {
+  interface User {
+    role: string;
+  }
  interface Session extends DefaultSession {
    user: {
      id: string;
      role: string;
    } & DefaultSession["user"];
  }
}

これでSessionから role も取得できるようになりました!

完成

最終的にはこんな感じ

auth.config.ts
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import {
  DefaultSession,
  NextAuthOptions,
  getServerSession,
} from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import prismaClient from "@/lib/prisma";
import { Adapter, AdapterUser } from "next-auth/adapters";

declare module "next-auth" {
  interface User {
    role: string;
  }
  interface Session extends DefaultSession {
    user: {
      id: string;
      role: string;
    } & DefaultSession["user"];
  }
}

declare module "next-auth/jwt" {
  interface JWT {
    id: string;
    role: string;
  }
}

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prismaClient),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    }),
  ],
  session: {
    strategy: "jwt",
  },
  secret: process.env.NEXTAUTH_SECRET,
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.id = user.id;
        token.role = user.role;
      }

      return token;
    },
    async session({ session, token }) {
      if (token) {
        session.user.id = token.id;
        session.user.role = token.role;
      }
      return session;
    },
  },
  pages: {
    signIn: "login",
  },
};

終わり

やってみるとシンプルですね。必要なものは拡張して加えていく、それだけ。
「Property 'role' does not exist on type 'AdapterUser | User'.」 を受けてUserを拡張するのは苦戦しましたが、こっちもやってみればシンプル。
これで自由に Session を扱えそうです。

ありがとうございました。

Discussion