import { CookieOptions, Request, Response } from 'express'; import ms from 'ms'; import { User } from '@/entities'; import { ACCESS_TOKEN_HEADER_NAME, REFRESH_TOKEN_COOKIE_NAME, REFRESH_TOKEN_EXPIRATION_TIME, } from '../../constants'; import { getUserFromToken } from './getUserFromToken'; type SetRefreshTokenOptions = { /** * Duration the cookie should last, in milliseconds. * @default REFRESH_TOKEN_EXPIRATION_TIME */ maxAge?: number; }; export const setRefreshTokenCookie = ( refreshToken: string | undefined, res: Response, options?: SetRefreshTokenOptions ): void => { const cookieOptions: CookieOptions = { /** * From Mozilla: * "the cookie is not sent on cross-site requests, such as on requests * to load images or frames, but is sent when a user is navigating to * the origin site from an external site (for example, when following a * link)." @see {https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie} */ sameSite: 'lax', httpOnly: true, domain: process.env.COOKIE_DOMAIN, ...(process.env.NODE_ENV === 'development' ? { domain: 'localhost', secure: false, } : {}), maxAge: options?.maxAge ?? ms(REFRESH_TOKEN_EXPIRATION_TIME), }; if (refreshToken?.length === 0) { res.clearCookie(REFRESH_TOKEN_COOKIE_NAME, cookieOptions); return; } // check for string, beccause if (refreshToken) { res.cookie(REFRESH_TOKEN_COOKIE_NAME, refreshToken, cookieOptions); } }; export type ExtractAuthDataFromRequest = { user?: User; accessToken?: string; refreshToken?: string; authError?: Error; }; /** * Extracts meaningful auth data from the request and creates the User object * if signed in, or any auth errors if authorization based on auth data failed * Also updates refresh tokens on cookies */ export const extractAuthDataFromRequest = async ( req: Request, res: Response ): Promise => { const accessToken = req.headers ? req.headers[ACCESS_TOKEN_HEADER_NAME] : undefined; const refreshToken = req.cookies ? req.cookies[REFRESH_TOKEN_COOKIE_NAME] : undefined; if (accessToken || refreshToken) { const { user, authError, potentiallyRefreshedAccessToken, potentiallyRefreshedRefreshToken, } = await getUserFromToken({ accessToken: accessToken, refreshToken, }); setRefreshTokenCookie(potentiallyRefreshedRefreshToken, res); return { user, authError, accessToken: potentiallyRefreshedAccessToken ?? accessToken, refreshToken: potentiallyRefreshedRefreshToken ?? refreshToken, }; } return {}; }; export { default as getAdminBasicAuth } from './get-admin-basic-auth';