Estoy desarrollando una aplicación Next.js y tengo una API definida de la siguiente manera:
export default function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method === 'GET') { fn1Get(req, res); } else if (req.method === 'POST') { fn1Post(req, res); } else { res.status(501).json({ operation: `${req.method}: not implemented` }); } } async function fn1Get( req: NextApiRequest, res: NextApiResponse ): Promise<void> { const authenticated = await checkAuth(req, res); if (authenticated) { // Get Stuff res.status(200).json({status: 'all right!'}); } } async function fn1Post( req: NextApiRequest, res: NextApiResponse ): Promise<void> { const authenticated = await checkAuth(req, res); if (authenticated) { // Post Stuff res.status(201).json({status: 'all right!'}); } } const checkAuth = async (req: NextApiRequest, res: NextApiResponse) => { const tokenValid = await extnernalApiCall(getToken(req)); if (!tokenValid) { res.status(403).json({ error: 'Authentication Failed' }); } return tokenValid }; Estoy tratando de encontrar una configuración más fácil para definir métodos autenticados, en lugar de agregar dentro de ellos la línea const authenticated = await checkAuth(req, res);
En otros lenguajes como Java o Python podría usar decoradores/anotaciones/AOP, algo como:
@checkAuth async function fn1Get( req: NextApiRequest, res: NextApiResponse ):¿Puedo hacer algo parecido en javascript? ¿Quizás a través de funciones de ajuste y/o usando bind/call/apply?
Ejemplo de pseudocódigo:
const checkAuth = async (fn) => { const req = arguments[1]; const res = arguments[2]; const tokenValid = await extnernalApiCall(getToken(req)); if (!tokenValid) { res.status(403).json({ error: 'Authentication Failed' }); } return fn(arguments); } async function fn1Get = checkAuth(_fn1Get( req: NextApiRequest, res: NextApiResponse ): Promise<void> { const authenticated = await checkAuth(req, res); if (authenticated) { // Get Stuff res.status(200).json({status: 'all right!'}); } }) Como puede ver, todas las funciones que quiero autenticar recibirán los mismos dos parámetros req y res (solicitud y respuesta), y mi función de autenticación también necesita ambos parámetros para obtener el token para autenticarse desde el req y escribir un 403 en res si no está autenticado
Las tecnologías que estoy usando son Next.js con React 17, TypeScript, ECMA6
Sí, puede lograr esto con una función de envoltura (eso es básicamente lo que son los decoradores de todos modos). Esa función contenedora tiene que devolver una función. Algo como lo siguiente (tendrá que ajustar los tipos en consecuencia):
const checkAuth = (fn) => { return async (req: NextApiRequest,res: NextApiResponse): Promise<void> => { const tokenValid = await extnernalApiCall(getToken(req)); if (!tokenValid) { res.status(403).json({ error: 'Authentication Failed' }); } else { fn(req, res); } } } const fn1Get = checkAuth(( req: NextApiRequest, res: NextApiResponse ): Promise<void> => { // Get Stuff res.status(200).json({status: 'all right!'}); })Habiendo dicho eso, no estoy familiarizado con next.js. Puede haber una manera de registrar controladores de middelware que se activen en cada solicitud sin tener que envolver cada controlador explícitamente.
Uso next js junto con next-auth para la autenticación. Hice una función que verifica si la solicitud tiene una sesión. Si no hay sesión, el usuario es redirigido a la página de inicio de sesión. Si el usuario tiene una sesión, pasa los accesorios a la función getServerSideProps .
import { getSession } from "next-auth/react"; /* gssp = */ export const requireAuth = (gssp) => { return async (ctx) => { const { req } = ctx; const session = await getSession({ req }) if (!session) { return { redirect: { permanent: false, destination: '/api/auth/signin' } }; }; const ctxWithSession = { ...ctx, session }; return await gssp(ctxWithSession); }; };Luego, llamo a esta función en mi próxima página js como una función superior de getServerSideProps :
export const getServerSideProps = requireAuth(async _ctx => { const { session } = _ctx; return { props: { session: session, }, }; });