@sync-in/server
Version:
The secure, open-source platform for file storage, sharing, collaboration, and sync
197 lines (196 loc) • 10.2 kB
JavaScript
/*
* 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