1.0.2 • Published 2 years ago

@vantezzen/pow v1.0.2

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

pow - A simple Proof-of-Work implementation for web apps

This package provides a simple Proof-of-Work implementation for web apps. It is designed to abstract away the complexity of Proof-of-Work and provide a simple interface to use it in your web app to verify users and prevent spam.

The library is unopinionated about what Frontend and Backend frameworks you use and how you transfer data between them.

Installation

npm install @vantezzen/pow

Example

A fully client-side demo can be accessed at https://vantezzen.github.io/pow/.

The example implementation can be found in the demo folder.

Usage

This library contains two main classes: PowServer should run in a secure environment (e.g. your NodeJS backend) and PowClient should run in your client (e.g. a React frontend).

Your PowServer contains a secret key that is used to generate and verify challenges. The PowClient can then take a challenge generated by the server to solve.

Both the server and client are written to be environment-agnositic and can be used inside NodeJS and the browser.

Server

import { PowServer, PowCrypto } from "@vantezzen/pow";

// You can use any secret key you want or use `PowCrypto` to generate a random key instead
const powCrypto = new PowCrypto();
const secret = await powCrypto.generateSecret();

// Please note that the secret key must be the same between generating a challenge and verifying it.
// If you use multiple servers, you should use a shared secret key for all of them.

// Create a new server instance
const server = new PowServer(secret);

// Generate a challenge. This is just an encrypted string that you can send to the client.
const challenge = await server.generateChallenge();

// You should now send the challenge string over to the client using your preferred method.
// In this example, the challenge is sent using socket.io
socket.emit("challenge", challenge, async (nonce) => {
  // We now received a nonce from the client and can verify it.
  const result = await powServer.verifyProofOfWork(challenge, nonce);

  // The result is a `PowVerifyResult` object that contains the result of the verification.
  if (result.isValid) {
    // The nonce is valid and the user can be verified
  } else {
    // The nonce is invalid and the user should be rejected
    console.log("Error", result.error);
  }
});

Client

import { PowClient } from "@vantezzen/pow";

// Create a new client instance
const client = new PowClient();

// Again, the challenge can be transferred to the client using any method you want.
// In this example, the challenge is received using socket.io
socket.on("challenge", async (challenge, callback) => {
  // We now received a challenge from the server and can solve it.
  try {
    const nonce = await powClient.solveChallenge(challenge);

    // We now have a nonce that we can send back to the server.
    // In this example, the nonce is sent using socket.io
    callback(nonce);
  } catch (error) {
    // The challenge could not be solved. This is most likely because the proof of work took to long and timed out.
    console.log("Error", error);
  }
});

Changing the difficulty

By default, the proof of work difficulty is set to 4. This means that the hash of the challenge and nonce must start with 0000 to be considered valid. On average, it takes 1 second to solve a challenge with difficulty 4 on a modern computer.

If you want to increase the difficulty, you can do so by passing a higher difficulty to the PowServer and PowClient constructor. You should always consider updating the validity option of the PowServer as well as the timeout option of the PowClient to prevent the challenge from being discarded if the client takes too long. The difficulty needs to be the same on the server and client.

const server = new PowServer(secret, {
  difficulty: 6,
  validity: 30000,
});

const client = new PowClient({
  difficulty: 6,
  timeout: 30000,
});

API

PowServer

constructor(secret: string, options?: PowServerOptions)

Creates a new PowServer instance.

  • secret - The secret key that is used to generate and verify challenges. This should be a random string of at least 32 characters.
  • options - Optional options for the server.
    • difficulty - The difficulty of the proof of work (This is the number of leading zeros that the hash of the challenge and nonce must have). Defaults to 4.
    • validity - The validity of the proof of work challenge in milliseconds. Defaults to 15000 (15 seconds). If the client takes longer than this to solve the challenge, the challenge will be considered invalid to prevent replay attacks.

generateChallenge(): Promise<string>

Generates a new challenge.

verifyProofOfWork(challenge: string, nonce: string): Promise<PowVerifyResult>

Verifies a proof of work challenge.

  • challenge - The challenge that was generated by the server.
  • nonce - The nonce that was generated by the client.

PowClient

constructor(options?: PowClientOptions)

Creates a new PowClient instance.

  • options - Optional options for the client.
    • difficulty - The difficulty of the proof of work (This is the number of leading zeros that the hash of the challenge and nonce must have). Defaults to 4.
    • timeout - Time in milliseconds after which the proof of work should be stopped. Defaults to 10000 (10 seconds). If the client takes longer than this to solve the challenge, the challenge will be considered too difficult.

solveChallenge(challenge: string): Promise<string>

Solves a challenge.

  • challenge - The challenge that was generated by the server.

PowCrypto

generateSecret(): Promise<string>

Generates a new random secret key.

PowVerifyResult

isValid: boolean

Whether the proof of work is valid.

error?: string

The error message if the proof of work is invalid.

License

MIT