Kyle Edwards

WeakMap for Private Request Properties

Typically when you introduce authorization into a web server, you’ll create a piece of middleware to reach out to the user authorization service, fetch all of the permissions and determine if the permission you want is correct, where a route is either authorized or unauthorized based on a permission key. Maybe there are some circumstances where the program logic needs to perform other checks, however you can’t exactly store the permissions on the request object where it can be modified or replaced (and you can’t really Object.freeze an entire request context).

By leveraging a WeakMap, we can use the request object itself as a key, and its value can be garbage collected, while the permissions can be isolated from malicious or inadvertent changes.

const requests = new WeakMap();

// Request helper
function isAuthorized(permission) {
    const storedModules = requests.get(this);
    return storedModules.includes(permission);
}

// Middleware
const middleware = async (request, response, next) => {
    permissions = await getPermissions(request);
    requests.set(request, permissions);

    // Add a new method to determine permission
    request.isAuthorized = isAuthorized.bind(request);

    next();
};

/* =========== */

const routeHandler = (request, response) => {
    if (request.isAuthorized('canPerformActionXYZ')) {
        // Yippee!
    } else {
        // Boo! This creep can't perform action XYZ!
    }
};

Benefits

Doesn’t add things to the request object that are accessible to other code

The WeakMap isn’t accessible from the request object, so as long as it’s not exported by the authorization module, there’s good isolation between controller code and permissions.

Avoids memory leaks

Adding the request to an array or a hashmap will cause a memory leak unless you remember to invalidate it when the request ends. However, WeakMap keys aren’t counted as references, and when the request falls out of scope it’s cleaned up in a future GC cycle.