Skip to content

Commit 0e2c0ea

Browse files
farhanW3arcoraven
andauthored
[WIP] Update: Key-Pair auth data check (#542)
* Update: Key-Pair auth data check * parse payload on verify * updated fastify hook for Auth to preValidation from onRequest --------- Co-authored-by: Phillip Ho <arcoraven@gmail.com>
1 parent 3325fad commit 0e2c0ea

File tree

1 file changed

+26
-3
lines changed

1 file changed

+26
-3
lines changed

src/server/middleware/auth.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
getToken as getJWT,
66
} from "@thirdweb-dev/auth/fastify";
77
import { AsyncWallet } from "@thirdweb-dev/wallets/evm/wallets/async";
8+
import { createHash } from "crypto";
89
import { FastifyInstance } from "fastify";
910
import { FastifyRequest } from "fastify/types/request";
1011
import jsonwebtoken from "jsonwebtoken";
@@ -102,7 +103,9 @@ export const withAuth = async (server: FastifyInstance) => {
102103
server.decorateRequest("user", null);
103104

104105
// Add auth validation middleware to check for authenticated requests
105-
server.addHook("onRequest", async (req, res) => {
106+
// Note: in the onRequest hook, request.body will always be undefined, because the body parsing happens before the preValidation hook.
107+
// https://fastify.dev/docs/latest/Reference/Hooks/#onrequest
108+
server.addHook("preValidation", async (req, res) => {
106109
let message =
107110
"Please provide a valid access token or other authentication. See: https://portal.thirdweb.com/engine/features/access-tokens";
108111

@@ -162,7 +165,7 @@ export const onRequest = async ({
162165
} else if (payload.iss === THIRDWEB_DASHBOARD_ISSUER) {
163166
return await handleDashboardAuth(jwt);
164167
} else {
165-
return await handleKeypairAuth(jwt, payload.iss);
168+
return await handleKeypairAuth(jwt, req, payload.iss);
166169
}
167170
}
168171
}
@@ -258,11 +261,14 @@ const handleWebsocketAuth = async (
258261
* Auth via keypair.
259262
* Allow a request that provides a JWT signed by an ES256 private key
260263
* matching the configured public key.
264+
* @param jwt string
261265
* @param req FastifyRequest
266+
* @param iss string
262267
* @returns AuthResponse
263268
*/
264269
const handleKeypairAuth = async (
265270
jwt: string,
271+
req: FastifyRequest,
266272
iss: string,
267273
): Promise<AuthResponse> => {
268274
// The keypair auth feature must be explicitly enabled.
@@ -279,10 +285,21 @@ const handleKeypairAuth = async (
279285
}
280286

281287
// The JWT is valid if `verify` did not throw.
282-
jsonwebtoken.verify(jwt, keypair.publicKey, {
288+
const payload = jsonwebtoken.verify(jwt, keypair.publicKey, {
283289
algorithms: [keypair.algorithm as jsonwebtoken.Algorithm],
284290
}) as jsonwebtoken.JwtPayload;
285291

292+
// If `bodyHash` is provided, it must match a hash of the POST request body.
293+
if (
294+
req.method === "POST" &&
295+
payload?.bodyHash &&
296+
payload.bodyHash !== hashRequestBody(req)
297+
) {
298+
error =
299+
"The request body does not match the hash in the access token. See: https://portal.thirdweb.com/engine/features/keypair-authentication";
300+
throw error;
301+
}
302+
286303
return { isAuthed: true };
287304
} catch (e) {
288305
if (e instanceof jsonwebtoken.TokenExpiredError) {
@@ -421,3 +438,9 @@ const handleAuthWebhooks = async (
421438

422439
return { isAuthed: false };
423440
};
441+
442+
const hashRequestBody = (req: FastifyRequest): string => {
443+
return createHash("sha256")
444+
.update(JSON.stringify(req.body), "utf8")
445+
.digest("hex");
446+
};

0 commit comments

Comments
 (0)