@dollhousemcp/mcp-server
Version:
DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.
60 lines • 6.8 kB
JavaScript
/**
* Shared helpers for console auth route handlers (TOTP + token management).
*
* Extracted to eliminate duplication between totpRoutes.ts and tokenRoutes.ts.
* Both routers need the same error-mapping, structured-error-response, and
* body-field-extraction logic.
*
* @since v2.1.0 — Issue #1795
*/
import { UnicodeValidator } from '../../security/validators/unicodeValidator.js';
/**
* Maps a store error code to the appropriate HTTP status.
* Centralized so all console auth endpoints return consistent status codes
* for the same failure class. The `satisfies never` exhaustiveness check
* ensures new TotpErrorCode values get mapped at compile time.
*/
export function httpStatusForStoreError(code) {
switch (code) {
case 'ALREADY_ENROLLED':
return 409;
case 'NOT_ENROLLED':
case 'PENDING_NOT_FOUND':
case 'INVALID_TOTP_CODE':
return 400;
case 'TOO_MANY_PENDING':
return 429;
case 'TOTP_REQUIRED':
return 403;
case 'STORE_NOT_INITIALIZED':
return 503;
default: {
code;
return 400;
}
}
}
/**
* Send a structured error response with both a human-readable message and
* a machine-readable code. Shape is stable so the CLI and Security tab UI
* can branch on `code` instead of string-matching the message.
*/
export function sendStoreError(res, status, code, message) {
res.status(status).json({ error: message, code });
}
/**
* Safely extract a string field from an unknown request body and NFC-normalize
* it (DMCP-SEC-004). Returns null if the body is not an object or the field
* is missing / not a string. Normalization blocks homograph, bidi, and
* zero-width abuse before the value reaches downstream parsers (TOTP codes,
* otpauth URI labels, pending-id lookups).
*/
export function getNormalizedStringField(body, field) {
if (!body || typeof body !== 'object')
return null;
const val = body[field];
if (typeof val !== 'string')
return null;
return UnicodeValidator.normalize(val).normalizedContent;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc29sZVJvdXRlSGVscGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy93ZWIvcm91dGVzL2NvbnNvbGVSb3V0ZUhlbHBlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFJSCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSwrQ0FBK0MsQ0FBQztBQUVqRjs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSx1QkFBdUIsQ0FBQyxJQUFtQjtJQUN6RCxRQUFRLElBQUksRUFBRSxDQUFDO1FBQ2IsS0FBSyxrQkFBa0I7WUFDckIsT0FBTyxHQUFHLENBQUM7UUFDYixLQUFLLGNBQWMsQ0FBQztRQUNwQixLQUFLLG1CQUFtQixDQUFDO1FBQ3pCLEtBQUssbUJBQW1CO1lBQ3RCLE9BQU8sR0FBRyxDQUFDO1FBQ2IsS0FBSyxrQkFBa0I7WUFDckIsT0FBTyxHQUFHLENBQUM7UUFDYixLQUFLLGVBQWU7WUFDbEIsT0FBTyxHQUFHLENBQUM7UUFDYixLQUFLLHVCQUF1QjtZQUMxQixPQUFPLEdBQUcsQ0FBQztRQUNiLE9BQU8sQ0FBQyxDQUFDLENBQUM7WUFDUixJQUFvQixDQUFDO1lBQ3JCLE9BQU8sR0FBRyxDQUFDO1FBQ2IsQ0FBQztJQUNILENBQUM7QUFDSCxDQUFDO0FBRUQ7Ozs7R0FJRztBQUNILE1BQU0sVUFBVSxjQUFjLENBQzVCLEdBQWEsRUFDYixNQUFjLEVBQ2QsSUFBWSxFQUNaLE9BQWU7SUFFZixHQUFHLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztBQUNwRCxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxVQUFVLHdCQUF3QixDQUFDLElBQWEsRUFBRSxLQUFhO0lBQ25FLElBQUksQ0FBQyxJQUFJLElBQUksT0FBTyxJQUFJLEtBQUssUUFBUTtRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQ25ELE1BQU0sR0FBRyxHQUFJLElBQWdDLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDckQsSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDekMsT0FBTyxnQkFBZ0IsQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUMsaUJBQWlCLENBQUM7QUFDM0QsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU2hhcmVkIGhlbHBlcnMgZm9yIGNvbnNvbGUgYXV0aCByb3V0ZSBoYW5kbGVycyAoVE9UUCArIHRva2VuIG1hbmFnZW1lbnQpLlxuICpcbiAqIEV4dHJhY3RlZCB0byBlbGltaW5hdGUgZHVwbGljYXRpb24gYmV0d2VlbiB0b3RwUm91dGVzLnRzIGFuZCB0b2tlblJvdXRlcy50cy5cbiAqIEJvdGggcm91dGVycyBuZWVkIHRoZSBzYW1lIGVycm9yLW1hcHBpbmcsIHN0cnVjdHVyZWQtZXJyb3ItcmVzcG9uc2UsIGFuZFxuICogYm9keS1maWVsZC1leHRyYWN0aW9uIGxvZ2ljLlxuICpcbiAqIEBzaW5jZSB2Mi4xLjAg4oCUIElzc3VlICMxNzk1XG4gKi9cblxuaW1wb3J0IHR5cGUgeyBSZXNwb25zZSB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHR5cGUgeyBUb3RwRXJyb3JDb2RlIH0gZnJvbSAnLi4vY29uc29sZS9jb25zb2xlVG9rZW4uanMnO1xuaW1wb3J0IHsgVW5pY29kZVZhbGlkYXRvciB9IGZyb20gJy4uLy4uL3NlY3VyaXR5L3ZhbGlkYXRvcnMvdW5pY29kZVZhbGlkYXRvci5qcyc7XG5cbi8qKlxuICogTWFwcyBhIHN0b3JlIGVycm9yIGNvZGUgdG8gdGhlIGFwcHJvcHJpYXRlIEhUVFAgc3RhdHVzLlxuICogQ2VudHJhbGl6ZWQgc28gYWxsIGNvbnNvbGUgYXV0aCBlbmRwb2ludHMgcmV0dXJuIGNvbnNpc3RlbnQgc3RhdHVzIGNvZGVzXG4gKiBmb3IgdGhlIHNhbWUgZmFpbHVyZSBjbGFzcy4gVGhlIGBzYXRpc2ZpZXMgbmV2ZXJgIGV4aGF1c3RpdmVuZXNzIGNoZWNrXG4gKiBlbnN1cmVzIG5ldyBUb3RwRXJyb3JDb2RlIHZhbHVlcyBnZXQgbWFwcGVkIGF0IGNvbXBpbGUgdGltZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGh0dHBTdGF0dXNGb3JTdG9yZUVycm9yKGNvZGU6IFRvdHBFcnJvckNvZGUpOiBudW1iZXIge1xuICBzd2l0Y2ggKGNvZGUpIHtcbiAgICBjYXNlICdBTFJFQURZX0VOUk9MTEVEJzpcbiAgICAgIHJldHVybiA0MDk7XG4gICAgY2FzZSAnTk9UX0VOUk9MTEVEJzpcbiAgICBjYXNlICdQRU5ESU5HX05PVF9GT1VORCc6XG4gICAgY2FzZSAnSU5WQUxJRF9UT1RQX0NPREUnOlxuICAgICAgcmV0dXJuIDQwMDtcbiAgICBjYXNlICdUT09fTUFOWV9QRU5ESU5HJzpcbiAgICAgIHJldHVybiA0Mjk7XG4gICAgY2FzZSAnVE9UUF9SRVFVSVJFRCc6XG4gICAgICByZXR1cm4gNDAzO1xuICAgIGNhc2UgJ1NUT1JFX05PVF9JTklUSUFMSVpFRCc6XG4gICAgICByZXR1cm4gNTAzO1xuICAgIGRlZmF1bHQ6IHtcbiAgICAgIGNvZGUgc2F0aXNmaWVzIG5ldmVyO1xuICAgICAgcmV0dXJuIDQwMDtcbiAgICB9XG4gIH1cbn1cblxuLyoqXG4gKiBTZW5kIGEgc3RydWN0dXJlZCBlcnJvciByZXNwb25zZSB3aXRoIGJvdGggYSBodW1hbi1yZWFkYWJsZSBtZXNzYWdlIGFuZFxuICogYSBtYWNoaW5lLXJlYWRhYmxlIGNvZGUuIFNoYXBlIGlzIHN0YWJsZSBzbyB0aGUgQ0xJIGFuZCBTZWN1cml0eSB0YWIgVUlcbiAqIGNhbiBicmFuY2ggb24gYGNvZGVgIGluc3RlYWQgb2Ygc3RyaW5nLW1hdGNoaW5nIHRoZSBtZXNzYWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2VuZFN0b3JlRXJyb3IoXG4gIHJlczogUmVzcG9uc2UsXG4gIHN0YXR1czogbnVtYmVyLFxuICBjb2RlOiBzdHJpbmcsXG4gIG1lc3NhZ2U6IHN0cmluZyxcbik6IHZvaWQge1xuICByZXMuc3RhdHVzKHN0YXR1cykuanNvbih7IGVycm9yOiBtZXNzYWdlLCBjb2RlIH0pO1xufVxuXG4vKipcbiAqIFNhZmVseSBleHRyYWN0IGEgc3RyaW5nIGZpZWxkIGZyb20gYW4gdW5rbm93biByZXF1ZXN0IGJvZHkgYW5kIE5GQy1ub3JtYWxpemVcbiAqIGl0IChETUNQLVNFQy0wMDQpLiBSZXR1cm5zIG51bGwgaWYgdGhlIGJvZHkgaXMgbm90IGFuIG9iamVjdCBvciB0aGUgZmllbGRcbiAqIGlzIG1pc3NpbmcgLyBub3QgYSBzdHJpbmcuIE5vcm1hbGl6YXRpb24gYmxvY2tzIGhvbW9ncmFwaCwgYmlkaSwgYW5kXG4gKiB6ZXJvLXdpZHRoIGFidXNlIGJlZm9yZSB0aGUgdmFsdWUgcmVhY2hlcyBkb3duc3RyZWFtIHBhcnNlcnMgKFRPVFAgY29kZXMsXG4gKiBvdHBhdXRoIFVSSSBsYWJlbHMsIHBlbmRpbmctaWQgbG9va3VwcykuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXROb3JtYWxpemVkU3RyaW5nRmllbGQoYm9keTogdW5rbm93biwgZmllbGQ6IHN0cmluZyk6IHN0cmluZyB8IG51bGwge1xuICBpZiAoIWJvZHkgfHwgdHlwZW9mIGJvZHkgIT09ICdvYmplY3QnKSByZXR1cm4gbnVsbDtcbiAgY29uc3QgdmFsID0gKGJvZHkgYXMgUmVjb3JkPHN0cmluZywgdW5rbm93bj4pW2ZpZWxkXTtcbiAgaWYgKHR5cGVvZiB2YWwgIT09ICdzdHJpbmcnKSByZXR1cm4gbnVsbDtcbiAgcmV0dXJuIFVuaWNvZGVWYWxpZGF0b3Iubm9ybWFsaXplKHZhbCkubm9ybWFsaXplZENvbnRlbnQ7XG59XG4iXX0=