caplib
Version:
Credentialless Authentication Protocol Library for Web Applications
383 lines (377 loc) • 11.7 kB
JavaScript
;
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