import { PrismaAdapter } from "@next-auth/prisma-adapter"; import { type GetServerSidePropsContext } from "next"; import { getServerSession, type NextAuthOptions, type DefaultSession, } from "next-auth"; import KeycloakProvider from "next-auth/providers/keycloak"; import { type KeycloakProfile } from "next-auth/providers/keycloak"; import { type JWT } from "next-auth/jwt"; import { type OAuthConfig } from "next-auth/providers"; import { env } from "~/env.mjs"; import { prisma } from "~/server/db"; /** * Module augmentation for `next-auth` types. Allows us to add custom properties to the `session` * object and keep type safety. * * @see https://next-auth.js.org/getting-started/typescript#module-augmentation */ declare module "next-auth" { interface Session extends DefaultSession { user: { id: string; // ...other properties // role: UserRole; } & DefaultSession["user"]; } // interface User { // // ...other properties // // role: UserRole; // } } /** * Part of the Keycloak fix/workaround, see code bellow for method `signOut`. * * @see https://stackoverflow.com/a/75526977 */ declare module 'next-auth/jwt' { interface JWT { id_token?: string; provider?: string; } } /** * Options for NextAuth.js used to configure adapters, providers, callbacks, etc. * * @see https://next-auth.js.org/configuration/options */ export const authOptions: NextAuthOptions = { callbacks: { session: ({ session, user }) => ({ ...session, user: { ...session.user, id: user.id, email: user.email, image: user.image, }, }), /** * Part of the Keycloak fix/workaround, see code bellow for method `signOut`. * * @see https://stackoverflow.com/a/75526977 */ async jwt({ token, account }) { if (account) { token.id_token = account.id_token token.provider = account.provider } return token }, }, adapter: PrismaAdapter(prisma), providers: [ KeycloakProvider({ clientId: env.KEYCLOAK_CLIENT_ID, clientSecret: env.KEYCLOAK_CLIENT_SECRET, issuer: env.KEYCLOAK_ISSUER, // authorizationUrl: env.KEYCLOAK_ISSUER + "/protocol/openid-connect/auth", // accessTokenUrl: env.KEYCLOAK_ISSUER + "/protocol/openid-connect/token", // profileUrl: env.KEYCLOAK_ISSUER + "/protocol/openid-connect/userinfo", // wellKnown: env.KEYCLOAK_ISSUER + "/.well-known/openid-configuration", }), /** * ...add more providers here. * * Most other providers require a bit more work than the Discord provider. For example, the * GitHub provider requires you to add the `refresh_token_expires_in` field to the Account * model. Refer to the NextAuth.js docs for the provider you want to use. Example: * * @see https://next-auth.js.org/providers/github */ ], events: { /** * Fix for Keycloak not destroying the session token on logout, * we must send an extra request to delete the session. * * @see https://stackoverflow.com/a/75526977 */ async signOut({ token }: { token: JWT }) { if (token.provider === "keycloak") { const issuerUrl = (authOptions.providers.find(p => p.id === "keycloak") as OAuthConfig).options!.issuer! const logOutUrl = new URL(`${issuerUrl}/protocol/openid-connect/logout`) logOutUrl.searchParams.set("id_token_hint", token.id_token!) await fetch(logOutUrl); } }, } }; /** * Wrapper for `getServerSession` so that you don't need to import the `authOptions` in every file. * * @see https://next-auth.js.org/configuration/nextjs */ export const getServerAuthSession = (ctx: { req: GetServerSidePropsContext["req"]; res: GetServerSidePropsContext["res"]; }) => { return getServerSession(ctx.req, ctx.res, authOptions); };