UNPKG

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
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, }; }; }