@sync-in/server
Version:
The secure, open-source platform for file storage, sharing, collaboration, and sync
174 lines (173 loc) • 9.02 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 _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