UNPKG

@bsv/auth-express-middleware

Version:

BSV Blockchain mutual-authentication express middleware

753 lines 37.4 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 = void 0; exports.createAuthMiddleware = createAuthMiddleware; const fs_1 = __importDefault(require("fs")); const mime_types_1 = __importDefault(require("mime-types")); const sdk_1 = require("@bsv/sdk"); /** * Helper to determine if a given message-level log should be output * based on the configured log level. */ function isLogLevelEnabled(configuredLevel, messageLevel) { const levels = ['debug', 'info', 'warn', 'error']; const configuredIndex = levels.indexOf(configuredLevel); const messageIndex = levels.indexOf(messageLevel); return messageIndex >= configuredIndex; } /** * Retrieves the appropriate logging method from the logger, * falling back to `log` if not found. */ function getLogMethod(logger, level) { switch (level) { case 'debug': return typeof logger.debug === 'function' ? logger.debug.bind(logger) : logger.log.bind(logger); case 'info': // We'll map 'info' to console.info if available return typeof logger.info === 'function' ? logger.info.bind(logger) : logger.log.bind(logger); case 'warn': return typeof logger.warn === 'function' ? logger.warn.bind(logger) : logger.log.bind(logger); case 'error': return typeof logger.error === 'function' ? logger.error.bind(logger) : logger.log.bind(logger); default: return logger.log.bind(logger); } } /** * 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.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 (!isLogLevelEnabled(this.logLevel, level)) return; const logMethod = 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') { 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!'); } else { // Since this is an initial response, we can assume there's only one handle per identity const { res, next } = handles[0]; const responseHeaders = {}; responseHeaders['x-bsv-auth-version'] = message.version; responseHeaders['x-bsv-auth-message-type'] = message.messageType; 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); 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(); } } else { // General 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 = {}; const nHeaders = reader.readVarIntNum(); if (nHeaders > 0) { 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; } } 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(); } } } /** * Stores the callback bound by a Peer * @param callback */ async onData(callback) { this.log('debug', `onData callback set`); // Just store the callback 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. */ 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') { // Non-general message const message = req.body; this.log('debug', `Received non-general message at /.well-known/auth`, { message }); // Get a the request id 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 (!this.peer.sessionManager.hasSession(message.identityKey)) { const listenerId = this.peer.listenForCertificatesReceived((senderPublicKey, certs) => { var _a; this.log('debug', 'Certificates received event triggered', { senderPublicKey, certCount: certs === null || certs === void 0 ? void 0 : certs.length }); if (senderPublicKey !== req.body.identityKey) { return; } if (!Array.isArray(certs) || certs.length === 0) { this.log('warn', 'No certificates provided by peer', { senderPublicKey }); this.openNonGeneralHandles[senderPublicKey][0].res .status(400) .json({ status: 'No certificates provided' }); } else { this.log('info', 'Certificates successfully received from peer', { senderPublicKey, certs }); // this.openNonGeneralHandles[message.initialNonce!][0].res.json({ status: 'certificate received' }) if (typeof onCertificatesReceived === 'function') { onCertificatesReceived(senderPublicKey, certs, req, res, next); } const nextFn = this.openNextHandlers[message.identityKey]; if (typeof nextFn === 'function') { nextFn(); delete this.openNextHandlers[message.identityKey]; } } this.openNonGeneralHandles[message.initialNonce].shift(); (_a = this.peer) === null || _a === void 0 ? void 0 : _a.stopListeningForCertificatesReceived(listenerId); }); this.log('debug', 'listenForCertificatesReceived registered', { listenerId }); } 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.' }); }); } } else { // Possibly general message if (req.headers['x-bsv-auth-request-id']) { 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, _b, _c, _d; 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']) { this.log('debug', `General message from the correct identity key`, { requestId, senderPublicKey }); (_a = this.peer) === null || _a === void 0 ? void 0 : _a.stopListeningForGeneralMessages(listenerId); req.auth = { identityKey: senderPublicKey }; let responseStatus = 200; let responseHeaders = {}; let responseBody = []; // Override methods after checking res is clear this.checkRes(res, 'needs to be clear', next); res.__status = res.status; res.status = (n) => { responseStatus = n; return res; // Return res for chaining }; res.__set = res.set; res.set = (keyOrHeaders, value) => { if (typeof keyOrHeaders === 'object' && keyOrHeaders !== null) { // Handle setting multiple headers with an object for (const [key, val] of Object.entries(keyOrHeaders)) { if (typeof key !== 'string') { throw new TypeError(`Header name must be a string, received: ${typeof key}`); } responseHeaders[key.toLowerCase()] = String(val); // Ensure value is a string } } else if (typeof keyOrHeaders === 'string') { // Handle setting a single header if (typeof value === 'undefined') { throw new TypeError('Value must be provided when setting a single header'); } responseHeaders[keyOrHeaders.toLowerCase()] = String(value); // Ensure value is a string } else { throw new TypeError('Invalid arguments: res.set expects a string or an object'); } return res; // Return res for chaining }; const buildResponse = async () => { var _a; const payload = buildResponsePayload(requestId, responseStatus, responseHeaders, responseBody, req, this.logger, this.logLevel); this.openGeneralHandles[requestId] = { res, next }; this.log('debug', `Sending general message response`, { requestId, responseStatus, responseHeaders, responseBodyLength: responseBody.length }); await ((_a = this.peer) === null || _a === void 0 ? void 0 : _a.toPeer(payload, req.headers['x-bsv-auth-identity-key'])); }; res.__send = res.send; res.send = (val) => { // If the value is an object and no content-type is set, assume JSON if (typeof val === 'object' && val !== null && !responseHeaders['content-type']) { res.set('content-type', 'application/json'); } responseBody = convertValueToArray(val, responseHeaders); buildResponse(); }; res.__json = res.json; res.json = (obj) => { if (!responseHeaders['content-type']) { res.set('content-type', 'application/json'); } responseBody = sdk_1.Utils.toArray(JSON.stringify(obj), 'utf8'); buildResponse(); }; res.text = (str) => { if (!responseHeaders['content-type']) { res.set('content-type', 'text/plain'); } responseBody = sdk_1.Utils.toArray(str, 'utf8'); buildResponse(); }; res.__end = res.end; res.end = () => { buildResponse(); }; res.__sendFile = res.sendFile; res.sendFile = (path, options, callback) => { 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); res.status(500); return buildResponse(); } const mimeType = mime_types_1.default.lookup(path) || 'application/octet-stream'; res.set('Content-Type', mimeType); responseBody = Array.from(data); buildResponse(); }); }; if (((_d = (_c = (_b = this.peer) === null || _b === void 0 ? void 0 : _b.certificatesToRequest) === null || _c === void 0 ? void 0 : _c.certifiers) === null || _d === void 0 ? void 0 : _d.length) && Object.keys(this.openNextHandlers[senderPublicKey] || {}).length > 0) { this.openNextHandlers[senderPublicKey] = next; } else { next(); } } } catch (error) { this.log('error', `Error in listenForGeneralMessages callback`, { error }); next(error); } }); this.log('debug', `listenForGeneralMessages registered`, { listenerId }); if (this.messageCallback) { // Note: The requester may want more detailed error handling this.log('debug', `Invoking stored messageCallback for general message`); this.messageCallback(message).catch((err) => { this.log('error', `Error in messageCallback (general message)`, { error: err.message }); return res.status(500).json({ status: 'error', code: 'ERR_INTERNAL_SERVER_ERROR', description: err.message || 'An unknown error occurred.' }); }); } } else { // No auth headers 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!' }); } } } } catch (error) { this.log('error', `Caught error in handleIncomingRequest`, { error }); next(error); } } 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) { // Possibly log raw request details at debug level if (logger && logLevel && isLogLevelEnabled(logLevel, 'debug')) { getLogMethod(logger, 'debug')(`[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)); // Dynamically determine the base URL const protocol = req.protocol; // Ex. 'http' or 'https' const host = req.get('host'); // Ex. 'example.com:3000' const baseUrl = `${protocol}://${host}`; const parsedUrl = new URL(`${baseUrl}${req.originalUrl}`); // Pathname if (parsedUrl.pathname.length > 0) { const pathnameAsArray = sdk_1.Utils.toArray(parsedUrl.pathname); writer.writeVarIntNum(pathnameAsArray.length); writer.write(pathnameAsArray); } else { writer.writeVarIntNum(-1); } // Search if (parsedUrl.search.length > 0) { const searchAsArray = sdk_1.Utils.toArray(parsedUrl.search); writer.writeVarIntNum(searchAsArray.length); writer.write(searchAsArray); } else { writer.writeVarIntNum(-1); } // Parse request headers from client and include only the signed headers: // - Include custom headers prefixed with x-bsv (excluding those starting with x-bsv-auth) // - Include a normalized version of the content-type header // - Include the authorization header // Headers const includedHeaders = []; for (let [k, v] of Object.entries(req.headers)) { k = k.toLowerCase(); // Normalize the Content-Type header by removing any parameters. if (k === 'content-type') { v = v.split(';')[0].trim(); } if ((k.startsWith('x-bsv-') || k === 'content-type' || k === 'authorization') && !k.startsWith('x-bsv-auth')) { includedHeaders.push([k, v]); } } includedHeaders.sort(([keyA], [keyB]) => keyA.localeCompare(keyB)); writer.writeVarIntNum(includedHeaders.length); for (let i = 0; i < includedHeaders.length; i++) { const headerKeyAsArray = sdk_1.Utils.toArray(includedHeaders[i][0], 'utf8'); writer.writeVarIntNum(headerKeyAsArray.length); writer.write(headerKeyAsArray); const headerValueAsArray = sdk_1.Utils.toArray(includedHeaders[i][1], 'utf8'); writer.writeVarIntNum(headerValueAsArray.length); writer.write(headerValueAsArray); } // Body 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') : [] }; if (logger && logLevel && isLogLevelEnabled(logLevel, 'debug')) { getLogMethod(logger, 'debug')(`[buildAuthMessageFromRequest] AuthMessage built`, { authMessage }); } return authMessage; } /** * Helper: Write body to writer */ function writeBodyToWriter(req, writer, logger, logLevel) { const { body, headers } = req; if (Array.isArray(body) && body.every((item) => typeof item === 'number')) { // If the body is already a number[] writer.writeVarIntNum(body.length); writer.write(body); if (logger && logLevel && isLogLevelEnabled(logLevel, 'debug')) { getLogMethod(logger, 'debug')(`[writeBodyToWriter] Body recognized as number[]`, { length: body.length }); } } else if (body instanceof Uint8Array) { // If the body is a Uint8Array writer.writeVarIntNum(body.length); writer.write(Array.from(body)); // Convert Uint8Array to number[] if (logger && logLevel && isLogLevelEnabled(logLevel, 'debug')) { getLogMethod(logger, 'debug')(`[writeBodyToWriter] Body recognized as Uint8Array`, { length: body.length }); } } else if (headers['content-type'] === 'application/json' && typeof body === 'object') { // If the body is JSON const bodyAsArray = sdk_1.Utils.toArray(JSON.stringify(body), 'utf8'); writer.writeVarIntNum(bodyAsArray.length); writer.write(bodyAsArray); if (logger && logLevel && isLogLevelEnabled(logLevel, 'debug')) { getLogMethod(logger, 'debug')(`[writeBodyToWriter] Body recognized as JSON`, { body }); } } else if (headers['content-type'] === 'application/x-www-form-urlencoded' && body && Object.keys(body).length > 0) { // If the body is URL-encoded const parsedBody = new URLSearchParams(body).toString(); const bodyAsArray = sdk_1.Utils.toArray(parsedBody, 'utf8'); writer.writeVarIntNum(bodyAsArray.length); writer.write(bodyAsArray); if (logger && logLevel && isLogLevelEnabled(logLevel, 'debug')) { getLogMethod(logger, 'debug')(`[writeBodyToWriter] Body recognized as x-www-form-urlencoded`, { parsedBody }); } } else if (headers['content-type'] === 'text/plain' && typeof body === 'string' && body.length > 0) { // If the body is plain text const bodyAsArray = sdk_1.Utils.toArray(body, 'utf8'); writer.writeVarIntNum(bodyAsArray.length); writer.write(bodyAsArray); if (logger && logLevel && isLogLevelEnabled(logLevel, 'debug')) { getLogMethod(logger, 'debug')(`[writeBodyToWriter] Body recognized as text/plain`, { body }); } } else { // No valid body writer.writeVarIntNum(-1); if (logger && logLevel && isLogLevelEnabled(logLevel, 'debug')) { getLogMethod(logger, 'debug')(`[writeBodyToWriter] No valid body to write`); } } } /** * Helper: Build response payload for sending back to peer */ function buildResponsePayload(requestId, responseStatus, responseHeaders, responseBody, req, logger, logLevel) { if (logger && logLevel && isLogLevelEnabled(logLevel, 'debug')) { getLogMethod(logger, 'debug')(`[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 (let i = 0; i < includedHeaders.length; i++) { const headerKeyAsArray = sdk_1.Utils.toArray(includedHeaders[i][0], 'utf8'); writer.writeVarIntNum(headerKeyAsArray.length); writer.write(headerKeyAsArray); const headerValueAsArray = sdk_1.Utils.toArray(includedHeaders[i][1], 'utf8'); writer.writeVarIntNum(headerValueAsArray.length); writer.write(headerValueAsArray); } if (responseBody.length > 0) { writer.writeVarIntNum(responseBody.length); writer.write(responseBody); } else { writer.writeVarIntNum(-1); } return writer.toArray(); } /** * Helper: Convert values passed to res.send(...) into byte arrays */ function convertValueToArray(val, responseHeaders) { if (typeof val === 'string') { return sdk_1.Utils.toArray(val, 'utf8'); } else if (val instanceof Buffer) { return Array.from(val); } else if (typeof val === 'object') { if (val !== null) { if (!responseHeaders['content-type']) { responseHeaders['content-type'] = 'application/json'; } return sdk_1.Utils.toArray(JSON.stringify(val), 'utf8'); } } else if (typeof val === 'number') { return sdk_1.Utils.toArray(val.toString(), 'utf8'); } else { return sdk_1.Utils.toArray(String(val), 'utf8'); } return []; } /** * 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 && isLogLevelEnabled(logLevel, 'error')) { getLogMethod(logger, 'error')(`[createAuthMiddleware] No wallet provided in AuthMiddlewareOptions.`); } throw new Error('You must configure the auth middleware with a wallet.'); } // Construct transport with logging const transport = new ExpressTransport(allowUnauthenticated !== null && allowUnauthenticated !== void 0 ? allowUnauthenticated : false, logger, logLevel); const sessionMgr = sessionManager || new sdk_1.SessionManager(); if (logger && logLevel && isLogLevelEnabled(logLevel, 'info')) { 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 the express middleware return (req, res, next) => { if (logger && logLevel && isLogLevelEnabled(logLevel, 'debug')) { getLogMethod(logger, 'debug')(`[createAuthMiddleware] Incoming request to auth middleware`, { path: req.path, headers: req.headers, method: req.method }); } transport.handleIncomingRequest(req, res, next, onCertificatesReceived); }; } //# sourceMappingURL=index.js.map