auth/middleware/Utility.js

/**
 * @module flitter-auth/middleware/Utility
 */

const Middleware = require('libflitter/middleware/Middleware')
const helpers = require('../Helpers')
const SecurityContext = require('../SecurityContext')

/**
 * This should be applied globally. Ensures basic things about the request
 * are true. For example, it provides the auth session data and handles auth
 * flow redirects.
 * @extends module:libflitter/middleware/Middleware~Middleware
 */
class Utility extends Middleware {
    /**
     * Defines the services required by this unit.
     * @returns {Array<string>}
     */
    static get services() {
        return [...super.services, 'models', 'output']
    }

    /**
     * Applies the middleware. Creates an unauthenticated auth session if one doesn't already exist.
     * If the user is authenticated, injects an instance of {module:flitter-auth/model/User~User} into
     * the request as 'request.user'.
     *
     * Also instantiates a {@link module:flitter-auth/SecurityContext~SecurityContext} for the request
     * and injects it as 'request.security'.
     *
     * If the session has a key_action_key property, the respective {@link module:flitter-auth/model/KeyAction~KeyAction}
     * will be inserted into "request.key_action".
     *
     * @param {express/request} req - the request
     * @param {express/response} res - the response
     * @param {function} next - the next function in the stack
     * @param {object} [args] - optional args
     * @returns {Promise<void>}
     */
    async test(req, res, next, args) {
        // Guarantees that req.session.auth is available.
        helpers.guarantee_auth_session(req)

        req.is_auth = !!req.session.auth.user_id

        // Inject the user object
        if ( req.is_auth ) {
            req.user = await this.models.get('auth:User').findById(req.session.auth.user_id)

            if ( !req.user ) {
                // If we have a user_id, but can't find a user in the database,
                // something has gone horribly wrong. To prevent security flaws,
                // reset the session to the logged-out state.
                this.output.warn(`Authenticated session with invalid user_id: ${req.session.auth.user_id}. Kicking user.`)
                delete req.user
                req.session.auth.user_id = false
                req.is_auth = false
            }
        }

        // Create a security context for the request
        const security = new SecurityContext(req, res)
        req.security = security

        if ( req.session.key_action_key ) {
            const KeyAction = this.models.get('auth:KeyAction')
            const ka = await KeyAction.findOne({key: req.session.key_action_key})
            if ( ka ) {
                req.key_action = ka
            } else {
                delete req.session.key_action_key
            }
        }

        /*
         * Call the next function in the stack.
         */
        next()
    }

}

module.exports = Utility