148 lines
4.0 KiB
TypeScript
148 lines
4.0 KiB
TypeScript
import { type GetServerSidePropsContext } from "next";
|
|
import {
|
|
getServerSession,
|
|
type NextAuthOptions,
|
|
type DefaultSession,
|
|
} from "next-auth";
|
|
import CredentialsProvider from "next-auth/providers/credentials";
|
|
import { PrismaAdapter } from "@next-auth/prisma-adapter";
|
|
import { prisma } from "~/server/db";
|
|
import { loginSchema } from "~/lib/validation/auth";
|
|
import { verify } from "argon2";
|
|
import { type Role } from "@prisma/client";
|
|
|
|
interface SessionUser {
|
|
id: string;
|
|
name: string;
|
|
username: string;
|
|
role: Role;
|
|
}
|
|
|
|
/**
|
|
* 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: SessionUser;
|
|
}
|
|
|
|
interface User {
|
|
id: string;
|
|
name: string;
|
|
username: string;
|
|
role: Role;
|
|
}
|
|
}
|
|
|
|
declare module "next-auth/jwt" {
|
|
interface JWT {
|
|
id: string;
|
|
name: string;
|
|
username: string;
|
|
role: Role;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Options for NextAuth.js used to configure adapters, providers, callbacks, etc.
|
|
*
|
|
* @see https://next-auth.js.org/configuration/options
|
|
*/
|
|
export const authOptions: NextAuthOptions = {
|
|
callbacks: {
|
|
jwt({ token, user }) {
|
|
if (user) {
|
|
token.id = user.id;
|
|
token.username = user.username;
|
|
token.name = user.name;
|
|
token.role = user.role;
|
|
}
|
|
return token;
|
|
},
|
|
session({ session, token }) {
|
|
if (token) {
|
|
session.user.id = token.id;
|
|
session.user.username = token.username;
|
|
session.user.name = token.name;
|
|
session.user.role = token.role;
|
|
}
|
|
return session;
|
|
},
|
|
},
|
|
adapter: PrismaAdapter(prisma),
|
|
providers: [
|
|
CredentialsProvider({
|
|
// The name to display on the sign in form (e.g. 'Sign in with...')
|
|
name: "Credentials",
|
|
// The credentials is used to generate a suitable form on the sign in page.
|
|
// You can specify whatever fields you are expecting to be submitted.
|
|
// e.g. domain, username, password, 2FA token, etc.
|
|
// You can pass any HTML attribute to the <input> tag through the object.
|
|
credentials: {
|
|
username: { label: "Username", type: "text" },
|
|
password: { label: "Password", type: "password" },
|
|
},
|
|
async authorize(credentials): Promise<SessionUser | null> {
|
|
try {
|
|
// get the username and password from the credientials
|
|
const { username, password } = await loginSchema.parseAsync(
|
|
credentials
|
|
);
|
|
|
|
// check if username exists in the database
|
|
const result = await prisma.user.findFirst({
|
|
where: { username },
|
|
});
|
|
if (!result) return null;
|
|
|
|
// check if input password match the hashed password
|
|
const isValidPassword = await verify(result.password, password);
|
|
if (!isValidPassword) return null;
|
|
|
|
return {
|
|
id: result.id,
|
|
name: result.name,
|
|
username,
|
|
role: result.role,
|
|
};
|
|
} catch {
|
|
return null;
|
|
}
|
|
},
|
|
}),
|
|
/**
|
|
* ...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
|
|
*/
|
|
],
|
|
jwt: {
|
|
maxAge: 2 * 60 * 60, // 2 hours
|
|
},
|
|
pages: {
|
|
signIn: "/admin/login",
|
|
},
|
|
session: {
|
|
strategy: "jwt",
|
|
},
|
|
};
|
|
|
|
/**
|
|
* 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);
|
|
};
|