Created
July 12, 2025 06:24
-
-
Save lazuee/465a7de656708079ed3c2f40a853ff24 to your computer and use it in GitHub Desktop.
Secret Token
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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