From d283cc2d26033e2231f6ebc5d2d820f49ecf92c9 Mon Sep 17 00:00:00 2001 From: Catalin Pit <25515812+catalinpit@users.noreply.github.com> Date: Thu, 21 Dec 2023 16:02:02 +0200 Subject: [PATCH] chore: implemented feedback --- apps/web/src/pages/api/v1/[...ts-rest].tsx | 18 +++++---------- packages/lib/server-only/auth/hash.ts | 5 ++++ .../public-api/create-api-token.ts | 23 ++++++++++--------- .../public-api/get-user-by-token.ts | 10 +++++--- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/apps/web/src/pages/api/v1/[...ts-rest].tsx b/apps/web/src/pages/api/v1/[...ts-rest].tsx index 1c645a3ea..0b22d97c6 100644 --- a/apps/web/src/pages/api/v1/[...ts-rest].tsx +++ b/apps/web/src/pages/api/v1/[...ts-rest].tsx @@ -1,12 +1,10 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; - import { upsertDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta'; import { deleteDocument } from '@documenso/lib/server-only/document/delete-document'; import { findDocuments } from '@documenso/lib/server-only/document/find-documents'; import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id'; import { sendDocument } from '@documenso/lib/server-only/document/send-document'; import { setFieldsForDocument } from '@documenso/lib/server-only/field/set-fields-for-document'; -import { checkUserFromToken } from '@documenso/lib/server-only/public-api/get-user-by-token'; +import { getUserByApiToken } from '@documenso/lib/server-only/public-api/get-user-by-token'; import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/set-recipients-for-document'; import { getPresignPostUrl } from '@documenso/lib/universal/upload/server-actions'; import { contract } from '@documenso/trpc/api-contract/contract'; @@ -20,7 +18,7 @@ const router = createNextRoute(contract, { let user; try { - user = await checkUserFromToken({ token: authorization }); + user = await getUserByApiToken({ token: authorization }); } catch (e) { return { status: 401, @@ -46,7 +44,7 @@ const router = createNextRoute(contract, { let user; try { - user = await checkUserFromToken({ token: authorization }); + user = await getUserByApiToken({ token: authorization }); } catch (e) { return { status: 401, @@ -79,7 +77,7 @@ const router = createNextRoute(contract, { let user; try { - user = await checkUserFromToken({ token: authorization }); + user = await getUserByApiToken({ token: authorization }); } catch (e) { return { status: 401, @@ -140,7 +138,7 @@ const router = createNextRoute(contract, { let user; try { - user = await checkUserFromToken({ token: authorization }); + user = await getUserByApiToken({ token: authorization }); } catch (e) { return { status: 401, @@ -226,8 +224,4 @@ const router = createNextRoute(contract, { }, }); -const nextRouter = createNextRouter(contract, router); - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - await nextRouter(req, res); -} +export default createNextRouter(contract, router); diff --git a/packages/lib/server-only/auth/hash.ts b/packages/lib/server-only/auth/hash.ts index df9931c97..bb0b760fe 100644 --- a/packages/lib/server-only/auth/hash.ts +++ b/packages/lib/server-only/auth/hash.ts @@ -1,4 +1,5 @@ import { compareSync as bcryptCompareSync, hashSync as bcryptHashSync } from 'bcrypt'; +import crypto from 'crypto'; import { SALT_ROUNDS } from '../../constants/auth'; @@ -12,3 +13,7 @@ export const hashSync = (password: string) => { export const compareSync = (password: string, hash: string) => { return bcryptCompareSync(password, hash); }; + +export const hashString = (input: string) => { + return crypto.createHash('sha512').update(input).digest('hex'); +}; diff --git a/packages/lib/server-only/public-api/create-api-token.ts b/packages/lib/server-only/public-api/create-api-token.ts index 645e9fb1b..9fe74c1f6 100644 --- a/packages/lib/server-only/public-api/create-api-token.ts +++ b/packages/lib/server-only/public-api/create-api-token.ts @@ -1,9 +1,9 @@ -import crypto from 'crypto'; - import { prisma } from '@documenso/prisma'; // temporary choice for testing only import { ONE_YEAR } from '../../constants/time'; +import { alphaid } from '../../universal/id'; +import { hashString } from '../auth/hash'; type CreateApiTokenInput = { userId: number; @@ -11,24 +11,25 @@ type CreateApiTokenInput = { }; export const createApiToken = async ({ userId, tokenName }: CreateApiTokenInput) => { - // quick implementation for testing; it needs double checking - const tokenHash = crypto - .createHash('sha512') - .update(crypto.randomBytes(32).toString('hex')) - .digest('hex'); + const apiToken = `api_${alphaid(16)}`; - const token = await prisma.apiToken.create({ + const hashedToken = hashString(apiToken); + + const dbToken = await prisma.apiToken.create({ data: { - token: tokenHash, + token: hashedToken, name: tokenName, userId, expires: new Date(Date.now() + ONE_YEAR), }, }); - if (!token) { + if (!dbToken) { throw new Error(`Failed to create the API token`); } - return token; + return { + id: dbToken.id, + token: apiToken, + }; }; diff --git a/packages/lib/server-only/public-api/get-user-by-token.ts b/packages/lib/server-only/public-api/get-user-by-token.ts index 5e696521c..3377b5aa8 100644 --- a/packages/lib/server-only/public-api/get-user-by-token.ts +++ b/packages/lib/server-only/public-api/get-user-by-token.ts @@ -1,11 +1,15 @@ import { prisma } from '@documenso/prisma'; -export const checkUserFromToken = async ({ token }: { token: string }) => { +import { hashString } from '../auth/hash'; + +export const getUserByApiToken = async ({ token }: { token: string }) => { + const hashedToken = hashString(token); + const user = await prisma.user.findFirst({ where: { ApiToken: { some: { - token: token, + token: hashedToken, }, }, }, @@ -18,7 +22,7 @@ export const checkUserFromToken = async ({ token }: { token: string }) => { throw new Error('Invalid token'); } - const tokenObject = user.ApiToken.find((apiToken) => apiToken.token === token); + const tokenObject = user.ApiToken.find((apiToken) => apiToken.token === hashedToken); if (!tokenObject || new Date(tokenObject.expires) < new Date()) { throw new Error('Expired token');