UNPKG

@sync-in/server

Version:

The secure, open-source platform for file storage, sharing, collaboration, and sync

197 lines (196 loc) 10.2 kB
/* * Copyright (C) 2012-2025 Johan Legrand <johan.legrand@sync-in.com> * This file is part of Sync-in | The open source file sync and share solution * See the LICENSE file for licensing details */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "AuthManager", { enumerable: true, get: function() { return AuthManager; } }); const _cookie = require("@fastify/cookie"); const _common = require("@nestjs/common"); const _jwt = require("@nestjs/jwt"); const _nodecrypto = /*#__PURE__*/ _interop_require_default(require("node:crypto")); const _applicationsconstants = require("../../applications/applications.constants"); const _functions = require("../../common/functions"); const _shared = require("../../common/shared"); const _configenvironment = require("../../configuration/config.environment"); const _auth = require("../constants/auth"); const _loginresponsedto = require("../dto/login-response.dto"); const _tokeninterface = require("../interfaces/token.interface"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _ts_decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function _ts_metadata(k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); } let AuthManager = class AuthManager { async getTokens(user, refresh = false) { const currentTime = (0, _shared.currentTimeStamp)(); if (refresh && user.exp < currentTime) { this.logger.error(`${this.getTokens.name} - token refresh has incorrect expiration : *${user.login}*`); throw new _common.HttpException('Token has expired', _common.HttpStatus.FORBIDDEN); } const accessExpiration = (0, _functions.convertHumanTimeToSeconds)(_configenvironment.configuration.auth.token.access.expiration); const refreshExpiration = refresh ? user.exp - currentTime : (0, _functions.convertHumanTimeToSeconds)(_configenvironment.configuration.auth.token.refresh.expiration); return { [_tokeninterface.TOKEN_TYPE.ACCESS]: await this.jwtSign(user, _tokeninterface.TOKEN_TYPE.ACCESS, accessExpiration), [_tokeninterface.TOKEN_TYPE.REFRESH]: await this.jwtSign(user, _tokeninterface.TOKEN_TYPE.REFRESH, refreshExpiration), [`${_tokeninterface.TOKEN_TYPE.ACCESS}_expiration`]: accessExpiration + currentTime, [`${_tokeninterface.TOKEN_TYPE.REFRESH}_expiration`]: refreshExpiration + currentTime }; } async setCookies(user, res, init2FaVerify = false) { // If `verify2Fa` is true, it sets the cookies and response required for valid 2FA authentication. const verify2Fa = init2FaVerify && _configenvironment.configuration.auth.mfa.totp.enabled && user.twoFaEnabled; const response = verify2Fa ? new _loginresponsedto.LoginVerify2FaDto(_configenvironment.serverConfig) : new _loginresponsedto.LoginResponseDto(user, _configenvironment.serverConfig); const currentTime = (0, _shared.currentTimeStamp)(); const csrfToken = _nodecrypto.default.randomUUID(); const tokenTypes = verify2Fa ? _auth.TOKEN_2FA_TYPES : _auth.TOKEN_TYPES; for (const type of tokenTypes){ const isCSRFToken = type === _tokeninterface.TOKEN_TYPE.CSRF || type === _tokeninterface.TOKEN_TYPE.CSRF_2FA; const tokenExpiration = (0, _functions.convertHumanTimeToSeconds)(_configenvironment.configuration.auth.token[type].expiration); let cookieValue; if (isCSRFToken) { cookieValue = csrfToken; } else if (verify2Fa) { cookieValue = await this.jwtSign2Fa(user, type, tokenExpiration, csrfToken); } else { cookieValue = await this.jwtSign(user, type, tokenExpiration, csrfToken); } res.setCookie(_configenvironment.configuration.auth.token[type].name, cookieValue, { signed: isCSRFToken, path: _auth.TOKEN_PATHS[type], maxAge: tokenExpiration, httpOnly: !isCSRFToken }); if (type === _tokeninterface.TOKEN_TYPE.ACCESS || type === _tokeninterface.TOKEN_TYPE.REFRESH || type === _tokeninterface.TOKEN_TYPE.ACCESS_2FA) { response.token[`${type}_expiration`] = tokenExpiration + currentTime; } } return response; } async refreshCookies(user, res) { const response = {}; const currentTime = (0, _shared.currentTimeStamp)(); let refreshTokenExpiration; // refresh cookie must have the `exp` attribute // reuse token expiration to make it final if (user.exp && user.exp > currentTime) { refreshTokenExpiration = user.exp - currentTime; } else { this.logger.error(`${this.refreshCookies.name} - token ${_tokeninterface.TOKEN_TYPE.REFRESH} has incorrect expiration : *${user.login}*`); throw new _common.HttpException('Token has expired', _common.HttpStatus.FORBIDDEN); } const csrfToken = _nodecrypto.default.randomUUID(); for (const type of _auth.TOKEN_TYPES){ const tokenExpiration = type === _tokeninterface.TOKEN_TYPE.ACCESS ? (0, _functions.convertHumanTimeToSeconds)(_configenvironment.configuration.auth.token[_tokeninterface.TOKEN_TYPE.ACCESS].expiration) : refreshTokenExpiration; const cookieValue = type === _tokeninterface.TOKEN_TYPE.CSRF ? csrfToken : await this.jwtSign(user, type, tokenExpiration, csrfToken); res.setCookie(_configenvironment.configuration.auth.token[type].name, cookieValue, { signed: type === _tokeninterface.TOKEN_TYPE.CSRF, path: _auth.TOKEN_PATHS[type], maxAge: tokenExpiration, httpOnly: type !== _tokeninterface.TOKEN_TYPE.CSRF }); if (type === _tokeninterface.TOKEN_TYPE.ACCESS || type === _tokeninterface.TOKEN_TYPE.REFRESH) { response[`${type}_expiration`] = tokenExpiration + currentTime; } } return response; } async clearCookies(res) { for (const [type, path] of Object.entries(_auth.TOKEN_PATHS)){ res.clearCookie(_configenvironment.configuration.auth.token[type].name, { path: path, httpOnly: type !== _tokeninterface.TOKEN_TYPE.CSRF }); } } csrfValidation(req, jwtPayload, type) { // ignore safe methods if (_applicationsconstants.HTTP_CSRF_IGNORED_METHODS.has(req.method)) { return; } // check csrf only for access and refresh cookies if (typeof req.cookies !== 'object' || req.cookies[_configenvironment.configuration.auth.token[type].name] === undefined) { return; } if (!jwtPayload.csrf) { this.logger.warn(`${this.csrfValidation.name} - ${_auth.CSRF_ERROR.MISSING_JWT}`); throw new _common.HttpException(_auth.CSRF_ERROR.MISSING_JWT, _common.HttpStatus.FORBIDDEN); } if (!req.headers[_auth.CSRF_KEY]) { this.logger.warn(`${this.csrfValidation.name} - ${_auth.CSRF_ERROR.MISSING_HEADERS}`); throw new _common.HttpException(_auth.CSRF_ERROR.MISSING_HEADERS, _common.HttpStatus.FORBIDDEN); } const csrfHeader = (0, _cookie.unsign)(req.headers[_auth.CSRF_KEY], _configenvironment.configuration.auth.token.csrf.secret); if (jwtPayload.csrf !== csrfHeader.value) { this.logger.warn(`${this.csrfValidation.name} - ${_auth.CSRF_ERROR.MISMATCH}`); throw new _common.HttpException(_auth.CSRF_ERROR.MISMATCH, _common.HttpStatus.FORBIDDEN); } } jwtSign(user, type, expiration, csrfToken) { return this.jwt.signAsync({ identity: { id: user.id, login: user.login, email: user.email, fullName: user.fullName, language: user.language, role: user.role, applications: user.applications, impersonatedFromId: user.impersonatedFromId || undefined, impersonatedClientId: user.impersonatedClientId || undefined, clientId: user.clientId || undefined, twoFaEnabled: user.twoFaEnabled || undefined }, ...(type === _tokeninterface.TOKEN_TYPE.ACCESS || type === _tokeninterface.TOKEN_TYPE.REFRESH) && { csrf: csrfToken } }, { secret: _configenvironment.configuration.auth.token[type].secret, expiresIn: expiration }); } jwtSign2Fa(user, type, expiration, csrfToken) { // Restrict the temporary token to the minimum required information return this.jwt.signAsync({ identity: { id: user.id, login: user.login, language: user.language, role: user.role, twoFaEnabled: true }, csrf: csrfToken }, { secret: _configenvironment.configuration.auth.token[type].secret, expiresIn: expiration }); } constructor(jwt){ this.jwt = jwt; this.logger = new _common.Logger(AuthManager.name); } }; AuthManager = _ts_decorate([ (0, _common.Injectable)(), _ts_metadata("design:type", Function), _ts_metadata("design:paramtypes", [ typeof _jwt.JwtService === "undefined" ? Object : _jwt.JwtService ]) ], AuthManager); //# sourceMappingURL=auth-manager.service.js.map