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.
210 lines (209 loc) • 7.59 kB
JavaScript
import { cleanParams, paramsToObject } from "./params.js";
// auth apis
import createHandler from "../handlers/create.js";
import pollHandler from "../handlers/poll.js";
import callbackHandler from "../handlers/callback.js";
import tokenHandler from "../handlers/token.js";
// pages
import lightningSignInHandler from "../handlers/lightning-signin.js";
import nostrSignInHandler from "../handlers/nostr-signin.js";
// misc
import avatarHandler from "../handlers/avatar.js";
import qrHandler from "../handlers/qr.js";
import diagnosticsHandler from "../handlers/diagnostics.js";
export var HandlerErrorCodes;
(function (HandlerErrorCodes) {
// An attempted API request was made to an auth endpoint while already logged in.
HandlerErrorCodes["Forbidden"] = "You are already logged in.";
// An API request was made to a non existent `lnurl-auth` API path.
HandlerErrorCodes["NotFound"] = "Path not found.";
// Authorizing the user failed because the `lnurl-auth` callback received an invalid `signature` / `pubkey`
HandlerErrorCodes["Unauthorized"] = "You could not be signed in.";
// The user's session has been deleted. Either their session expired or they had a failed sign in attempt and must create a new session.
HandlerErrorCodes["Gone"] = "Session not found.";
// An API request was made to the `lnurl-auth` APIs with a missing required query param or body arguments.
HandlerErrorCodes["BadRequest"] = "Missing required query or body arguments.";
// Generic catch-all error code when one of the above errors is not matched.
HandlerErrorCodes["Default"] = "Unable to sign in.";
})(HandlerErrorCodes || (HandlerErrorCodes = {}));
export async function pagesHandler(req, res, config, handler) {
const query = cleanParams(req.query);
const body = req.body || {};
const url = new URL(config.baseUrl + req.url);
const args = {
query,
body,
cookies: {
sessionToken: req.cookies["next-auth.session-token"],
},
url,
config,
};
let output;
try {
output = await handler(args);
}
catch (e) {
output = {
error: "Default",
message: HandlerErrorCodes.Default,
status: 500,
log: e instanceof Error ? e.message : "",
};
}
if ("error" in output) {
if (output.log) {
console.error(output.log);
}
const errorUrl = new URL(config.baseUrl + config.pages.error);
if (config.pages.error === "/api/auth/error") {
// if using default next-auth error screen
errorUrl.searchParams.append("error", "OAuthSignin");
}
else {
// otherwise use `next-auth-pubkey` params
errorUrl.searchParams.append("error", output.error);
errorUrl.searchParams.append("message", HandlerErrorCodes[output.error]);
}
if (output.status === 302) {
return res.redirect(errorUrl.toString()).end();
}
else {
Object.entries(output.headers || {}).forEach(([key, value]) => res.setHeader(key, value));
res.status(output.status || 500);
return res.send(JSON.stringify({
error: output.error,
message: HandlerErrorCodes[output.error],
url: errorUrl.toString(),
}));
}
}
if ("redirect" in output) {
return res.redirect(output.redirect.toString()).end();
}
if ("response" in output) {
Object.entries(output.headers || {}).forEach(([key, value]) => res.setHeader(key, value));
res.status(output.status || 200);
if (typeof output.response === "string" ||
output.response instanceof Buffer) {
return res.send(output.response);
}
else if (typeof output.response === "object") {
return res.send(JSON.stringify(output.response));
}
}
}
export async function appHandler(req, config, handler) {
const query = paramsToObject(req.nextUrl.searchParams);
const text = await req.text();
const params = new URLSearchParams(text);
const body = paramsToObject(params);
const url = new URL(config.baseUrl + req.nextUrl.pathname);
const args = {
query,
body,
cookies: {
sessionToken: req.cookies.get("next-auth.session-token")?.value,
},
url,
config,
};
let output;
try {
output = await handler(args);
}
catch (e) {
output = {
error: "Default",
message: HandlerErrorCodes.Default,
status: 500,
log: e instanceof Error ? e.message : "",
};
}
if ("error" in output) {
if (output.log) {
console.error(output.log);
}
const errorUrl = new URL(config.baseUrl + config.pages.error);
if (config.pages.error === "/api/auth/error") {
// if using default next-auth error screen
errorUrl.searchParams.append("error", "OAuthSignin");
}
else {
// otherwise use `next-auth-pubkey` params
errorUrl.searchParams.append("error", output.error);
errorUrl.searchParams.append("message", HandlerErrorCodes[output.error]);
}
if (output.status === 302) {
return Response.redirect(errorUrl.toString());
}
else {
return Response.json({
error: output.error,
message: HandlerErrorCodes[output.error],
url: errorUrl.toString(),
}, {
status: output.status || 500,
headers: output.headers || {},
});
}
}
if ("redirect" in output) {
return Response.redirect(output.redirect.toString());
}
if ("response" in output) {
const options = {
status: output.status || 200,
headers: output.headers || {},
};
if (typeof output.response === "string" ||
output.response instanceof Buffer) {
return new Response(output.response, options);
}
else if (typeof output.response === "object") {
return Response.json(output.response, options);
}
}
}
export default function getHandler(args) {
const { req, res, config } = args;
// get path from either pages or app router req/res objects
const path = res?.params
? new URL(req.nextUrl).pathname
: req?.url;
let handler;
if (path?.indexOf(config.apis.create) > -1) {
return createHandler;
}
else if (path?.indexOf(config.apis.poll) > -1) {
return pollHandler;
}
else if (path?.indexOf(config.apis.callback) > -1) {
return callbackHandler;
}
else if (path?.indexOf(config.apis.token) > -1) {
return tokenHandler;
}
else if (path?.indexOf(config.apis.lightningSignIn) > -1) {
return lightningSignInHandler;
}
else if (path?.indexOf(config.apis.nostrSignIn) > -1) {
return nostrSignInHandler;
}
else if (path?.indexOf(config.apis.avatar) > -1) {
return avatarHandler;
}
else if (path?.indexOf(config.apis.qr) > -1) {
return qrHandler;
}
else if (path?.indexOf(config.apis.diagnostics) > -1 &&
config.flags.diagnostics) {
return diagnosticsHandler;
}
return async function ({}) {
return {
error: "NotFound",
status: 404,
};
};
}