next-auth-pubkey
Version:
A light-weight Lightning and Nostr auth provider for your Next.js app that's entirely self-hosted and plugs seamlessly into the next-auth framework.
102 lines (101 loc) • 3.47 kB
JavaScript
import { callbackQueryValidation, callbackBodyValidation, } from "../validation/api.js";
import lnurlVerifyAuthorizationSignature from "lnurl/lib/verifyAuthorizationSignature.js";
import { createHash } from "crypto";
import { schnorr } from "@noble/curves/secp256k1";
export default async function handler({ body, query, cookies, url, config, }) {
let authorize;
let k1, pubkey, sig;
if (body?.event) {
let event;
try {
({ event } = callbackBodyValidation.parse(body));
}
catch (e) {
return { error: "BadRequest", log: e instanceof Error ? e.message : "" };
}
try {
// is the event id a hash of this event
const id = createHash("sha256")
.update(JSON.stringify([
0,
event.pubkey,
event.created_at,
event.kind,
event.tags,
event.content,
]))
.digest("hex");
if (id !== event.id) {
throw new Error("Invalid event id");
}
// is the signature valid
if (!(await schnorr.verify(event.sig, event.id, event.pubkey))) {
throw new Error("Invalid signature");
}
// is the challenge present in the event
if (!(event.tags[0].length === 2 && event.tags[0][0] === "challenge")) {
throw new Error('Expected tags = [["challenge", <challenge>]]');
}
pubkey = event.pubkey;
sig = event.sig;
k1 = event.tags[0][1];
authorize = true;
}
catch (e) {
return {
error: "Unauthorized",
log: e?.message ? e.message : "",
};
}
}
else {
try {
({ k1, key: pubkey, sig } = callbackQueryValidation.parse(query));
}
catch (e) {
return { error: "BadRequest", log: e instanceof Error ? e.message : "" };
}
try {
if (!(await lnurlVerifyAuthorizationSignature(sig, k1, pubkey))) {
throw new Error("Invalid signature");
}
authorize = true;
}
catch (e) {
return {
error: "Unauthorized",
log: e?.message ? e.message : "",
};
}
}
if (!authorize) {
try {
await config.storage.delete({ k1 }, url, config);
}
catch (e) {
if (config.flags.logs) {
console.error(e);
}
if (config.flags.diagnostics && config.flags.logs) {
console.warn(`An error occurred in the storage.delete method. To debug the error see: ${config.baseUrl + config.apis.diagnostics}`);
}
}
return { error: "Unauthorized" };
}
try {
await config.storage.update({ k1, data: { pubkey, sig, success: true } }, url, config);
}
catch (e) {
if (config.flags.diagnostics && config.flags.logs) {
console.warn(`An error occurred in the storage.update method. To debug the error see: ${config.baseUrl + config.apis.diagnostics}`);
}
return { error: "Default", log: e instanceof Error ? e.message : "" };
}
return {
response: {
status: "OK",
success: true,
k1,
},
};
}