UNPKG

caplib

Version:

Credentialless Authentication Protocol Library for Web Applications

383 lines (377 loc) 11.7 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/server/index.ts var server_exports = {}; __export(server_exports, { EncryptedJSONDatabase: () => EncryptedJSONDatabase, createAuthHandler: () => createAuthHandler, db: () => db, getAvailableRoles: () => getAvailableRoles, getDefaultRole: () => getDefaultRole, getRoles: () => GET, isValidRole: () => isValidRole }); module.exports = __toCommonJS(server_exports); // src/server/db/encrypted-json-db.ts var import_fs = require("fs"); var import_path = require("path"); var import_crypto = require("crypto"); var defaultSchema = { users: [] }; var EncryptedJSONDatabase = class _EncryptedJSONDatabase { static instance; dbPath; data = defaultSchema; initialized = false; encryptionKey; constructor(dbPath) { this.dbPath = dbPath || (0, import_path.join)(process.cwd(), "data", "caplib-users.encrypted.json"); const key = process.env.DB_ENCRYPTION_KEY || (0, import_crypto.randomBytes)(32).toString("hex"); this.encryptionKey = Buffer.from(key, "hex"); } static getInstance(dbPath) { if (!_EncryptedJSONDatabase.instance) { _EncryptedJSONDatabase.instance = new _EncryptedJSONDatabase(dbPath); } return _EncryptedJSONDatabase.instance; } async encrypt(data) { const iv = (0, import_crypto.randomBytes)(16); const cipher = (0, import_crypto.createCipheriv)("aes-256-gcm", this.encryptionKey, iv); let encrypted = cipher.update(data, "utf8", "hex"); encrypted += cipher.final("hex"); const authTag = cipher.getAuthTag(); return JSON.stringify({ iv: iv.toString("hex"), data: encrypted, authTag: authTag.toString("hex") }); } async decrypt(encryptedData) { const { iv, data, authTag } = JSON.parse(encryptedData); const decipher = (0, import_crypto.createDecipheriv)( "aes-256-gcm", this.encryptionKey, Buffer.from(iv, "hex") ); decipher.setAuthTag(Buffer.from(authTag, "hex")); let decrypted = decipher.update(data, "hex", "utf8"); decrypted += decipher.final("utf8"); return decrypted; } async ensureDirectoryExists() { const dir = this.dbPath.substring(0, this.dbPath.lastIndexOf("/")); try { await import_fs.promises.access(dir); } catch { await import_fs.promises.mkdir(dir, { recursive: true }); } } async loadData() { try { const encryptedContent = await import_fs.promises.readFile(this.dbPath, "utf-8"); const decryptedContent = await this.decrypt(encryptedContent); this.data = JSON.parse(decryptedContent); } catch (error) { if (error.code === "ENOENT") { this.data = defaultSchema; await this.saveData(); } else { throw error; } } } async saveData() { await this.ensureDirectoryExists(); const encryptedData = await this.encrypt(JSON.stringify(this.data)); await import_fs.promises.writeFile(this.dbPath, encryptedData); } async initialize() { if (this.initialized) return; try { await this.loadData(); this.initialized = true; } catch (error) { console.error("Failed to initialize database:", error); throw error; } } async findUser(walletAddress) { await this.initialize(); const user = this.data.users.find( (u) => u.wallet_address.toLowerCase() === walletAddress.toLowerCase() ); return user || null; } async createUser(userData) { await this.initialize(); const existingUser = await this.findUser(userData.wallet_address); if (existingUser) { throw new Error("User already exists"); } const now = (/* @__PURE__ */ new Date()).toISOString(); const newUser = { ...userData, role: userData.role || "User", account_type: userData.account_type || "human", created_at: now }; this.data.users.push(newUser); await this.saveData(); return newUser; } async updateUser(walletAddress, updates) { await this.initialize(); const index = this.data.users.findIndex( (u) => u.wallet_address.toLowerCase() === walletAddress.toLowerCase() ); if (index === -1) return null; const updatedUser = { ...this.data.users[index], ...updates, wallet_address: this.data.users[index].wallet_address }; this.data.users[index] = updatedUser; await this.saveData(); return updatedUser; } async deleteUser(walletAddress) { await this.initialize(); const initialLength = this.data.users.length; this.data.users = this.data.users.filter( (u) => u.wallet_address.toLowerCase() !== walletAddress.toLowerCase() ); if (this.data.users.length !== initialLength) { await this.saveData(); return true; } return false; } async close() { await this.saveData(); this.initialized = false; } }; // src/server/utils/roles.ts function getAvailableRoles() { const roles = process.env.USER_ROLES || "User,Administrator"; return roles.split(",").map((role) => role.trim()).filter(Boolean); } function getDefaultRole() { return process.env.DEFAULT_USER_ROLE || "User"; } function isValidRole(role) { const availableRoles = getAvailableRoles(); return availableRoles.includes(role); } // src/server/handlers/wallet-auth.ts var getEnvVariable = (name, fallbackName) => { let value = process.env[name]; if (!value && fallbackName) { value = process.env[fallbackName]; } if (!value) { throw new Error(`Required environment variable ${name}${fallbackName ? ` or ${fallbackName}` : ""} is not set`); } return value; }; var createAuthHandler = (config) => { const db2 = EncryptedJSONDatabase.getInstance(); let isInitialized = false; const initialize = async () => { if (!isInitialized) { try { await db2.initialize(); isInitialized = true; } catch (error) { console.error("Failed to initialize database:", error); } } }; const GET2 = async () => { try { const API_URL = getEnvVariable("CAPLIB_API_URL"); const API_KEY = getEnvVariable("CAPLIB_API_KEY"); const CONTRACT_ID = getEnvVariable("NEXT_PUBLIC_AUTH_CONTRACT_ID", "AUTH_CONTRACT_ID"); await initialize(); const response = await fetch(`${API_URL}?action=generateNonce&contractId=${CONTRACT_ID}`, { headers: { "X-API-Key": API_KEY } }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); return new Response( JSON.stringify({ nonce: data.nonce }), { status: 200, headers: { "Content-Type": "application/json" } } ); } catch (error) { console.error("Error generating nonce:", error); return new Response( JSON.stringify({ error: "Failed to generate nonce", nonce: "" }), { status: 500, headers: { "Content-Type": "application/json" } } ); } }; const POST = async (req) => { try { const API_URL = getEnvVariable("CAPLIB_API_URL"); const API_KEY = getEnvVariable("CAPLIB_API_KEY"); const CONTRACT_ID = getEnvVariable("NEXT_PUBLIC_AUTH_CONTRACT_ID", "AUTH_CONTRACT_ID"); await initialize(); const body = await req.json(); const { nonce, userData } = body; const verifyResponse = await fetch(API_URL, { method: "POST", headers: { "X-API-Key": API_KEY, "Content-Type": "application/json" }, body: JSON.stringify({ action: "verifyAuthentication", contractId: CONTRACT_ID, nonce }) }); if (!verifyResponse.ok) { throw new Error(`Verification failed: ${verifyResponse.status}`); } const verifyData = await verifyResponse.json(); const { authenticated, userAddress } = verifyData; if (!authenticated || !userAddress) { return new Response( JSON.stringify({ authenticated: false, error: "Authentication failed" }), { status: 401, headers: { "Content-Type": "application/json" } } ); } if (config?.customValidation) { const isValid = await config.customValidation(userAddress); if (!isValid) { return new Response( JSON.stringify({ authenticated: false, error: "Custom validation failed" }), { status: 403, headers: { "Content-Type": "application/json" } } ); } } try { let user = await db2.findUser(userAddress); if (userData?.role && !isValidRole(userData.role)) { userData.role = getDefaultRole(); } if (!user && userData) { user = await db2.createUser({ wallet_address: userAddress, ...userData }); } return new Response( JSON.stringify({ authenticated: true, userAddress, user: user || void 0 }), { status: 200, headers: { "Content-Type": "application/json" } } ); } catch (error) { console.error("Database error:", error); return new Response( JSON.stringify({ authenticated: true, userAddress, error: "Database operation failed" }), { status: 200, headers: { "Content-Type": "application/json" } } ); } } catch (error) { console.error("Error verifying authentication:", error); return new Response( JSON.stringify({ authenticated: false, error: "Authentication verification failed" }), { status: 500, headers: { "Content-Type": "application/json" } } ); } }; return { GET: GET2, POST }; }; // src/server/db/index.ts var db = EncryptedJSONDatabase.getInstance(); // src/server/handlers/roles.ts var import_server = require("next/server"); async function GET(_req) { try { const roles = getAvailableRoles(); const defaultRole = getDefaultRole(); return import_server.NextResponse.json({ roles, defaultRole }); } catch (error) { console.error("Error fetching roles:", error); return import_server.NextResponse.json( { error: "Failed to fetch roles", roles: ["User", "Administrator", "Developer"], defaultRole: "User" }, { status: 500 } ); } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { EncryptedJSONDatabase, createAuthHandler, db, getAvailableRoles, getDefaultRole, getRoles, isValidRole }); //# sourceMappingURL=index.js.map