@foal/core
Version:
Full-featured Node.js framework, with no complexity
132 lines (131 loc) • 5.84 kB
JavaScript
;
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 });
}