Skip to content

Instantly share code, notes, and snippets.

@lazuee
Created July 12, 2025 06:24
Show Gist options
  • Save lazuee/465a7de656708079ed3c2f40a853ff24 to your computer and use it in GitHub Desktop.
Save lazuee/465a7de656708079ed3c2f40a853ff24 to your computer and use it in GitHub Desktop.
Secret Token
import { Buffer } from "node:buffer";
import crypto from "node:crypto";
type Payload = {
ts: number;
salt: string;
};
type EncodedPayload = {
iv: string;
data: string;
mac: string;
};
export class SecureToken {
private secretKey: Buffer;
private timestampToleranceMs: number;
private noiseLength = 12;
constructor(
secret: string = "my-super-secret",
timestampToleranceMs = 5 * 60 * 1000,
) {
this.secretKey = crypto.createHash("sha256").update(secret).digest(); // 32 bytes
this.timestampToleranceMs = timestampToleranceMs;
}
private addNoise(base64: string): string {
const noise = crypto
.randomBytes(this.noiseLength)
.toString("hex")
.slice(0, this.noiseLength);
const mid = Math.floor(base64.length / 2);
return base64.slice(0, mid) + noise + base64.slice(mid);
}
private removeNoise(noisy: string): string {
const mid = Math.floor(noisy.length / 2);
const halfNoise = Math.floor(this.noiseLength / 2);
return noisy.slice(0, mid - halfNoise) + noisy.slice(mid + halfNoise);
}
encode(): string {
const payload: Payload = {
ts: Date.now(),
salt: crypto.randomBytes(8).toString("hex"),
};
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv("aes-256-cbc", this.secretKey, iv);
const json = JSON.stringify(payload);
const encrypted = Buffer.concat([
cipher.update(json, "utf8"),
cipher.final(),
]);
const hmac = crypto
.createHmac("sha256", this.secretKey)
.update(encrypted)
.digest("hex");
const encoded: EncodedPayload = {
iv: iv.toString("hex"),
data: encrypted.toString("base64"),
mac: hmac,
};
const base64 = Buffer.from(JSON.stringify(encoded)).toString("base64");
return this.addNoise(base64);
}
decode(token: string): boolean {
try {
const cleaned = this.removeNoise(token);
const raw = Buffer.from(cleaned, "base64").toString("utf8");
const { iv, data, mac } = JSON.parse(raw) as EncodedPayload;
const encrypted = Buffer.from(data, "base64");
const expectedMac = crypto
.createHmac("sha256", this.secretKey)
.update(encrypted)
.digest("hex");
if (mac !== expectedMac) {
return false;
}
const decipher = crypto.createDecipheriv(
"aes-256-cbc",
this.secretKey,
Buffer.from(iv, "hex"),
);
const decrypted = Buffer.concat([
decipher.update(encrypted),
decipher.final(),
]);
const { ts } = JSON.parse(decrypted.toString("utf8")) as Payload;
if (Math.abs(Date.now() - ts) > this.timestampToleranceMs) {
return false;
}
return true;
} catch {
return false;
}
}
}
import { SecureToken } from "./secret-token";
const secure = new SecureToken("your-strong-secret-key");
const token = secure.encode();
console.log("Token:", token);
const valid = secure.decode(token);
console.log("Valid:", valid); // true if untampered and within 5 minutes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment