Skip to content

Commit

Permalink
api: streamline verify auth logic; allow user address override for re…
Browse files Browse the repository at this point in the history
…ad reqs (#32)
  • Loading branch information
alecananian authored May 23, 2024
1 parent c575bc4 commit 81d7ec2
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 41 deletions.
31 changes: 30 additions & 1 deletion apps/api/src/middleware/auth.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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;
});
};
23 changes: 8 additions & 15 deletions apps/api/src/routes/harvesters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand All @@ -45,6 +45,8 @@ export const harvestersRoutes =
const {
chainId,
params: { id },
userAddress: authUserAddress,
overrideUserAddress,
} = req;

const harvesterAddress = id as AddressString;
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
23 changes: 13 additions & 10 deletions apps/api/src/routes/transactions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { FastifyPluginAsync } from "fastify";

import "../middleware/auth";
import "../middleware/chain";
import "../middleware/project";
import "../middleware/swagger";
Expand All @@ -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;
Expand All @@ -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(
Expand All @@ -56,7 +59,7 @@ export const transactionsRoutes =
backendWallet,
body,
false,
authResult.parsedJWT.sub,
userAddress,
);
reply.send(result);
} catch (err) {
Expand Down
26 changes: 11 additions & 15 deletions apps/api/src/routes/users.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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;
Expand All @@ -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,
}),
]);
Expand All @@ -58,7 +54,7 @@ export const usersRoutes =
return reply.code(401).send({ error: "Unauthorized" });
}

return {
reply.send({
...dbUser,
allActiveSigners: allActiveSigners.map((activeSigner) => ({
...activeSigner,
Expand All @@ -70,7 +66,7 @@ export const usersRoutes =
startTimestamp: activeSigner.startTimestamp.toString(),
endTimestamp: activeSigner.endTimestamp.toString(),
})),
};
});
},
);
};

0 comments on commit 81d7ec2

Please sign in to comment.