UNPKG

@bsv/auth-express-middleware

Version:
763 lines 34.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ExpressTransport = exports.writeBodyToWriter = exports.getLogMethod = exports.isLogLevelEnabled = void 0; exports.createAuthMiddleware = createAuthMiddleware; const node_fs_1 = __importDefault(require("node:fs")); const mime_types_1 = __importDefault(require("mime-types")); const sdk_1 = require("@bsv/sdk"); const authMiddlewareHelpers_js_1 = require("./authMiddlewareHelpers.js"); var authMiddlewareHelpers_js_2 = require("./authMiddlewareHelpers.js"); Object.defineProperty(exports, "isLogLevelEnabled", { enumerable: true, get: function () { return authMiddlewareHelpers_js_2.isLogLevelEnabled; } }); Object.defineProperty(exports, "getLogMethod", { enumerable: true, get: function () { return authMiddlewareHelpers_js_2.getLogMethod; } }); var authMiddlewareHelpers_js_3 = require("./authMiddlewareHelpers.js"); Object.defineProperty(exports, "writeBodyToWriter", { enumerable: true, get: function () { return authMiddlewareHelpers_js_3.writeBodyToWriter; } }); /** * ResponseWriterWrapper buffers response data until signing is complete. * This pattern matches the Go implementation for cleaner response handling. */ class ResponseWriterWrapper { constructor(res) { this.statusCode = 200; this.headers = {}; this.body = []; this.flushed = false; this.originalRes = res; } status(code) { this.statusCode = code; return this; } set(key, value) { if (typeof key === 'object' && key !== null) { for (const [k, v] of Object.entries(key)) { this.headers[k.toLowerCase()] = String(v); } } else if (typeof key === 'string' && value !== undefined) { this.headers[key.toLowerCase()] = String(value); } return this; } send(data) { this.body = (0, authMiddlewareHelpers_js_1.convertValueToArray)(data, this.headers); return this; } json(data) { if (!this.headers['content-type']) { this.headers['content-type'] = 'application/json'; } this.body = sdk_1.Utils.toArray(JSON.stringify(data), 'utf8'); return this; } text(data) { if (!this.headers['content-type']) { this.headers['content-type'] = 'text/plain'; } this.body = sdk_1.Utils.toArray(data, 'utf8'); return this; } end() { // No-op for buffering, actual end happens on flush return this; } getStatusCode() { return this.statusCode; } getHeaders() { return this.headers; } getBody() { return this.body; } getOriginalRes() { return this.originalRes; } // Called after peer signs the response flush() { if (this.flushed) return; this.flushed = true; this.originalRes.status(this.statusCode); for (const [key, value] of Object.entries(this.headers)) { this.originalRes.set(key, value); } if (this.body.length > 0) { this.originalRes.send(Buffer.from(new Uint8Array(this.body))); } else { this.originalRes.end(); } } } /** * Transport implementation for Express. */ class ExpressTransport { /** * Constructs a new ExpressTransport instance. * * @param {boolean} [allowUnauthenticated=false] - Whether to allow unauthenticated requests passed the auth middleware. * If `true`, requests without authentication will be permitted, and `req.auth.identityKey` * will be set to `"unknown"`. If `false`, unauthenticated requests will result in a `401 Unauthorized` response. * @param {typeof console} [logger] - Logger to use (e.g., console). If omitted, logging is disabled. * @param {'debug' | 'info' | 'warn' | 'error'} [logLevel] - Log level. If omitted, no logs are output. */ constructor(allowUnauthenticated = false, logger, logLevel) { this.openNonGeneralHandles = {}; this.openGeneralHandles = {}; this.openNextHandlers = {}; this.openNextHandlerTimeouts = {}; this.allowAuthenticated = allowUnauthenticated; this.logger = logger; this.logLevel = logLevel || 'error'; // Default to 'error' if not provided } /** * Internal logging method, only logs if logger is defined and log level is appropriate. * * @param level - The log level for this message * @param message - The message to log * @param data - Optional additional data to log */ log(level, message, data) { if (typeof this.logger !== 'object') return; // Logging disabled if ((0, authMiddlewareHelpers_js_1.isLogLevelEnabled)(this.logLevel, level)) { const logMethod = (0, authMiddlewareHelpers_js_1.getLogMethod)(this.logger, level); if (data !== undefined) { logMethod(`[ExpressTransport] [${level.toUpperCase()}] ${message}`, data); } else { logMethod(`[ExpressTransport] [${level.toUpperCase()}] ${message}`); } } } setPeer(peer) { this.peer = peer; this.log('debug', 'Peer set in ExpressTransport', { peer }); } /** * Sends an AuthMessage to the connected Peer. * This method uses an Express response object to deliver the message to the specified Peer. * * ### Parameters: * @param {AuthMessage} message - The authenticated message to send. * * ### Returns: * @returns {Promise<void>} A promise that resolves once the message has been sent successfully. */ async send(message) { this.log('debug', 'Attempting to send AuthMessage', { message }); if (message.messageType === 'general') { await this.sendGeneralMessage(message); } else { await this.sendNonGeneralMessage(message); } } /** * Handles a general (authenticated application) AuthMessage response. */ async sendGeneralMessage(message) { const reader = new sdk_1.Utils.Reader(message.payload); const requestId = sdk_1.Utils.toBase64(reader.read(32)); if (typeof this.openGeneralHandles[requestId] !== 'object') { this.log('warn', 'No response handle for this requestId', { requestId }); throw new Error('No response handle for this requestId!'); } let { res, next } = this.openGeneralHandles[requestId]; delete this.openGeneralHandles[requestId]; const statusCode = reader.readVarIntNum(); res.__status(statusCode); const responseHeaders = this.readResponseHeaders(reader); responseHeaders['x-bsv-auth-version'] = message.version; responseHeaders['x-bsv-auth-identity-key'] = message.identityKey; responseHeaders['x-bsv-auth-nonce'] = message.nonce; responseHeaders['x-bsv-auth-your-nonce'] = message.yourNonce; responseHeaders['x-bsv-auth-signature'] = sdk_1.Utils.toHex(message.signature); responseHeaders['x-bsv-auth-request-id'] = requestId; if (message.requestedCertificates) { responseHeaders['x-bsv-auth-requested-certificates'] = JSON.stringify(message.requestedCertificates); } for (const [k, v] of Object.entries(responseHeaders)) { ; res.__set(k, v); } let responseBody; const responseBodyBytes = reader.readVarIntNum(); if (responseBodyBytes > 0) { responseBody = reader.read(responseBodyBytes); } res = this.resetRes(res, next); this.log('info', 'Sending general AuthMessage response', { status: statusCode, responseHeaders, responseBodyLength: responseBody ? responseBody.length : 0, requestId }); if (responseBody) { res.send(Buffer.from(new Uint8Array(responseBody))); } else { res.end(); } } /** * Reads response headers from a binary reader. */ readResponseHeaders(reader) { const responseHeaders = {}; const nHeaders = reader.readVarIntNum(); for (let i = 0; i < nHeaders; i++) { const nHeaderKeyBytes = reader.readVarIntNum(); const headerKeyBytes = reader.read(nHeaderKeyBytes); const headerKey = sdk_1.Utils.toUTF8(headerKeyBytes); const nHeaderValueBytes = reader.readVarIntNum(); const headerValueBytes = reader.read(nHeaderValueBytes); const headerValue = sdk_1.Utils.toUTF8(headerValueBytes); responseHeaders[headerKey] = headerValue; } return responseHeaders; } /** * Handles a non-general (handshake) AuthMessage response. */ async sendNonGeneralMessage(message) { const handles = this.openNonGeneralHandles[message.yourNonce]; if (!Array.isArray(handles) || handles.length === 0) { this.log('warn', 'No open handles to peer for nonce', { yourNonce: message.yourNonce }); throw new Error('No open handles to this peer!'); } // Since this is an initial response, we can assume there's only one handle per identity const { res, next } = handles[0]; const responseHeaders = { 'x-bsv-auth-version': message.version, 'x-bsv-auth-message-type': message.messageType, 'x-bsv-auth-identity-key': message.identityKey, 'x-bsv-auth-nonce': message.nonce, 'x-bsv-auth-your-nonce': message.yourNonce, 'x-bsv-auth-signature': sdk_1.Utils.toHex(message.signature) }; if (typeof message.requestedCertificates === 'object') { responseHeaders['x-bsv-auth-requested-certificates'] = JSON.stringify(message.requestedCertificates); } if (res.__set !== undefined) { this.resetRes(res, next); } for (const [k, v] of Object.entries(responseHeaders)) { res.set(k, v); } this.log('info', 'Sending non-general AuthMessage response', { status: 200, responseHeaders, messagePayload: message }); res.send(message); handles.shift(); } /** * Stores the callback bound by a Peer * @param callback */ async onData(callback) { this.log('debug', 'onData callback set'); this.messageCallback = callback; } /** * Handles an incoming request for the Express server. * * This method processes both general and non-general message types, * manages peer-to-peer certificate handling, and modifies the response object * to enable custom behaviors like certificate requests and tailored responses. * * ### Behavior: * - For `/.well-known/auth`: * - Handles non-general messages and listens for certificates. * - Calls the `onCertificatesReceived` callback (if provided) when certificates are received. * - For general messages: * - Sets up a listener for peer-to-peer general messages. * - Overrides response methods (`send`, `json`, etc.) for custom handling. * - Returns a 401 error if mutual authentication fails. * * ### Parameters: * @param {AuthRequest} req - The incoming HTTP request. * @param {Response} res - The HTTP response. * @param {NextFunction} next - The Express `next` middleware function. * @param {Function} [onCertificatesReceived] - Optional callback invoked when certificates are received. */ async handleIncomingRequest(req, res, next, onCertificatesReceived) { this.log('debug', 'Handling incoming request', { path: req.path, headers: req.headers, method: req.method, body: req.body }); try { if (!this.peer) { this.log('error', 'No Peer set in ExpressTransport! Cannot handle request.'); throw new Error('You must set a Peer before you can handle incoming requests!'); } if (req.path === '/.well-known/auth') { await this.handleWellKnownAuth(req, res, next, onCertificatesReceived); } else if (req.headers['x-bsv-auth-request-id']) { this.handleGeneralMessage(req, res, next); } else { this.handleUnauthenticated(req, res, next); } } catch (error) { this.log('error', 'Caught error in handleIncomingRequest', { error }); next(error); } } /** * Handles a request to /.well-known/auth (non-general / handshake messages). */ async handleWellKnownAuth(req, res, next, onCertificatesReceived) { const message = req.body; this.log('debug', 'Received non-general message at /.well-known/auth', { message }); let requestId = req.headers['x-bsv-auth-request-id']; if (!requestId) { requestId = message.initialNonce; } if (Array.isArray(this.openNonGeneralHandles[requestId])) { this.openNonGeneralHandles[requestId].push({ res, next }); } else { this.openNonGeneralHandles[requestId] = [{ res, next }]; } if (!await this.peer.sessionManager.hasSession(message.identityKey)) { this.registerCertificateListener(req, res, next, requestId, message, onCertificatesReceived); } if (this.messageCallback) { this.log('debug', 'Invoking stored messageCallback for non-general message'); this.messageCallback(message).catch((err) => { this.log('error', 'Error in messageCallback', { error: err.message, err }); return res.status(500).json({ status: 'error', code: 'ERR_INTERNAL_SERVER_ERROR', description: err.message || 'An unknown error occurred.' }); }); } } /** * Registers a certificate-received listener for a non-general message. */ registerCertificateListener(req, res, next, requestId, message, onCertificatesReceived) { const listenerId = this.peer.listenForCertificatesReceived((senderPublicKey, certs) => { var _a; try { this.log('debug', 'Certificates received event triggered', { senderPublicKey, certCount: certs === null || certs === void 0 ? void 0 : certs.length, requestId }); if (senderPublicKey === req.body.identityKey) { this.handleCertificatesForPeer(senderPublicKey, certs, req, res, next, message, onCertificatesReceived); } } catch (error) { this.log('error', 'Error in certificate listener callback', { error }); } finally { const handles = this.openNonGeneralHandles[requestId]; if (handles && handles.length > 0) { handles.shift(); if (handles.length === 0) { delete this.openNonGeneralHandles[requestId]; } } (_a = this.peer) === null || _a === void 0 ? void 0 : _a.stopListeningForCertificatesReceived(listenerId); } }); this.log('debug', 'listenForCertificatesReceived registered', { listenerId, requestId }); } /** * Processes certificates received from a peer during the handshake. */ handleCertificatesForPeer(senderPublicKey, certs, req, res, next, message, onCertificatesReceived) { var _a; if (!Array.isArray(certs) || certs.length === 0) { this.log('warn', 'No certificates provided by peer', { senderPublicKey }); const handles = this.openNonGeneralHandles[(_a = req.headers['x-bsv-auth-request-id']) !== null && _a !== void 0 ? _a : message.initialNonce]; if (handles && handles.length > 0) { handles[0].res.status(400).json({ status: 'No certificates provided' }); } return; } this.log('info', 'Certificates successfully received from peer', { senderPublicKey, certs }); if (typeof onCertificatesReceived === 'function') { onCertificatesReceived(senderPublicKey, certs, req, res, next); } // Validate that identityKey is an own property of the handler map before // invoking, preventing CodeQL js/unvalidated-dynamic-method-call from // flagging prototype-chain dispatch on a user-supplied key. const identityKey = message.identityKey; if (typeof identityKey === 'string' && Object.hasOwn(this.openNextHandlers, identityKey)) { const nextFn = this.openNextHandlers[identityKey]; const timeoutHandle = this.openNextHandlerTimeouts[identityKey]; if (timeoutHandle != null) { clearTimeout(timeoutHandle); delete this.openNextHandlerTimeouts[identityKey]; } nextFn(); delete this.openNextHandlers[identityKey]; } } /** * Handles an authenticated general message (has x-bsv-auth-request-id header). */ handleGeneralMessage(req, res, next) { const message = buildAuthMessageFromRequest(req, this.logger, this.logLevel); this.log('debug', 'Received general message with x-bsv-auth-request-id', { message }); // Setup general message listener const listenerId = this.peer.listenForGeneralMessages((senderPublicKey, payload) => { var _a; try { if (senderPublicKey !== req.headers['x-bsv-auth-identity-key']) return; const requestId = sdk_1.Utils.toBase64(new sdk_1.Utils.Reader(payload).read(32)); if (requestId === req.headers['x-bsv-auth-request-id']) { (_a = this.peer) === null || _a === void 0 ? void 0 : _a.stopListeningForGeneralMessages(listenerId); this.setupAuthenticatedResponse(req, res, next, senderPublicKey, requestId); } } catch (error) { this.log('error', 'Error in listenForGeneralMessages callback', { error }); next(error); } }); this.log('debug', 'listenForGeneralMessages registered', { listenerId }); if (this.messageCallback) { this.log('debug', 'Invoking stored messageCallback for general message'); this.messageCallback(message).catch((err) => { const msg = err instanceof Error ? err.message : String(err); const isAuthError = /nonce|signature|session|auth version/i.test(msg); this.log('error', 'Error in messageCallback (general message)', { error: msg, isAuthError }); const statusCode = isAuthError ? 401 : 500; const code = isAuthError ? 'ERR_AUTH_FAILED' : 'ERR_INTERNAL_SERVER_ERROR'; const description = isAuthError ? (msg || 'Authentication failed.') : (msg || 'An unexpected error occurred.'); return res.status(statusCode).json({ status: 'error', code, description }); }); } } /** * Sets up the intercepted response for an authenticated general message. */ setupAuthenticatedResponse(req, res, next, senderPublicKey, requestId) { this.log('debug', 'General message from the correct identity key', { requestId, senderPublicKey }); req.auth = { identityKey: senderPublicKey }; const wrapper = new ResponseWriterWrapper(res); let responseSent = false; const buildAndSendResponse = async () => { var _a; if (responseSent) return; responseSent = true; try { const responsePayload = buildResponsePayload(requestId, wrapper.getStatusCode(), wrapper.getHeaders(), wrapper.getBody(), req, this.logger, this.logLevel); this.openGeneralHandles[requestId] = { res, next }; this.log('debug', 'Sending general message response', { requestId, responseStatus: wrapper.getStatusCode(), responseHeaders: wrapper.getHeaders(), responseBodyLength: wrapper.getBody().length }); await ((_a = this.peer) === null || _a === void 0 ? void 0 : _a.toPeer(responsePayload, req.headers['x-bsv-auth-identity-key'])); } catch (err) { delete this.openGeneralHandles[requestId]; this.log('error', 'Failed to build and send authenticated response', { error: err }); try { const restored = this.resetRes(res, next); restored.status(500).json({ status: 'error', code: 'ERR_RESPONSE_SIGNING_FAILED', description: err instanceof Error ? err.message : 'Failed to sign response' }); } catch (_responseAlreadySent) { // Response may already be partially sent — nothing to do } } }; this.hijackResponse(res, next, wrapper, buildAndSendResponse); void this.scheduleNextOrCertificateWait(next, senderPublicKey, wrapper, buildAndSendResponse).catch(next); } /** * Overrides the response methods to intercept and buffer the response for signing. */ hijackResponse(res, next, wrapper, buildAndSendResponse) { // Override methods to capture response data this.checkRes(res, 'needs to be clear', next); res.__status = res.status; res.status = (n) => { wrapper.status(n); return res; }; res.__set = res.set; res.set = (keyOrHeaders, value) => { wrapper.set(keyOrHeaders, value); return res; }; res.__send = res.send; res.send = (val) => { if (typeof val === 'object' && val !== null && !wrapper.getHeaders()['content-type']) { wrapper.set('content-type', 'application/json'); } wrapper.send(val); buildAndSendResponse(); return res; }; res.__json = res.json; res.json = (obj) => { wrapper.json(obj); buildAndSendResponse(); return res; }; res.__text = res.text; res.text = (str) => { wrapper.text(str); buildAndSendResponse(); return res; }; res.__end = res.end; res.end = () => { buildAndSendResponse(); return res; }; res.__sendFile = res.sendFile; res.sendFile = (path, options, callback) => { node_fs_1.default.readFile(path, (err, data) => { if (err) { this.log('error', 'Error reading file in sendFile', { error: err.message }); if (callback) return callback(err); wrapper.status(500); buildAndSendResponse(); return; } const mimeType = mime_types_1.default.lookup(path) || 'application/octet-stream'; wrapper.set('Content-Type', mimeType); wrapper.send(Array.from(data)); buildAndSendResponse(); }); }; } /** * Either calls next() immediately or stores it pending certificate arrival. */ async scheduleNextOrCertificateWait(next, senderPublicKey, wrapper, buildAndSendResponse) { var _a, _b, _c, _d, _e; const hasSession = await ((_b = (_a = this.peer) === null || _a === void 0 ? void 0 : _a.sessionManager.hasSession(senderPublicKey)) !== null && _b !== void 0 ? _b : false); const needsCertificates = (_e = (_d = (_c = this.peer) === null || _c === void 0 ? void 0 : _c.certificatesToRequest) === null || _d === void 0 ? void 0 : _d.certifiers) === null || _e === void 0 ? void 0 : _e.length; this.log('debug', 'Checking if we need to wait for certificates', { senderPublicKey, hasSession, needsCertificates, openNextHandlersKeys: Object.keys(this.openNextHandlers) }); if (!needsCertificates || hasSession) { this.log('debug', 'Calling next() immediately - no certificate wait needed', { senderPublicKey, hasSession }); next(); return; } this.log('debug', 'Storing next handler to wait for certificates', { senderPublicKey }); const existingTimeout = this.openNextHandlerTimeouts[senderPublicKey]; if (existingTimeout != null) { clearTimeout(existingTimeout); delete this.openNextHandlerTimeouts[senderPublicKey]; } this.openNextHandlers[senderPublicKey] = next; const CERTIFICATE_TIMEOUT_MS = 30000; const timeoutHandle = setTimeout(() => { if (this.openNextHandlers[senderPublicKey]) { this.log('warn', 'Certificate request timed out', { senderPublicKey }); delete this.openNextHandlers[senderPublicKey]; delete this.openNextHandlerTimeouts[senderPublicKey]; wrapper.status(408).json({ status: 'error', code: 'CERTIFICATE_TIMEOUT', message: 'Certificate request timed out' }); buildAndSendResponse(); } }, CERTIFICATE_TIMEOUT_MS); this.openNextHandlerTimeouts[senderPublicKey] = timeoutHandle; } /** * Handles a request with no auth headers. */ handleUnauthenticated(req, res, next) { this.log('warn', 'No Auth headers found on request. Checking allowUnauthenticated setting.', { allowAuthenticated: this.allowAuthenticated }); if (this.allowAuthenticated) { req.auth = { identityKey: 'unknown' }; next(); } else { this.log('warn', 'Mutual-authentication failed. Returning 401.'); res.status(401).json({ status: 'error', code: 'UNAUTHORIZED', message: 'Mutual-authentication failed!' }); } } checkRes(res, test, next) { if (test === 'needs to be clear') { if (typeof res.__status === 'function' || typeof res.__set === 'function' || typeof res.__json === 'function' || typeof res.__text === 'function' || typeof res.__send === 'function' || typeof res.__end === 'function' || typeof res.__sendFile === 'function') { const e = new Error('Unable to install Auth midddleware on the response object as it is not clear. Are two middleware instances installed?'); if (typeof next === 'function') { next(e); } throw e; } } else if (typeof res.__status !== 'function' || typeof res.__set !== 'function' || typeof res.__json !== 'function' || typeof res.__send !== 'function' || typeof res.__end !== 'function' || typeof res.__sendFile !== 'function') { const e = new Error('Unable to restore response object. Did you tamper with hijacked properties (res.__status, __set, __json, __text, __send, __end, __sendFile) ?'); if (typeof next === 'function') { next(e); } throw e; } } resetRes(res, next) { this.checkRes(res, 'needs to be hijacked', next); res.status = res.__status; res.set = res.__set; res.json = res.__json; res.text = res.__text; res.send = res.__send; res.end = res.__end; res.sendFile = res.__sendFile; return res; } } exports.ExpressTransport = ExpressTransport; /** * Helper: Build AuthMessage from Request */ function buildAuthMessageFromRequest(req, logger, logLevel) { const debugLog = (0, authMiddlewareHelpers_js_1.makeDebugLogger)(logger, logLevel); debugLog('[buildAuthMessageFromRequest] Building message from request...', { path: req.path, headers: req.headers, method: req.method, body: req.body }); const writer = new sdk_1.Utils.Writer(); const requestNonce = req.headers['x-bsv-auth-request-id']; const requestNonceBytes = requestNonce ? sdk_1.Utils.toArray(requestNonce, 'base64') : []; writer.write(requestNonceBytes); writer.writeVarIntNum(req.method.length); writer.write(sdk_1.Utils.toArray(req.method)); const protocol = req.protocol; const host = req.get('host'); const parsedUrl = new URL(`${protocol}://${host}${req.originalUrl}`); (0, authMiddlewareHelpers_js_1.writeUrlToWriter)(parsedUrl, writer); (0, authMiddlewareHelpers_js_1.writeRequestHeadersToWriter)(req, writer); (0, authMiddlewareHelpers_js_1.writeBodyToWriter)(req, writer, logger, logLevel); const authMessage = { messageType: 'general', version: req.headers['x-bsv-auth-version'], identityKey: req.headers['x-bsv-auth-identity-key'], nonce: req.headers['x-bsv-auth-nonce'], yourNonce: req.headers['x-bsv-auth-your-nonce'], payload: writer.toArray(), signature: req.headers['x-bsv-auth-signature'] ? sdk_1.Utils.toArray(req.headers['x-bsv-auth-signature'], 'hex') : [] }; debugLog('[buildAuthMessageFromRequest] AuthMessage built', { authMessage }); return authMessage; } /** * Helper: Build response payload for sending back to peer */ function buildResponsePayload(requestId, responseStatus, responseHeaders, responseBody, req, logger, logLevel) { const debugLog = (0, authMiddlewareHelpers_js_1.makeDebugLogger)(logger, logLevel); debugLog('[buildResponsePayload] Building response payload', { requestId, responseStatus, responseHeaders, responseBodyLength: responseBody.length }); const writer = new sdk_1.Utils.Writer(); writer.write(sdk_1.Utils.toArray(requestId, 'base64')); writer.writeVarIntNum(responseStatus); // Filter out headers that should NOT be signed: // - Include custom headers prefixed with x-bsv (excluding those starting with x-bsv-auth) // - Include the authorization header const includedHeaders = []; Object.entries(responseHeaders).forEach(([key, value]) => { const lowerKey = key.toLowerCase(); if ((lowerKey.startsWith('x-bsv-') || lowerKey === 'authorization') && !lowerKey.startsWith('x-bsv-auth')) { includedHeaders.push([lowerKey, value]); } }); // Sort the headers by key to ensure a consistent order for signing and verification. includedHeaders.sort(([keyA], [keyB]) => keyA.localeCompare(keyB)); writer.writeVarIntNum(includedHeaders.length); for (const [headerKey, headerValue] of includedHeaders) { (0, authMiddlewareHelpers_js_1.writeHeaderPair)(writer, headerKey, headerValue); } if (responseBody.length > 0) { writer.writeVarIntNum(responseBody.length); writer.write(responseBody); } else { writer.writeVarIntNum(-1); } return writer.toArray(); } /** * Creates an Express middleware that handles authentication via BSV-SDK. * * @param {AuthMiddlewareOptions} options * @returns {(req: Request, res: Response, next: NextFunction) => void} Express middleware */ function createAuthMiddleware(options) { const { wallet, sessionManager, allowUnauthenticated, certificatesToRequest, onCertificatesReceived, logger, logLevel } = options; if (!wallet) { if (logger && logLevel && (0, authMiddlewareHelpers_js_1.isLogLevelEnabled)(logLevel, 'error')) { (0, authMiddlewareHelpers_js_1.getLogMethod)(logger, 'error')('[createAuthMiddleware] No wallet provided in AuthMiddlewareOptions.'); } throw new Error('You must configure the auth middleware with a wallet.'); } const transport = new ExpressTransport(allowUnauthenticated !== null && allowUnauthenticated !== void 0 ? allowUnauthenticated : false, logger, logLevel); const sessionMgr = sessionManager || new sdk_1.SessionManager(); if (logger && logLevel && (0, authMiddlewareHelpers_js_1.isLogLevelEnabled)(logLevel, 'info')) { (0, authMiddlewareHelpers_js_1.getLogMethod)(logger, 'info')(`[createAuthMiddleware] Creating Peer with provided wallet & transport. Session Manager: ${sessionManager ? 'Custom' : 'Default'}`); } const peer = new sdk_1.Peer(wallet, transport, certificatesToRequest, sessionMgr); transport.setPeer(peer); return (req, res, next) => { if (logger && logLevel && (0, authMiddlewareHelpers_js_1.isLogLevelEnabled)(logLevel, 'debug')) { (0, authMiddlewareHelpers_js_1.getLogMethod)(logger, 'debug')('[createAuthMiddleware] Incoming request to auth middleware', { path: req.path, headers: req.headers, method: req.method }); } void transport.handleIncomingRequest(req, res, next, onCertificatesReceived).catch(next); }; } //# sourceMappingURL=index.js.map