Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 43 additions & 3 deletions apps/bazaar/api/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/

import { cors } from '@elysiajs/cors'
import { type WalletSignatureConfig, validateWalletSignatureFromHeaders } from '@jejunetwork/api'
import {
CORE_PORTS,
getCoreAppUrl,
Expand Down Expand Up @@ -72,6 +73,40 @@ const RATE_LIMITS = {
tfmm: { maxRequests: 20, windowMs: 60_000 }, // 20 req/min
} as const

const walletSignatureConfig: WalletSignatureConfig = {
validityWindowMs: 5 * 60 * 1000,
}

async function requireWalletOwner(
request: Request,
): Promise<`0x${string}`> {
const addressHeader =
request.headers.get('x-jeju-address') ??
request.headers.get('x-wallet-address')
if (!addressHeader) {
throw new Error('Authentication required: x-wallet-address missing')
}

if (getCurrentNetwork() === 'localnet') {
return addressHeader as `0x${string}`
}

const signatureResult = await validateWalletSignatureFromHeaders(
{
'x-jeju-address': addressHeader,
'x-jeju-timestamp': request.headers.get('x-jeju-timestamp') ?? undefined,
'x-jeju-signature': request.headers.get('x-jeju-signature') ?? undefined,
},
walletSignatureConfig,
)

if (!signatureResult.valid || !signatureResult.user?.address) {
throw new Error(signatureResult.error ?? 'Authentication required')
}

return signatureResult.user.address as `0x${string}`
}

function checkRateLimit(
clientId: string,
endpoint: keyof typeof RATE_LIMITS,
Expand Down Expand Up @@ -768,12 +803,17 @@ export function createBazaarApp(env?: Partial<BazaarEnv>) {
})
.post('/', async ({ body, request }) => {
// Security: Validate request has wallet signature header for write operations
const walletAddress = request.headers.get('x-wallet-address')
if (!walletAddress) {
let walletAddress: `0x${string}`
try {
walletAddress = await requireWalletOwner(request)
} catch (error) {
return new Response(
JSON.stringify({
error: 'Authentication required',
message: 'x-wallet-address header required for write operations',
message:
error instanceof Error
? error.message
: 'Authentication required',
}),
{ status: 401, headers: { 'Content-Type': 'application/json' } },
)
Expand Down