From 6dd3110bb162cc2005ed7762238c42ba5071925d Mon Sep 17 00:00:00 2001 From: Tyler Myracle Date: Thu, 18 Jan 2024 01:53:00 -0600 Subject: [PATCH] fix failing integration tests --- apps/client/pages/api/auth/[...nextauth].ts | 1 - .../app/__tests__/account.integration.spec.ts | 5 +- .../__tests__/connection.integration.spec.ts | 5 +- apps/server/src/app/__tests__/utils/axios.ts | 47 +++++++++---------- .../src/app/middleware/validate-auth-jwt.ts | 21 +++++++-- apps/server/src/env.ts | 2 - .../__tests__/finicity.integration.spec.ts | 4 +- apps/workers/src/env.ts | 2 - .../shared/src/services/crypto.service.ts | 27 ++--------- package.json | 1 + yarn.lock | 2 +- 11 files changed, 53 insertions(+), 64 deletions(-) diff --git a/apps/client/pages/api/auth/[...nextauth].ts b/apps/client/pages/api/auth/[...nextauth].ts index 3a18412a..a641f0ac 100644 --- a/apps/client/pages/api/auth/[...nextauth].ts +++ b/apps/client/pages/api/auth/[...nextauth].ts @@ -85,7 +85,6 @@ export const authOptions = { strategy: 'jwt' as SessionStrategy, maxAge: 1 * 24 * 60 * 60, // 1 Day }, - providers: [ CredentialsProvider({ name: 'Credentials', diff --git a/apps/server/src/app/__tests__/account.integration.spec.ts b/apps/server/src/app/__tests__/account.integration.spec.ts index 23b068c7..d8ebb18e 100644 --- a/apps/server/src/app/__tests__/account.integration.spec.ts +++ b/apps/server/src/app/__tests__/account.integration.spec.ts @@ -9,7 +9,6 @@ import { } from '@maybe-finance/server/features' import { InMemoryQueueFactory, PgService, type IQueueFactory } from '@maybe-finance/server/shared' import { createLogger, transports } from 'winston' -import isCI from 'is-ci' import nock from 'nock' import Decimal from 'decimal.js' import { startServer, stopServer } from './utils/server' @@ -27,7 +26,7 @@ const prisma = new PrismaClient() // For TypeScript support const plaid = jest.mocked(_plaid) // eslint-disable-line -const auth0Id = isCI ? 'auth0|61afd38f678a0c006895f046' : 'auth0|61afd340678a0c006895f000' +const authId = '__TEST_USER_ID__' let axios: AxiosInstance let user: User @@ -38,7 +37,7 @@ if (process.env.IS_VSCODE_DEBUG === 'true') { beforeEach(async () => { // Clears old user and data, creates new user - user = await resetUser(auth0Id) + user = await resetUser(authId) }) describe('/v1/accounts API', () => { diff --git a/apps/server/src/app/__tests__/connection.integration.spec.ts b/apps/server/src/app/__tests__/connection.integration.spec.ts index 747f9ee0..7eb81099 100644 --- a/apps/server/src/app/__tests__/connection.integration.spec.ts +++ b/apps/server/src/app/__tests__/connection.integration.spec.ts @@ -2,7 +2,6 @@ import type { AxiosInstance } from 'axios' import type { SharedType } from '@maybe-finance/shared' import type { Prisma, AccountConnection, AccountSyncStatus, User } from '@prisma/client' import type { ItemRemoveResponse } from 'plaid' -import isCI from 'is-ci' import { startServer, stopServer } from './utils/server' import { getAxiosClient } from './utils/axios' import prisma from '../lib/prisma' @@ -18,7 +17,7 @@ jest.mock('plaid') // For TypeScript support const plaid = jest.mocked(_plaid) -const auth0Id = isCI ? 'auth0|61afd38f678a0c006895f046' : 'auth0|61afd340678a0c006895f000' +const authId = '__TEST_USER_ID__' let axios: AxiosInstance let user: User | null let connection: AccountConnection @@ -45,7 +44,7 @@ afterAll(async () => { }) beforeEach(async () => { - user = await resetUser(auth0Id) + user = await resetUser(authId) connectionData = { data: { diff --git a/apps/server/src/app/__tests__/utils/axios.ts b/apps/server/src/app/__tests__/utils/axios.ts index e5b80ac5..1449e1a2 100644 --- a/apps/server/src/app/__tests__/utils/axios.ts +++ b/apps/server/src/app/__tests__/utils/axios.ts @@ -1,38 +1,37 @@ import type { AxiosResponse } from 'axios' import type { SharedType } from '@maybe-finance/shared' import { superjson } from '@maybe-finance/shared' -import env from '../../../env' -import isCI from 'is-ci' import Axios from 'axios' +import { encode } from 'next-auth/jwt' -// Fetches Auth0 access token (JWT) and prepares Axios client to use it on each request export async function getAxiosClient() { - const tenantUrl = isCI - ? 'REPLACE_THIS-staging.us.auth0.com' - : 'REPLACE_THIS-development.us.auth0.com' - - const { - data: { access_token: token }, - } = await Axios.request({ - method: 'POST', - url: `https://${tenantUrl}/oauth/token`, - headers: { 'content-type': 'application/json' }, - data: { - grant_type: 'password', - username: 'REPLACE_THIS', - password: 'REPLACE_THIS', - audience: 'https://maybe-finance-api/v1', - scope: '', - client_id: isCI ? 'REPLACE_THIS' : 'REPLACE_THIS', + const baseUrl = 'http://127.0.0.1:53333/v1' + const jwt = await encode({ + maxAge: 1 * 24 * 60 * 60, + secret: process.env.NEXTAUTH_SECRET || 'CHANGE_ME', + token: { + sub: '__TEST_USER_ID__', + user: '__TEST_USER_ID__', + 'https://maybe.co/email': 'REPLACE_THIS', + firstName: 'REPLACE_THIS', + lastName: 'REPLACE_THIS', + name: 'REPLACE_THIS', }, }) + const defaultHeaders = { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Credentials': true, + Authorization: `Bearer ${jwt}`, + } + const axiosOptions = { + baseURL: baseUrl, + headers: defaultHeaders, + } + const axios = Axios.create({ - baseURL: 'http://127.0.0.1:53333/v1', + ...axiosOptions, validateStatus: () => true, // Tests should determine whether status is correct, not Axios - headers: { - Authorization: `Bearer ${token}`, - }, }) axios.interceptors.response.use((response: AxiosResponse) => { diff --git a/apps/server/src/app/middleware/validate-auth-jwt.ts b/apps/server/src/app/middleware/validate-auth-jwt.ts index 5e46cec8..2124c88b 100644 --- a/apps/server/src/app/middleware/validate-auth-jwt.ts +++ b/apps/server/src/app/middleware/validate-auth-jwt.ts @@ -2,17 +2,20 @@ import cookieParser from 'cookie-parser' import { decode } from 'next-auth/jwt' const SECRET = process.env.NEXTAUTH_SECRET ?? 'REPLACE_THIS' - export const validateAuthJwt = async (req, res, next) => { cookieParser(SECRET)(req, res, async (err) => { if (err) { return res.status(500).json({ message: 'Internal Server Error' }) } - if (req.cookies && 'next-auth.session-token' in req.cookies) { + const cookieName = req.secure + ? '__Secure-next-auth.session-token' + : 'next-auth.session-token' + + if (req.cookies && cookieName in req.cookies) { try { const token = await decode({ - token: req.cookies['next-auth.session-token'], + token: req.cookies[cookieName], secret: SECRET, }) @@ -26,6 +29,18 @@ export const validateAuthJwt = async (req, res, next) => { console.error('Error in token validation', error) return res.status(500).json({ message: 'Internal Server Error' }) } + } else if (req.headers.authorization) { + const token = req.headers.authorization.split(' ')[1] + const decoded = await decode({ + token, + secret: SECRET, + }) + if (decoded) { + req.user = decoded + return next() + } else { + return res.status(401).json({ message: 'Unauthorized' }) + } } else { return res.status(401).json({ message: 'Unauthorized' }) } diff --git a/apps/server/src/env.ts b/apps/server/src/env.ts index 11fd2f76..c9040de3 100644 --- a/apps/server/src/env.ts +++ b/apps/server/src/env.ts @@ -48,8 +48,6 @@ const envSchema = z.object({ NX_SENTRY_DSN: z.string().optional(), NX_SENTRY_ENV: z.string().optional(), - NX_LD_SDK_KEY: z.string().default('REPLACE_THIS'), - NX_POLYGON_API_KEY: z.string().default(''), NX_PORT: z.string().default('3333'), diff --git a/apps/workers/src/app/__tests__/finicity.integration.spec.ts b/apps/workers/src/app/__tests__/finicity.integration.spec.ts index b56e632c..de3bac65 100644 --- a/apps/workers/src/app/__tests__/finicity.integration.spec.ts +++ b/apps/workers/src/app/__tests__/finicity.integration.spec.ts @@ -68,8 +68,8 @@ describe('Finicity', () => { userId: user.id, name: 'TEST_FINICITY', type: 'finicity', - finicityInstitutionId: 'REPLACE_THIS', - finicityInstitutionLoginId: 'REPLACE_THIS', + finicityInstitutionId: '101732', + finicityInstitutionLoginId: '6000483842', }, }) diff --git a/apps/workers/src/env.ts b/apps/workers/src/env.ts index f58963bf..ba2464b9 100644 --- a/apps/workers/src/env.ts +++ b/apps/workers/src/env.ts @@ -22,8 +22,6 @@ const envSchema = z.object({ NX_SENTRY_DSN: z.string().optional(), NX_SENTRY_ENV: z.string().optional(), - NX_LD_SDK_KEY: z.string().default('REPLACE_THIS'), - NX_REDIS_URL: z.string().default('redis://localhost:6379'), NX_POLYGON_API_KEY: z.string().default(''), diff --git a/libs/server/shared/src/services/crypto.service.ts b/libs/server/shared/src/services/crypto.service.ts index 60ce93d5..9a865325 100644 --- a/libs/server/shared/src/services/crypto.service.ts +++ b/libs/server/shared/src/services/crypto.service.ts @@ -1,4 +1,4 @@ -import crypto from 'crypto' +import CryptoJS from 'crypto-js' export interface ICryptoService { encrypt(plainText: string): string @@ -6,32 +6,13 @@ export interface ICryptoService { } export class CryptoService implements ICryptoService { - private key: Buffer - private ivLength = 16 // Initialization vector length. For AES, this is always 16 - - constructor(private readonly secret: string) { - // Ensure the key length is suitable for AES-256 - this.key = crypto.createHash('sha256').update(String(this.secret)).digest() - } + constructor(private readonly secret: string) {} encrypt(plainText: string) { - const iv = crypto.randomBytes(this.ivLength) - const cipher = crypto.createCipheriv('aes-256-cbc', this.key, iv) - let encrypted = cipher.update(plainText, 'utf8', 'hex') - encrypted += cipher.final('hex') - - // Include the IV at the start of the encrypted result - return iv.toString('hex') + ':' + encrypted + return CryptoJS.AES.encrypt(plainText, this.secret).toString() } decrypt(encrypted: string) { - const textParts = encrypted.split(':') - const iv = Buffer.from(textParts.shift()!, 'hex') - const encryptedText = textParts.join(':') - const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, iv) - let decrypted = decipher.update(encryptedText, 'hex', 'utf8') - decrypted += decipher.final('utf8') - - return decrypted + return CryptoJS.AES.decrypt(encrypted, this.secret).toString(CryptoJS.enc.Utf8) } } diff --git a/package.json b/package.json index c1328b00..0a2d297a 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "bcrypt": "^5.1.1", "bull": "^4.10.2", "classnames": "^2.3.1", + "cookie": "^0.6.0", "cookie-parser": "^1.4.6", "core-js": "^3.6.5", "cors": "^2.8.5", diff --git a/yarn.lock b/yarn.lock index 8121c329..c5943f5f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7988,7 +7988,7 @@ cookie@0.5.0, cookie@^0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cookie@0.6.0: +cookie@0.6.0, cookie@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==