Untitled
unknown
javascript
a year ago
4.7 kB
6
Indexable
import { NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { signInSchema } from "./zod";
import { JWT } from "next-auth/jwt";
import { User } from "next-auth";
import axios, { AxiosError } from "axios";
import { Role } from "@/types/role";
const BASEURL = process.env.NEXT_PUBLIC_API_BASE_URL;
interface ApiError {
message: string;
statusCode?: number;
errors?: Record<string, string[]>;
}
interface AuthResponse {
accessToken: string;
refreshToken: string;
user: {
id: string;
email: string;
firstName: string;
lastName: string;
role: Omit<Role, "departments">;
};
}
const handleApiError = (error: unknown): never => {
if (axios.isAxiosError(error)) {
const axiosError = error as AxiosError<ApiError>;
const errorMessage =
axiosError.response?.data?.message ||
axiosError.response?.data?.errors?.general?.[0] ||
axiosError.message ||
"Authentication failed";
throw new Error(errorMessage);
}
throw error;
};
export const refreshAccessToken = async (token: JWT): Promise<JWT> => {
try {
const { data } = await axios.post<AuthResponse>(
`${BASEURL}/auth/refresh-token`,
{ refreshToken: token.refreshToken },
{ headers: { "Content-Type": "application/json" } }
);
return {
...token,
accessToken: data.accessToken,
refreshToken: data.refreshToken || token.refreshToken,
accessTokenExpires: Date.now() + 60 * 60 * 1000,
user: token.user,
};
} catch (error) {
return {
...token,
error: "RefreshAccessTokenError",
};
}
};
export const authOptions: NextAuthOptions = {
providers: [
CredentialsProvider({
name: "Credentials",
type: "credentials",
credentials: {
email: {
label: "Email",
type: "email",
placeholder: "johndoe@email.com",
},
password: {
label: "Password",
type: "password",
placeholder: "********",
},
},
async authorize(credentials) {
try {
const { email, password } = signInSchema.parse(credentials);
const res = await axios.post<AuthResponse>(
`${BASEURL}/auth/login`,
{ email, password },
{
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
withCredentials: true,
timeout: 10000,
}
);
const data = res.data;
if (!data.user) {
return null;
}
const user: User = {
id: data.user.id,
firstName: data.user.firstName,
lastName: data.user.lastName,
email: data.user.email,
role: {
id: data.user.role.id,
createdAt: data.user.role.createdAt,
description: data.user.role.description,
name: data.user.role.name,
},
accessToken: data.accessToken,
};
return user;
} catch (error) {
handleApiError(error);
return null;
}
},
}),
],
callbacks: {
async jwt({ token, user }) {
if (user) {
if (
!user.email ||
!user.id ||
!user.firstName ||
!user.lastName ||
!user.role
) {
throw new Error("Missing required user fields");
}
token.accessToken = user.accessToken!;
token.user = {
id: user.id,
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
role: user.role,
};
token.accessTokenExpires = Date.now() + 60 * 60 * 1000;
return token;
}
if (
token.accessTokenExpires &&
typeof token.accessTokenExpires === "number" &&
Date.now() < token.accessTokenExpires
) {
return token;
}
return await refreshAccessToken(token);
},
async session({ session, token }) {
session.user = {
id: token.user.id,
email: token.user.email,
firstName: token.user.firstName,
lastName: token.user.lastName,
role: token.user.role,
};
session.accessToken = token.accessToken;
session.error = token.error || null;
return session;
},
},
pages: {
signIn: "/",
},
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60,
},
jwt: {
secret: process.env.NEXTAUTH_SECRET,
maxAge: 30 * 24 * 60 * 60,
},
debug: process.env.NODE_ENV === "development",
};
Editor is loading...
Leave a Comment