import type { AxiosInstance, AxiosResponse } from "axios" import type { GetServerSideProps, GetServerSidePropsContext } from "next" import { api } from "../api" import { Cookies } from "../cookies" import type { RefreshTokenResponse, Tokens } from "./index" import { Authentication } from "./Authentication" import type { UserCurrent } from "../../models/User" export const fetchRefreshToken = async ( refreshToken: string, ): Promise => { const { data } = await api.post( "/users/refresh-token", { refreshToken, }, ) return { ...data, refreshToken } } interface AuthenticationFromServerSideOptions { shouldBeAuthenticated: boolean /** allows to fetch data server side with the authenticated user, the callback should returns the data that will be transfer to the component as props */ fetchData?: ( context: GetServerSidePropsContext, api: AxiosInstance, ) => Promise<{ [key: string]: any }> } export const authenticationFromServerSide = ( options: AuthenticationFromServerSideOptions, ): GetServerSideProps => { const { shouldBeAuthenticated, fetchData } = options return async (context) => { const cookies = new Cookies(context.req.headers.cookie) const refreshToken = cookies.get("refreshToken") let tokens: Tokens | null = null if (refreshToken != null) { try { tokens = await fetchRefreshToken(refreshToken) } catch { cookies.remove("refreshToken") } } if (!shouldBeAuthenticated) { if (tokens != null) { return { redirect: { destination: "/application", permanent: false, }, } } else { let data: any = {} if (fetchData != null) { data = await fetchData(context, api) } if (data.notFound != null) { return data } return { props: data } } } else { if (tokens == null) { return { redirect: { destination: "/authentication/signin", permanent: false, }, } } else { try { let data: any = {} const authentication = new Authentication(tokens) const { data: currentUser } = await authentication.api.get< unknown, AxiosResponse >("/users/current") if (fetchData != null) { data = await fetchData(context, authentication.api) } if (data.notFound != null) { return data } return { props: { authentication: { tokens, ...currentUser }, ...data }, } } catch { return { notFound: true, } } } } } }