We live in a time of offline apps, AI-generated content, and decentralized storage. In this world, guaranteeing data authenticity is no longer optional. Here’s how you can sign data on the front-end and verify on the back-end — simply and securely.
Why Data Integrity Matters
Whether you’re syncing user-generated data, caching files locally, or fetching data from unstrusted APIs or IPFS — you’d like to have a way to tell if the data is still valid and unchanged.
Some real-world use cases:
- Digitally signed documents or contracts
- Secure exports/imports between systems
- App configurations stored locally
- Offline apps that sync later
What We Want to Achieve
- The front-end signs the data with a private key (in-memory or user secret derived)
- The back-end verifies the signature with public key (safely stored in .env or config)
- Optionally, we validate the shape of the data with JSON Schema
How to do it in JS/TS?
You can use sign-proof — a lightweight Typescript library build on Ed25519 cryptography with JSON Schema support.
npm install sign-proof
Key Storage & Generation
You need to generate Ed25519 key pair only once per user or session. This is how to handle keys securely:
import { generateKeyPair } from 'sign-proof';
const { publicKey, privateKey } = generateKeyPair();
- Private key: This should be kept secret in the browser, for example by using encrypted localStorage or a session.
- Public key: Can be securely shared with back-end for signature verification
Example: Make the key pair on first login or account registration in the browser and send the publicKey to the back-end to associate it with the user. Store the privateKey securely on the client (e.g. memory, IndexedDB, secure storage)
Front-end: Signing the Data
import { signData} from 'sign-proof';
const privateKey = '...'; // base64
// Example user input
const payload = { message: 'Hello world!', userId: 5 };
// Sign it
const proof = signData(payload, privateKey);
// Send to backend
const signed = { payload, proof};
fetch('/api/messages/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(signed),
});
Back-end: Verifying the Data
import { verifyData} from 'sign-proof';
app.post('/api/message/send', (req, res) => {
const { payload, proof}: SignedData<any> = req.body;
// Get public key from database
const publicKey = database.getPublicKey(payload.userId);
// Verify proof
const isValid = verifyData({ payload, proof }, publicKey);
if (!isValid) return res.status(400).send({ ok: false, reason: 'Invalid signature' });
// Proceed with trusted data
res.send({ ok: true });
});
Why Ed25519?
- Lightweight, speedy elliptic-curve algorithm
- No heavy libraries or key infrastructure
- Extremely small footprint — front-end-friendly
- Used by Signal, WhatsApp, and blockchain systems
Summary
It this workflow:
- Signing is performed on the front-end using the private key
- Verification is performed on the back-end using only the public key
- By separating the concerns between verify and sign, you have a secure, scalable, and tamper-proof data-validation process — across environments and devices.
Why Not Try It Yourself
NPM Package
GitHub Repo
Give it a ⭐ if you find it helpful!
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.