UNPKG

@chittyos/core

Version:

ChittyOS Core - Essential package with ID, auth, verification, beacon tracking, and brand components for all ChittyOS applications

150 lines 4.28 kB
// src/auth/index.ts import { SignJWT, jwtVerify } from "jose"; import { nanoid } from "nanoid"; import * as crypto from "crypto"; var DEFAULT_CONFIG = { jwtSecret: process.env.CHITTY_JWT_SECRET || crypto.randomBytes(32).toString("base64"), issuer: "chittyos", audience: "chittyos-apps", expiresIn: "24h" }; var config = { ...DEFAULT_CONFIG }; var sessions = /* @__PURE__ */ new Map(); function configure(customConfig) { config = { ...config, ...customConfig }; } async function createToken(user) { const secret = new TextEncoder().encode(config.jwtSecret); const expiresAt = /* @__PURE__ */ new Date(); const hours = parseInt(config.expiresIn?.replace("h", "") || "24"); expiresAt.setHours(expiresAt.getHours() + hours); const token = await new SignJWT({ sub: user.id, chittyId: user.chittyId, email: user.email, roles: user.roles || [], permissions: user.permissions || [], metadata: user.metadata || {} }).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setIssuer(config.issuer).setAudience(config.audience).setExpirationTime(config.expiresIn).setJti(nanoid()).sign(secret); const refreshToken2 = nanoid(32); const session = { id: nanoid(), userId: user.id, token, refreshToken: refreshToken2, expiresAt, createdAt: /* @__PURE__ */ new Date() }; sessions.set(session.id, session); sessions.set(refreshToken2, session); return { token, expiresAt, refreshToken: refreshToken2 }; } async function verifyToken(token) { try { const secret = new TextEncoder().encode(config.jwtSecret); const { payload } = await jwtVerify(token, secret, { issuer: config.issuer, audience: config.audience }); return { ...payload, id: payload.sub, chittyId: payload.chittyId, email: payload.email, roles: payload.roles, permissions: payload.permissions, metadata: payload.metadata }; } catch (error) { throw new Error("Invalid or expired token"); } } async function refreshToken(refreshToken2) { const session = sessions.get(refreshToken2); if (!session) { throw new Error("Invalid refresh token"); } if (session.expiresAt < /* @__PURE__ */ new Date()) { sessions.delete(session.id); sessions.delete(session.refreshToken); throw new Error("Session expired"); } const user = await verifyToken(session.token).catch(() => null); if (!user) { throw new Error("Cannot refresh token"); } return createToken({ id: user.id, chittyId: user.chittyId, email: user.email, roles: user.roles, permissions: user.permissions, metadata: user.metadata }); } function revokeSession(sessionId) { const session = sessions.get(sessionId); if (session) { sessions.delete(session.id); sessions.delete(session.refreshToken); return true; } return false; } function hasRoles(user, requiredRoles) { if (!user.roles) return false; return requiredRoles.every((role) => user.roles.includes(role)); } function hasPermissions(user, requiredPermissions) { if (!user.permissions) return false; return requiredPermissions.every((perm) => user.permissions.includes(perm)); } async function hashPassword(password) { const salt = crypto.randomBytes(16).toString("hex"); const hash = crypto.pbkdf2Sync(password, salt, 1e5, 64, "sha512").toString("hex"); return `${salt}:${hash}`; } async function verifyPassword(password, hashedPassword) { const [salt, hash] = hashedPassword.split(":"); const verifyHash = crypto.pbkdf2Sync(password, salt, 1e5, 64, "sha512").toString("hex"); return hash === verifyHash; } function cleanupSessions() { const now = /* @__PURE__ */ new Date(); for (const [key, session] of sessions) { if (session.expiresAt < now) { sessions.delete(key); } } } setInterval(cleanupSessions, 36e5).unref(); var auth_default = { configure, createToken, verifyToken, refreshToken, revokeSession, hasRoles, hasPermissions, hashPassword, verifyPassword, cleanupSessions }; export { cleanupSessions, configure, createToken, auth_default as default, hasPermissions, hasRoles, hashPassword, refreshToken, revokeSession, verifyPassword, verifyToken }; //# sourceMappingURL=index.mjs.map