2024-08-16 18:28:03 +01:00
|
|
|
import { nanoid } from "nanoid";
|
|
|
|
import { createHmac } from "crypto";
|
|
|
|
|
|
|
|
import { env } from "../config.js";
|
|
|
|
|
|
|
|
const toBase64URL = (b) => Buffer.from(b).toString("base64url");
|
|
|
|
const fromBase64URL = (b) => Buffer.from(b, "base64url").toString();
|
|
|
|
|
|
|
|
const makeHmac = (header, payload) =>
|
|
|
|
createHmac("sha256", env.jwtSecret)
|
|
|
|
.update(`${header}.${payload}`)
|
|
|
|
.digest("base64url");
|
|
|
|
|
|
|
|
export const generate = () => {
|
2024-08-17 12:58:40 +01:00
|
|
|
const exp = Math.floor(new Date().getTime() / 1000) + env.jwtLifetime;
|
2024-08-16 18:28:03 +01:00
|
|
|
|
|
|
|
const header = toBase64URL(JSON.stringify({
|
|
|
|
alg: "HS256",
|
|
|
|
typ: "JWT"
|
|
|
|
}));
|
|
|
|
|
|
|
|
const payload = toBase64URL(JSON.stringify({
|
2024-08-17 12:58:40 +01:00
|
|
|
jti: nanoid(8),
|
2024-08-16 18:28:03 +01:00
|
|
|
exp,
|
|
|
|
}));
|
|
|
|
|
|
|
|
const signature = makeHmac(header, payload);
|
|
|
|
|
|
|
|
return {
|
|
|
|
token: `${header}.${payload}.${signature}`,
|
2024-08-19 17:25:21 +01:00
|
|
|
exp: env.jwtLifetime - 2,
|
2024-08-16 18:28:03 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export const verify = (jwt) => {
|
|
|
|
const [header, payload, signature] = jwt.split(".", 3);
|
2024-08-17 12:58:40 +01:00
|
|
|
const timestamp = Math.floor(new Date().getTime() / 1000);
|
2024-08-16 18:28:03 +01:00
|
|
|
|
|
|
|
if ([header, payload, signature].join('.') !== jwt) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const verifySignature = makeHmac(header, payload);
|
|
|
|
|
|
|
|
if (verifySignature !== signature) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (timestamp >= JSON.parse(fromBase64URL(payload)).exp) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
export default {
|
|
|
|
generate,
|
|
|
|
verify,
|
|
|
|
}
|