UNPKG

@sync-in/server

Version:

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

174 lines (173 loc) 9.02 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 _config = require("@nestjs/config"); 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 _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)(this.authConfig.token.access.expiration); const refreshExpiration = refresh ? user.exp - currentTime : (0, _functions.convertHumanTimeToSeconds)(this.authConfig.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) { const response = new _loginresponsedto.LoginResponseDto(user); const currentTime = (0, _shared.currentTimeStamp)(); const csrfToken = _nodecrypto.default.randomUUID(); for (const type of _auth.TOKEN_TYPES){ const tokenExpiration = (0, _functions.convertHumanTimeToSeconds)(this.authConfig.token[type].expiration); const cookieValue = type === _tokeninterface.TOKEN_TYPE.CSRF ? csrfToken : await this.jwtSign(user, type, tokenExpiration, csrfToken); res.setCookie(this.authConfig.token[type].name, cookieValue, { signed: type === _tokeninterface.TOKEN_TYPE.CSRF, path: _auth.TOKEN_PATHS[type], maxAge: (0, _functions.convertHumanTimeToSeconds)(this.authConfig.token[type].cookieMaxAge), httpOnly: type !== _tokeninterface.TOKEN_TYPE.CSRF }); if (type === _tokeninterface.TOKEN_TYPE.ACCESS || type === _tokeninterface.TOKEN_TYPE.REFRESH) { response.token[`${type}_expiration`] = tokenExpiration + currentTime; } } return response; } async refreshCookies(user, res) { const response = {}; const currentTime = (0, _shared.currentTimeStamp)(); let refreshTokenExpiration; let refreshMaxAge; // refresh cookie must have the `exp` attribute // reuse token expiration to make it final if (user.exp && user.exp > currentTime) { refreshTokenExpiration = user.exp - currentTime; refreshMaxAge = refreshTokenExpiration; } 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.REFRESH ? refreshTokenExpiration : (0, _functions.convertHumanTimeToSeconds)(this.authConfig.token[type].expiration); const maxAge = type === _tokeninterface.TOKEN_TYPE.REFRESH ? refreshMaxAge : (0, _functions.convertHumanTimeToSeconds)(this.authConfig.token[type].cookieMaxAge); const cookieValue = type === _tokeninterface.TOKEN_TYPE.CSRF ? csrfToken : await this.jwtSign(user, type, tokenExpiration, csrfToken); res.setCookie(this.authConfig.token[type].name, cookieValue, { signed: type === _tokeninterface.TOKEN_TYPE.CSRF, path: _auth.TOKEN_PATHS[type], maxAge: maxAge, 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(this.authConfig.token[type].name, { path: path, httpOnly: type !== _tokeninterface.TOKEN_TYPE.CSRF }); } } jwtSign(user, type, expiration, csrfToken) { return this.jwt.signAsync({ identity: { id: user.id, login: user.login, email: user.email, fullName: user.fullName, role: user.role, applications: user.applications, impersonatedFromId: user.impersonatedFromId || undefined, impersonatedClientId: user.impersonatedClientId || undefined, clientId: user.clientId || undefined }, ...(type === _tokeninterface.TOKEN_TYPE.ACCESS || type === _tokeninterface.TOKEN_TYPE.REFRESH) && { csrf: csrfToken } }, { secret: this.authConfig.token[type].secret, expiresIn: expiration }); } csrfValidation(req, jwtPayload, type) { // ignore safe methods if (_applicationsconstants.HTTP_CSRF_IGNORED_METHODS.has(req.method)) { return; } // check csrf only for access & refresh cookies if (typeof req.cookies !== 'object' || req.cookies[this.authConfig.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], this.authConfig.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); } } constructor(jwt, config){ this.jwt = jwt; this.config = config; this.logger = new _common.Logger(AuthManager.name); this.authConfig = this.config.get('auth'); } }; AuthManager = _ts_decorate([ (0, _common.Injectable)(), _ts_metadata("design:type", Function), _ts_metadata("design:paramtypes", [ typeof _jwt.JwtService === "undefined" ? Object : _jwt.JwtService, typeof _config.ConfigService === "undefined" ? Object : _config.ConfigService ]) ], AuthManager); //# sourceMappingURL=auth-manager.service.js.map