1
0
silverbullet/server/crypto.ts
Zef Hemel a2dbf7b3db
PlugOS refactor and other tweaks (#631)
* Prep for in-process plug loading (e.g. for CF workers, Deno Deploy)
* Prototype of fixed in-process loading plugs
* Fix: buttons not to scroll with content
* Better positioning of modal especially on mobile
* Move query caching outside query
* Fix annoying mouse behavior when filter box appears
* Page navigator search tweaks
2024-01-15 16:43:12 +01:00

93 lines
2.5 KiB
TypeScript

import {
create,
getNumericDate,
verify,
} from "https://deno.land/x/djwt@v3.0.1/mod.ts";
import { KvPrimitives } from "../plugos/lib/kv_primitives.ts";
const jwtSecretKey = "jwtSecretKey";
export class JWTIssuer {
private key!: CryptoKey;
constructor(readonly kv: KvPrimitives) {
}
// authString is only used to compare hashes to see if the auth has changed
async init(authString: string) {
const [secret] = await this.kv.batchGet([[jwtSecretKey]]);
if (!secret) {
console.log("Generating new JWT secret key");
return this.generateNewKey();
} else {
this.key = await crypto.subtle.importKey(
"raw",
secret,
{ name: "HMAC", hash: "SHA-512" },
true,
["sign", "verify"],
);
}
// Check if the authentication has changed since last run
const [currentAuthHash] = await this.kv.batchGet([[
"authHash",
]]);
const newAuthHash = await this.hashSHA256(authString);
if (currentAuthHash && currentAuthHash !== newAuthHash) {
console.log(
"Authentication has changed since last run, so invalidating all existing tokens",
);
// It has, so we need to generate a new key to invalidate all existing tokens
await this.generateNewKey();
}
if (currentAuthHash !== newAuthHash) {
// Persist new auth hash
await this.kv.batchSet([{
key: ["authHash"],
value: newAuthHash,
}]);
}
}
async generateNewKey() {
this.key = await crypto.subtle.generateKey(
{ name: "HMAC", hash: "SHA-512" },
true,
["sign", "verify"],
);
await this.kv.batchSet([{
key: [jwtSecretKey],
value: await crypto.subtle.exportKey("raw", this.key),
}]);
}
createJWT(
payload: Record<string, unknown>,
expirySeconds: number,
): Promise<string> {
return create({ alg: "HS512", typ: "JWT" }, {
...payload,
exp: getNumericDate(expirySeconds),
}, this.key);
}
verifyAndDecodeJWT(jwt: string): Promise<Record<string, unknown>> {
return verify(jwt, this.key);
}
async hashSHA256(message: string): Promise<string> {
// Transform the string into an ArrayBuffer
const encoder = new TextEncoder();
const data = encoder.encode(message);
// Generate the hash
const hashBuffer = await window.crypto.subtle.digest("SHA-256", data);
// Transform the hash into a hex string
return Array.from(new Uint8Array(hashBuffer)).map((b) =>
b.toString(16).padStart(2, "0")
).join("");
}
}