The legacy library can’t generate keys itself, but it can import an Ed25519 key.
We’ll generate the key pair once (with tweetnacl, a crypto library) and feed it into paseto.
1. Install dependencies
Run this once:
npm install express paseto tweetnacl dotenv
2. Create a .env file
PORT=3000
3. Create an Express Server with PASETO
Create a file named server.js:
import express from "express";
import { V2 } from "paseto";
import nacl from "tweetnacl";
import dotenv from "dotenv";
dotenv.config();
/**
* Generate an Ed25519 key pair with tweetnacl
*/
console.log("🟡 Generating Ed25519 key pair (v2.public) ...");
const keyPair = nacl.sign.keyPair(); // Uint8Arrays
const privateKey = Buffer.from(keyPair.secretKey);
const publicKey = Buffer.from(keyPair.publicKey);
console.log("✅ Keys ready, starting Express...");
const app = express();
app.use(express.json());
// issue token
app.post("/token", async (req, res) => {
try {
const payload = {
userId: req.body.userId,
role: req.body.role,
issuedAt: new Date().toISOString(),
};
const token = await V2.sign(payload, privateKey, {
issuer: "my-app",
audience: "users",
expiresIn: "1h",
});
res.json({ token });
} catch (err) {
console.error("❌ Token generation failed:", err);
res.status(500).json({ error: err.message });
}
});
// verify token
app.post("/verify", async (req, res) => {
try {
const { token } = req.body;
const payload = await V2.verify(token, publicKey, {
issuer: "my-app",
audience: "users",
});
res.json({ valid: true, payload });
} catch (err) {
console.error("❌ Verification failed:", err);
res.status(401).json({ error: "Invalid or expired token" });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`🚀 Server running on http://localhost:${PORT}`));
4. Run it
node server.js
You should now see:
🟡 Generating Ed25519 key pair (v2.public) ...
✅ Keys ready, starting Express...
🚀 Server running on http://localhost:3000
5. Test the API
You can test using Insomnia or curl:
- Generate token:
curl -X POST http://localhost:3000/token \
-H "Content-Type: application/json" \
-d '{"userId":123,"role":"admin"}'
- Verify token:
curl -X POST http://localhost:3000/verify \
-H "Content-Type: application/json" \
-d '{"token":"PASTE_YOUR_TOKEN_HERE"}'
✅ Tip: In production, you’d typically store the key pair securely (not generate it every startup). But for demo purposes, this setup is perfect for learning how PASETO works.
Github:
- Express Paseto V2 - https://github.com/codefalconx/express-paseto
- Express Paseto V3 - https://github.com/codefalconx/express-paseto-v3
- Express Paseto V4 - https://github.com/codefalconx/express-paseto-v4
Reference: https://paseto.io/


Top comments (0)