pow-middleware
v0.1.2
Published
A middleware for Node.js HTTP request and proof of work handling
Readme
POW Middleware
A middleware for Node.js HTTP request and proof of work handling
Installation
npm install pow-middlewareUsage
import express from 'express'
import powMiddleware from 'pow-middleware'
const app = express()
app.use(powMiddleware())Custom options
app.use(
powMiddleware({
secret: 'my-secret-key',
difficulty: 20,
trustProxy: true,
ttl: 5 * 60 * 1_000, // 5 minutes
powHeaderName: 'x-custom-pow',
powChallengeHeaderName: 'x-custom-pow-challenge',
})
)Solving challenges in browser
Here's a complete example showing how to handle PoW challenges with fetch:
// Step 1: Initial request (will fail with 403)
let response = await fetch(url)
if (response.status === 403) {
const error = await response.json()
console.log('Request blocked:', error) // { error: 'pow-required' }
// Step 2: Extract challenge from response headers
const challengeHeader = response.headers.get('x-pow-challenge')
if (!challengeHeader) {
throw new Error('No challenge provided')
}
const challenge = JSON.parse(challengeHeader)
console.log('Challenge received:', challenge)
// Step 3: Solve the challenge
const { hash, solvedChallenge } = await solveChallenge(challenge)
console.log('Challenge solved! Nonce:', solvedChallenge.nonce)
// Step 4: Retry request with PoW headers
response = await fetch(url, {
headers: {
'x-pow': hash,
'x-pow-challenge': JSON.stringify(solvedChallenge),
},
})
}// Helper function to solve the challenge
async function solveChallenge(challenge: any) {
let nonce = 0
let hash: string
while (true) {
challenge.nonce = nonce
hash = await computePoWHash(challenge)
if (checkLeadingZeroBits(hash, challenge.difficulty)) {
return { hash, solvedChallenge: challenge }
}
nonce++
}
throw new Error('Failed to solve challenge')
}
// SHA-256 hash function for browser
async function sha256(data: string): Promise<string> {
const encoder = new TextEncoder()
const buffer = await crypto.subtle.digest('SHA-256', encoder.encode(data))
return Array.from(new Uint8Array(buffer))
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
}
// Compute PoW hash
async function computePoWHash(challenge: any): Promise<string> {
const seed = `${challenge.id}:${challenge.ts}:${challenge.difficulty}:${challenge.req}:${challenge.signature}:${challenge.nonce}`
return await sha256(seed)
}
// Check if hash has required leading zero bits
function checkLeadingZeroBits(hex: string, zeroBitsCount: number): boolean {
// Convert hex string to bytes
const bytes = new Uint8Array(hex.length / 2)
for (let i = 0; i < bytes.length; i++) {
bytes[i] = parseInt(hex.substring(i * 2, i * 2 + 2), 16)
}
// Check if first zeroBitsCount bits are 0
for (let bitIndex = 0; bitIndex < zeroBitsCount; bitIndex++) {
const byteIndex = Math.floor(bitIndex / 8)
if (byteIndex >= bytes.length) return false
const bitPosition = bitIndex % 8
const mask = 1 << (7 - bitPosition)
if ((bytes[byteIndex] & mask) !== 0) {
return false
}
}
return true
}