diff --git a/apps/api/src/middleware/auth.ts b/apps/api/src/middleware/auth.ts index bbd4df6c..1c03dacb 100644 --- a/apps/api/src/middleware/auth.ts +++ b/apps/api/src/middleware/auth.ts @@ -1,14 +1,24 @@ import type { Prisma } from "@prisma/client"; import { PrivateKeyWallet } from "@thirdweb-dev/auth/evm"; import { ThirdwebAuth } from "@thirdweb-dev/auth/fastify"; +import type { AddressString } from "@treasure-dev/tdk-core"; import type { FastifyInstance } from "fastify"; import type { TdkApiContext } from "../types"; +import { verifyAuth } from "../utils/auth"; import { fetchEmbeddedWalletUser } from "../utils/embeddedWalletApi"; +declare module "fastify" { + interface FastifyRequest { + userAddress: AddressString | undefined; + overrideUserAddress: AddressString | undefined; + authError: string | undefined; + } +} + export const withAuth = async ( app: FastifyInstance, - { env, db, engine }: TdkApiContext, + { env, db, auth, engine }: TdkApiContext, ) => { const { authRouter, authMiddleware } = ThirdwebAuth({ domain: env.THIRDWEB_AUTH_DOMAIN, @@ -106,4 +116,23 @@ export const withAuth = async ( // We add the auth middleware to our app to let us access the user across our API app.register(authMiddleware); + + // Parse JWT header and obtain user address in middleware + app.decorateRequest("userAddress", undefined); + app.decorateRequest("overrideUserAddress", undefined); + app.decorateRequest("authError", undefined); + app.addHook("onRequest", async (req) => { + if (req.headers.authorization) { + const authResult = await verifyAuth(auth, req); + if (authResult.valid) { + req.userAddress = authResult.parsedJWT.sub as AddressString; + } else { + req.authError = authResult.error; + } + } + + req.overrideUserAddress = req.headers["x-account-address"]?.toString() as + | AddressString + | undefined; + }); }; diff --git a/apps/api/src/routes/harvesters.ts b/apps/api/src/routes/harvesters.ts index dd1e0ea7..9817aedf 100644 --- a/apps/api/src/routes/harvesters.ts +++ b/apps/api/src/routes/harvesters.ts @@ -7,6 +7,7 @@ import { import type { FastifyPluginAsync } from "fastify"; import { zeroAddress } from "viem"; +import "../middleware/auth"; import "../middleware/chain"; import "../middleware/swagger"; import type { @@ -21,10 +22,9 @@ import { readHarvesterReplySchema, } from "../schema"; import type { TdkApiContext } from "../types"; -import { verifyAuth } from "../utils/auth"; export const harvestersRoutes = - ({ env, auth, wagmiConfig }: TdkApiContext): FastifyPluginAsync => + ({ env, wagmiConfig }: TdkApiContext): FastifyPluginAsync => async (app) => { app.get<{ Params: ReadHarvesterParams; @@ -45,6 +45,8 @@ export const harvestersRoutes = const { chainId, params: { id }, + userAddress: authUserAddress, + overrideUserAddress, } = req; const harvesterAddress = id as AddressString; @@ -58,12 +60,7 @@ export const harvestersRoutes = return reply.code(404).send({ error: "Not found" }); } - // User address is optional for this request - const authResult = await verifyAuth(auth, req); - const userAddress = authResult.valid - ? (authResult.parsedJWT.sub as AddressString) - : undefined; - + const userAddress = overrideUserAddress ?? authUserAddress; const harvesterUserInfo = userAddress ? await getHarvesterUserInfo({ chainId, @@ -101,19 +98,15 @@ export const harvestersRoutes = const { chainId, params: { id }, + userAddress: authUserAddress, + overrideUserAddress, } = req; - // User address is optional for this request - const authResult = await verifyAuth(auth, req); - const userAddress = authResult.valid - ? (authResult.parsedJWT.sub as AddressString) - : undefined; - const harvesterCorruptionRemovalInfo = await fetchHarvesterCorruptionRemovalInfo({ chainId, harvesterAddress: id, - userAddress, + userAddress: overrideUserAddress ?? authUserAddress, inventoryApiUrl: env.TROVE_API_URL, inventoryApiKey: env.TROVE_API_KEY, wagmiConfig, diff --git a/apps/api/src/routes/transactions.ts b/apps/api/src/routes/transactions.ts index b9874281..ee7489a1 100644 --- a/apps/api/src/routes/transactions.ts +++ b/apps/api/src/routes/transactions.ts @@ -1,5 +1,6 @@ import type { FastifyPluginAsync } from "fastify"; +import "../middleware/auth"; import "../middleware/chain"; import "../middleware/project"; import "../middleware/swagger"; @@ -14,10 +15,9 @@ import { readTransactionReplySchema, } from "../schema"; import type { TdkApiContext } from "../types"; -import { verifyAuth } from "../utils/auth"; export const transactionsRoutes = - ({ auth, engine }: TdkApiContext): FastifyPluginAsync => + ({ engine }: TdkApiContext): FastifyPluginAsync => async (app) => { app.post<{ Body: CreateTransactionBody; @@ -36,18 +36,21 @@ export const transactionsRoutes = }, }, async (req, reply) => { - const authResult = await verifyAuth(auth, req); - if (!authResult.valid) { + const { + chainId, + backendWallet, + userAddress, + authError, + body: postBody, + } = req; + if (!userAddress) { console.error( "Error authenticating user for transaction create:", - authResult.error, + authError, ); - return reply - .code(401) - .send({ error: `Unauthorized: ${authResult.error}` }); + return reply.code(401).send({ error: `Unauthorized: ${authError}` }); } - const { chainId, backendWallet, body: postBody } = req; const { address, ...body } = postBody; try { const { result } = await engine.contract.write( @@ -56,7 +59,7 @@ export const transactionsRoutes = backendWallet, body, false, - authResult.parsedJWT.sub, + userAddress, ); reply.send(result); } catch (err) { diff --git a/apps/api/src/routes/users.ts b/apps/api/src/routes/users.ts index 43681a7f..b43f1d31 100644 --- a/apps/api/src/routes/users.ts +++ b/apps/api/src/routes/users.ts @@ -1,6 +1,7 @@ import { getAllActiveSigners } from "@treasure-dev/tdk-core"; import type { FastifyPluginAsync } from "fastify"; +import "../middleware/auth"; import "../middleware/chain"; import "../middleware/swagger"; import { @@ -9,10 +10,9 @@ import { readCurrentUserReplySchema, } from "../schema"; import type { TdkApiContext } from "../types"; -import { verifyAuth } from "../utils/auth"; export const usersRoutes = - ({ db, auth, wagmiConfig }: TdkApiContext): FastifyPluginAsync => + ({ db, wagmiConfig }: TdkApiContext): FastifyPluginAsync => async (app) => { app.get<{ Reply: ReadCurrentUserReply | ErrorReply; @@ -29,27 +29,23 @@ export const usersRoutes = }, }, async (req, reply) => { - const authResult = await verifyAuth(auth, req); - if (!authResult.valid) { + const { chainId, userAddress, authError } = req; + if (!userAddress) { console.error( "Error authenticating user for user details read:", - authResult.error, + authError, ); - return reply - .code(401) - .send({ error: `Unauthorized: ${authResult.error}` }); + return reply.code(401).send({ error: `Unauthorized: ${authError}` }); } - const smartAccountAddress = authResult.parsedJWT.sub; - const [dbUser, allActiveSigners] = await Promise.all([ db.user.findUnique({ - where: { smartAccountAddress }, + where: { smartAccountAddress: userAddress }, select: { id: true, smartAccountAddress: true, email: true }, }), getAllActiveSigners({ - chainId: req.chainId, - address: smartAccountAddress, + chainId, + address: userAddress, wagmiConfig, }), ]); @@ -58,7 +54,7 @@ export const usersRoutes = return reply.code(401).send({ error: "Unauthorized" }); } - return { + reply.send({ ...dbUser, allActiveSigners: allActiveSigners.map((activeSigner) => ({ ...activeSigner, @@ -70,7 +66,7 @@ export const usersRoutes = startTimestamp: activeSigner.startTimestamp.toString(), endTimestamp: activeSigner.endTimestamp.toString(), })), - }; + }); }, ); };