UNPKG

@foal/core

Version:

Full-featured Node.js framework, with no complexity

132 lines (131 loc) 5.84 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.UseSessions = UseSessions; const core_1 = require("../../core"); const constants_1 = require("./constants"); const check_user_id_type_1 = require("./check-user-id-type"); const get_session_id_from_request_1 = require("./get-session-id-from-request"); const core_2 = require("../core"); const utils_1 = require("./utils"); function UseSessions(options = {}) { function badRequestOrRedirect(description) { if (options.redirectTo) { return new core_1.HttpResponseRedirect(options.redirectTo); } return new core_1.HttpResponseBadRequest({ code: 'invalid_request', description }); } function unauthorizedOrRedirect(description) { if (options.redirectTo) { return new core_1.HttpResponseRedirect(options.redirectTo); } return new core_1.HttpResponseUnauthorized({ code: 'invalid_token', description }) .setHeader('WWW-Authenticate', `error="invalid_token", error_description="${description}"`); } async function hook(ctx, services) { const ConcreteSessionStore = options.store || core_2.SessionStore; const store = services.get(ConcreteSessionStore); async function postFunction(response) { if (!(ctx.session) || (0, core_1.isHttpResponseInternalServerError)(response)) { return; } if (ctx.session.isDestroyed) { if (options.cookie) { (0, utils_1.removeSessionCookie)(response, !!options.userCookie); } return; } await ctx.session.commit(); if (options.cookie) { const userCookie = options.userCookie ? await options.userCookie(ctx, services) : undefined; (0, utils_1.setSessionCookie)(response, ctx.session, userCookie); } } /* Validate the request */ let sessionID; try { sessionID = (0, get_session_id_from_request_1.getSessionIDFromRequest)(ctx.request, options.cookie ? 'token-in-cookie' : 'token-in-header', !!options.required); } catch (error) { if (error instanceof get_session_id_from_request_1.RequestValidationError) { return badRequestOrRedirect(error.message); } // TODO: test this. throw error; } if (!sessionID) { if (options.create ?? options.cookie) { ctx.session = await (0, core_2.createSession)(store); } return postFunction; } /* Verify the session ID */ const session = await (0, core_2.readSession)(store, sessionID); if (!session) { const response = unauthorizedOrRedirect('token invalid or expired'); if (options.cookie) { (0, utils_1.removeSessionCookie)(response, !!options.userCookie); } return response; } /* Verify CSRF token */ if ((0, utils_1.shouldVerifyCsrfToken)(ctx.request, options)) { const expectedCsrftoken = session.get('csrfToken'); if (!expectedCsrftoken) { throw new Error('Unexpected error: the session content does not have a "csrfToken" field. ' + 'Are you sure you created the session with "createSession"?'); } const actualCsrfToken = (0, utils_1.getCsrfTokenFromRequest)(ctx.request); if (actualCsrfToken !== expectedCsrftoken) { return new core_1.HttpResponseForbidden('CSRF token missing or incorrect.'); } } /* Set ctx.session */ ctx.session = session; /* Set ctx.user */ const logger = services.get(core_1.Logger); logger.addLogContext({ userId: session.userId }); if (session.userId !== null && options.user) { const userId = (0, check_user_id_type_1.checkUserIdType)(session.userId, options.userIdType); ctx.user = await options.user(userId, services); if (!ctx.user) { await session.destroy(); const response = unauthorizedOrRedirect('The token does not match any user.'); if (options.cookie) { (0, utils_1.removeSessionCookie)(response, !!options.userCookie); } return response; } } return postFunction; } const openapi = [ options.required ? (0, core_1.ApiResponse)(401, { description: 'Auth token is missing or invalid.' }) : (0, core_1.ApiResponse)(401, { description: 'Auth token is invalid.' }) ]; if (options.cookie) { const securityScheme = { in: 'cookie', name: core_1.Config.get('settings.session.cookie.name', 'string', constants_1.SESSION_DEFAULT_COOKIE_NAME), type: 'apiKey', }; openapi.push((0, core_1.ApiDefineSecurityScheme)('cookieAuth', securityScheme)); if (options.required) { openapi.push((0, core_1.ApiSecurityRequirement)({ cookieAuth: [] })); } if (core_1.Config.get('settings.session.csrf.enabled', 'boolean', false)) { openapi.push((0, core_1.ApiResponse)(403, { description: 'CSRF token is missing or incorrect.' })); } } else { const securityScheme = { scheme: 'bearer', type: 'http', }; openapi.push((0, core_1.ApiDefineSecurityScheme)('bearerAuth', securityScheme)); if (options.required) { openapi.push((0, core_1.ApiSecurityRequirement)({ bearerAuth: [] })); } } return (0, core_1.Hook)(hook, openapi, { openapi: options.openapi }); }