mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-12 19:29:15 +00:00
Refactoring functions into services, helper functions, hooks, patch bugs
This commit is contained in:
@@ -269,19 +269,36 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
||||
secure: NODE_ENV === 'production' ? true : false
|
||||
});
|
||||
|
||||
interface VerifyMfaTokenRes {
|
||||
encryptionVersion: number;
|
||||
protectedKey?: string;
|
||||
protectedKeyIV?: string;
|
||||
protectedKeyTag?: string;
|
||||
token: string;
|
||||
publicKey: string;
|
||||
encryptedPrivateKey: string;
|
||||
iv: string;
|
||||
tag: string;
|
||||
}
|
||||
|
||||
const resObj: VerifyMfaTokenRes = {
|
||||
encryptionVersion: user.encryptionVersion,
|
||||
token: tokens.token,
|
||||
publicKey: user.publicKey as string,
|
||||
encryptedPrivateKey: user.encryptedPrivateKey as string,
|
||||
iv: user.iv as string,
|
||||
tag: user.tag as string
|
||||
}
|
||||
|
||||
if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) {
|
||||
resObj.protectedKey = user.protectedKey;
|
||||
resObj.protectedKeyIV = user.protectedKeyIV;
|
||||
resObj.protectedKeyTag = user.protectedKeyTag;
|
||||
}
|
||||
|
||||
// case: user does not have MFA enabled
|
||||
// return (access) token in response
|
||||
return res.status(200).send({
|
||||
encryptionVersion: user.encryptionVersion,
|
||||
protectedKey: user.protectedKey ?? null,
|
||||
protectedKeyIV: user.protectedKeyIV ?? null,
|
||||
protectedKeyTag: user.protectedKeyTag ?? null,
|
||||
token: tokens.token,
|
||||
publicKey: user.publicKey,
|
||||
encryptedPrivateKey: user.encryptedPrivateKey,
|
||||
iv: user.iv,
|
||||
tag: user.tag
|
||||
});
|
||||
return res.status(200).send(resObj);
|
||||
} catch (err) {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(err);
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from '../../helpers/signup';
|
||||
import { issueAuthTokens } from '../../helpers/auth';
|
||||
import { INVITED, ACCEPTED } from '../../variables';
|
||||
import { NODE_ENV } from '../../config';
|
||||
import axios from 'axios';
|
||||
|
||||
/**
|
||||
@@ -105,7 +106,6 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
token = tokens.token;
|
||||
refreshToken = tokens.refreshToken;
|
||||
|
||||
// sending a welcome email to new users
|
||||
if (process.env.LOOPS_API_KEY) {
|
||||
@@ -121,6 +121,14 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// store (refresh) token in httpOnly cookie
|
||||
res.cookie('jid', tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: 'strict',
|
||||
secure: NODE_ENV === 'production' ? true : false
|
||||
});
|
||||
} catch (err) {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(err);
|
||||
@@ -132,8 +140,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
return res.status(200).send({
|
||||
message: 'Successfully set up account',
|
||||
user,
|
||||
token,
|
||||
refreshToken
|
||||
token
|
||||
});
|
||||
};
|
||||
|
||||
@@ -219,7 +226,14 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
token = tokens.token;
|
||||
refreshToken = tokens.refreshToken;
|
||||
|
||||
// store (refresh) token in httpOnly cookie
|
||||
res.cookie('jid', tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: 'strict',
|
||||
secure: NODE_ENV === 'production' ? true : false
|
||||
});
|
||||
} catch (err) {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(err);
|
||||
@@ -227,11 +241,10 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
|
||||
message: 'Failed to complete account setup'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully set up account',
|
||||
user,
|
||||
token,
|
||||
refreshToken
|
||||
token
|
||||
});
|
||||
};
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import {
|
||||
SALT_ROUNDS
|
||||
} from '../config';
|
||||
import { UnauthorizedRequestError } from '../utils/errors';
|
||||
import { ForbiddenRequestError } from '../utils/errors';
|
||||
|
||||
/**
|
||||
* Create and store a token in the database for purpose [type]
|
||||
@@ -65,29 +65,45 @@ const createTokenHelper = async ({
|
||||
break;
|
||||
}
|
||||
|
||||
interface Query {
|
||||
interface TokenDataQuery {
|
||||
type: string;
|
||||
email?: string;
|
||||
phoneNumber?: string;
|
||||
organization?: Types.ObjectId;
|
||||
}
|
||||
|
||||
interface TokenDataUpdate {
|
||||
type: string;
|
||||
email?: string;
|
||||
phoneNumber?: string;
|
||||
organization?: Types.ObjectId;
|
||||
tokenHash: string;
|
||||
expiresAt: Date;
|
||||
}
|
||||
|
||||
const query: Query = { type };
|
||||
const query: TokenDataQuery = { type };
|
||||
const update: TokenDataUpdate = {
|
||||
type,
|
||||
tokenHash: await bcrypt.hash(token, SALT_ROUNDS),
|
||||
expiresAt
|
||||
}
|
||||
|
||||
if (email) { query.email = email; }
|
||||
if (phoneNumber) { query.phoneNumber = phoneNumber; }
|
||||
if (organizationId) { query.organization = organizationId }
|
||||
if (email) {
|
||||
query.email = email;
|
||||
update.email = email;
|
||||
}
|
||||
if (phoneNumber) {
|
||||
query.phoneNumber = phoneNumber;
|
||||
update.phoneNumber = phoneNumber;
|
||||
}
|
||||
if (organizationId) {
|
||||
query.organization = organizationId
|
||||
update.organization = organizationId
|
||||
}
|
||||
|
||||
await TokenData.findOneAndUpdate(
|
||||
query,
|
||||
{
|
||||
type,
|
||||
email,
|
||||
phoneNumber,
|
||||
organization: organizationId,
|
||||
tokenHash: await bcrypt.hash(token, SALT_ROUNDS),
|
||||
expiresAt
|
||||
},
|
||||
update,
|
||||
{
|
||||
new: true,
|
||||
upsert: true
|
||||
@@ -123,37 +139,38 @@ const validateTokenHelper = async ({
|
||||
organizationId?: Types.ObjectId;
|
||||
token: string;
|
||||
}) => {
|
||||
try {
|
||||
interface Query {
|
||||
type: string;
|
||||
email?: string;
|
||||
phoneNumber?: string;
|
||||
organization?: Types.ObjectId;
|
||||
}
|
||||
interface Query {
|
||||
type: string;
|
||||
email?: string;
|
||||
phoneNumber?: string;
|
||||
organization?: Types.ObjectId;
|
||||
}
|
||||
|
||||
const query: Query = { type };
|
||||
const query: Query = { type };
|
||||
|
||||
if (email) { query.email = email; }
|
||||
if (phoneNumber) { query.phoneNumber = phoneNumber; }
|
||||
if (organizationId) { query.organization = organizationId; }
|
||||
if (email) { query.email = email; }
|
||||
if (phoneNumber) { query.phoneNumber = phoneNumber; }
|
||||
if (organizationId) { query.organization = organizationId; }
|
||||
|
||||
const tokenData = await TokenData.findOneAndDelete(query);
|
||||
|
||||
if (!tokenData) throw new Error('Failed to find token to validate');
|
||||
|
||||
if (tokenData.expiresAt < new Date()) throw new Error('Token has expired');
|
||||
|
||||
const isValid = await bcrypt.compare(token, tokenData.tokenHash);
|
||||
if (!isValid) throw UnauthorizedRequestError({
|
||||
const tokenData = await TokenData.findOne(query).select('+tokenHash');
|
||||
|
||||
if (!tokenData) throw new Error('Failed to find token to validate');
|
||||
|
||||
if (tokenData.expiresAt < new Date()) {
|
||||
await TokenData.findByIdAndDelete(tokenData._id);
|
||||
throw ForbiddenRequestError({
|
||||
message: 'Failed token data validation due to token is no longer valid'
|
||||
});
|
||||
}
|
||||
|
||||
const isValid = await bcrypt.compare(token, tokenData.tokenHash);
|
||||
if (!isValid) {
|
||||
throw ForbiddenRequestError({
|
||||
message: 'Failed token data validation due to incorrect token'
|
||||
});
|
||||
} catch (err) {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(err);
|
||||
throw new Error(
|
||||
"Failed to validate token data"
|
||||
);
|
||||
}
|
||||
|
||||
await TokenData.findByIdAndDelete(tokenData._id);
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { ErrorRequestHandler } from "express";
|
||||
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { InternalServerError } from "../utils/errors";
|
||||
import { InternalServerError, UnauthorizedRequestError } from "../utils/errors";
|
||||
import { getLogger } from "../utils/logger";
|
||||
import RequestError, { LogLevel } from "../utils/requestError";
|
||||
import { NODE_ENV } from "../config";
|
||||
|
||||
import { TokenExpiredError } from 'jsonwebtoken';
|
||||
|
||||
export const requestErrorHandler: ErrorRequestHandler = (error: RequestError | Error, req, res, next) => {
|
||||
if (res.headersSent) return next();
|
||||
@@ -16,7 +17,9 @@ export const requestErrorHandler: ErrorRequestHandler = (error: RequestError | E
|
||||
}
|
||||
|
||||
//TODO: Find better way to type check for error. In current setting you need to cast type to get the functions and variables from RequestError
|
||||
if (!(error instanceof RequestError)) {
|
||||
if (error instanceof TokenExpiredError) {
|
||||
error = UnauthorizedRequestError({ stack: error.stack, message: 'Token expired' });
|
||||
} else if (!(error instanceof RequestError)) {
|
||||
error = InternalServerError({ context: { exception: error.message }, stack: error.stack })
|
||||
getLogger('backend-main').log((<RequestError>error).levelName.toLowerCase(), (<RequestError>error).message)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user