feat: make url signing optional
This commit is contained in:
parent
533ab24b2a
commit
ff98d3c9a0
|
@ -13,7 +13,7 @@ export default {
|
||||||
debug: parseBool(get("DEBUG", "0")),
|
debug: parseBool(get("DEBUG", "0")),
|
||||||
hostname: get("HOSTNAME", "127.0.0.1"),
|
hostname: get("HOSTNAME", "127.0.0.1"),
|
||||||
port: parseInt(get("PORT", "8080")),
|
port: parseInt(get("PORT", "8080")),
|
||||||
signKey: get("SIGN_KEY"),
|
signKey: get("SIGN_KEY", null),
|
||||||
maxWebhookRetries: parseInt(get("MAX_RETRIES", "3")),
|
maxWebhookRetries: parseInt(get("MAX_RETRIES", "3")),
|
||||||
maxWebhookRetryMs: parseInt(get("MAX_RETRY_MS", "30000")),
|
maxWebhookRetryMs: parseInt(get("MAX_RETRY_MS", "30000")),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,24 +1,37 @@
|
||||||
import config from "./config.ts";
|
import config from "./config.ts";
|
||||||
import { hex } from "../deps.ts";
|
import { hex } from "../deps.ts";
|
||||||
|
|
||||||
const signKey = await crypto.subtle.importKey(
|
export const hasKey = !!config.signKey;
|
||||||
"raw",
|
|
||||||
hex.decode(new TextEncoder().encode(config.signKey)),
|
|
||||||
{ name: "HMAC", hash: "SHA-256" },
|
|
||||||
false,
|
|
||||||
["sign", "verify"],
|
|
||||||
);
|
|
||||||
|
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
|
|
||||||
|
let _signKey: CryptoKey | undefined = undefined;
|
||||||
|
|
||||||
|
async function getKey(): Promise<CryptoKey> {
|
||||||
|
if (_signKey === undefined) {
|
||||||
|
if (!config.signKey) throw new Error("Signature requested but no key configured");
|
||||||
|
|
||||||
|
_signKey = await crypto.subtle.importKey(
|
||||||
|
"raw",
|
||||||
|
hex.decode(new TextEncoder().encode(config.signKey)),
|
||||||
|
{ name: "HMAC", hash: "SHA-256" },
|
||||||
|
false,
|
||||||
|
["sign", "verify"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _signKey;
|
||||||
|
}
|
||||||
|
|
||||||
export async function sign(input: string): Promise<string> {
|
export async function sign(input: string): Promise<string> {
|
||||||
|
const key = await getKey();
|
||||||
const inputData = encoder.encode(input);
|
const inputData = encoder.encode(input);
|
||||||
const sig = await crypto.subtle.sign("HMAC", signKey, inputData);
|
const sig = await crypto.subtle.sign("HMAC", key, inputData);
|
||||||
return new TextDecoder().decode(hex.encode(new Uint8Array(sig)));
|
return new TextDecoder().decode(hex.encode(new Uint8Array(sig)));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function verify(input: string, signature: string): Promise<boolean> {
|
export async function verify(input: string, signature: string): Promise<boolean> {
|
||||||
|
const key = await getKey();
|
||||||
const signatureData = hex.decode(encoder.encode(signature));
|
const signatureData = hex.decode(encoder.encode(signature));
|
||||||
const inputData = encoder.encode(input);
|
const inputData = encoder.encode(input);
|
||||||
return await crypto.subtle.verify("HMAC", signKey, signatureData, inputData);
|
return await crypto.subtle.verify("HMAC", key, signatureData, inputData);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { http, log } from "../deps.ts";
|
import { http, log } from "../deps.ts";
|
||||||
import { verify } from "./crypto.ts";
|
import { hasKey, verify } from "./crypto.ts";
|
||||||
import filterWebhook from "./filter.ts";
|
import filterWebhook from "./filter.ts";
|
||||||
import { UrlConfig } from "./types.d.ts";
|
import { UrlConfig } from "./types.d.ts";
|
||||||
import { parseBool } from "./util.ts";
|
import { parseBool } from "./util.ts";
|
||||||
|
@ -13,14 +13,15 @@ export default async function handle(req: Request): Promise<Response> {
|
||||||
// split url into parts
|
// split url into parts
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
const [, id, token] = url.pathname.split("/");
|
const [, id, token] = url.pathname.split("/");
|
||||||
const signature = url.searchParams.get("sig");
|
if (!id || !token) {
|
||||||
if (!id || !token || !signature) {
|
|
||||||
throw http.createHttpError(400);
|
throw http.createHttpError(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify signature
|
// verify signature
|
||||||
if (!(await verify(`${id}/${token}`, signature))) {
|
if (hasKey) {
|
||||||
throw http.createHttpError(403);
|
const signature = url.searchParams.get("sig");
|
||||||
|
if (!signature) throw http.createHttpError(400);
|
||||||
|
if (!(await verify(`${id}/${token}`, signature))) throw http.createHttpError(403);
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract data
|
// extract data
|
||||||
|
|
4
main.ts
4
main.ts
|
@ -44,6 +44,10 @@ async function handleRequest(req: Request, connInfo: http.ConnInfo): Promise<Res
|
||||||
if (import.meta.main) {
|
if (import.meta.main) {
|
||||||
setupLogs();
|
setupLogs();
|
||||||
|
|
||||||
|
if (!config.signKey) {
|
||||||
|
log.warning("url signing disabled");
|
||||||
|
}
|
||||||
|
|
||||||
log.info(`starting webserver on ${config.hostname}:${config.port}`);
|
log.info(`starting webserver on ${config.hostname}:${config.port}`);
|
||||||
http.serve(handleRequest, {
|
http.serve(handleRequest, {
|
||||||
hostname: config.hostname,
|
hostname: config.hostname,
|
||||||
|
|
Loading…
Reference in New Issue