mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
122 lines
3.1 KiB
TypeScript
122 lines
3.1 KiB
TypeScript
/* 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<PublicUser> => {
|
|
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<PublicUser> => {
|
|
// 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<IDataObject> => {
|
|
res.clearCookie(AUTH_COOKIE_NAME);
|
|
return {
|
|
loggedOut: true,
|
|
};
|
|
}),
|
|
);
|
|
}
|