/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { Request, Response } from 'express'; import { IDataObject } from 'n8n-workflow'; import * as Db from '@/Db'; import * as ResponseHelper from '@/ResponseHelper'; import { AUTH_COOKIE_NAME } from '@/constants'; import { issueCookie, resolveJwt } from '../auth/jwt'; import { N8nApp, PublicUser } from '../Interfaces'; import { compareHash, sanitizeUser } from '../UserManagementHelper'; import { User } from '@db/entities/User'; import type { LoginRequest } from '@/requests'; import config from '@/config'; export function authenticationMethods(this: N8nApp): void { /** * Log in a user. * * Authless endpoint. */ this.app.post( `/${this.restEndpoint}/login`, ResponseHelper.send(async (req: LoginRequest, res: Response): Promise => { const { email, password } = req.body; if (!email) { throw new Error('Email is required to log in'); } if (!password) { throw new Error('Password is required to log in'); } let user: User | undefined; try { user = await Db.collections.User.findOne( { email }, { relations: ['globalRole'], }, ); } catch (error) { throw new Error('Unable to access database.'); } if (!user?.password || !(await compareHash(req.body.password, user.password))) { // password is empty until user signs up const error = new Error('Wrong username or password. Do you have caps lock on?'); // @ts-ignore error.httpStatusCode = 401; throw error; } await issueCookie(res, user); return sanitizeUser(user); }), ); /** * Manually check the `n8n-auth` cookie. */ this.app.get( `/${this.restEndpoint}/login`, ResponseHelper.send(async (req: Request, res: Response): Promise => { // Manually check the existing cookie. const cookieContents = req.cookies?.[AUTH_COOKIE_NAME] as string | undefined; let user: User; if (cookieContents) { // If logged in, return user try { user = await resolveJwt(cookieContents); return sanitizeUser(user); } catch (error) { res.clearCookie(AUTH_COOKIE_NAME); } } if (config.get('userManagement.isInstanceOwnerSetUp')) { throw new ResponseHelper.ResponseError('Not logged in', undefined, 401); } try { user = await Db.collections.User.findOneOrFail({ relations: ['globalRole'] }); } catch (error) { throw new ResponseHelper.ResponseError( 'No users found in database - did you wipe the users table? Create at least one user.', undefined, 500, ); } if (user.email || user.password) { throw new ResponseHelper.ResponseError( 'Invalid database state - user has password set.', undefined, 500, ); } await issueCookie(res, user); return sanitizeUser(user); }), ); /** * Log out a user. * * Authless endpoint. */ this.app.post( `/${this.restEndpoint}/logout`, ResponseHelper.send(async (_, res: Response): Promise => { res.clearCookie(AUTH_COOKIE_NAME); return { loggedOut: true, }; }), ); }