@sync-in/server
Version:
The secure, open-source platform for file storage, sharing, collaboration, and sync
340 lines (339 loc) • 12.5 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
});
const _config = require("@nestjs/config");
const _jwt = require("@nestjs/jwt");
const _appbootstrap = require("../app.bootstrap");
const _user = require("../applications/users/constants/user");
const _usermodel = require("../applications/users/models/user.model");
const _adminusersmanagerservice = require("../applications/users/services/admin-users-manager.service");
const _test = require("../applications/users/utils/test");
const _functions = require("../common/functions");
const _shared = require("../common/shared");
const _utils = require("../infrastructure/database/utils");
const _auth = require("./constants/auth");
const _routes = require("./constants/routes");
const _tokenresponsedto = require("./dto/token-response.dto");
const _tokeninterface = require("./interfaces/token.interface");
describe('Auth (e2e)', ()=>{
let app;
let authConfig;
let jwtService;
let adminUsersManager;
let userTest;
let refreshToken;
let csrfToken;
beforeAll(async ()=>{
app = await (0, _appbootstrap.appBootstrap)();
await app.init();
await app.getHttpAdapter().getInstance().ready();
authConfig = app.get(_config.ConfigService).get('auth');
jwtService = app.get(_jwt.JwtService);
adminUsersManager = app.get(_adminusersmanagerservice.AdminUsersManager);
userTest = new _usermodel.UserModel((0, _test.generateUserTest)(false), false);
});
afterAll(async ()=>{
await expect(adminUsersManager.deleteUserOrGuest(userTest.id, userTest.login, {
deleteSpace: true
})).resolves.not.toThrow();
await (0, _utils.dbCloseConnection)(app);
await app.close();
});
it('should be defined', ()=>{
expect(authConfig).toBeDefined();
expect(jwtService).toBeDefined();
expect(adminUsersManager).toBeDefined();
expect(userTest).toBeDefined();
});
it('should get the database connection', async ()=>{
expect(await (0, _utils.dbCheckConnection)(app)).toBe(true);
});
it(`POST ${_routes.API_AUTH_LOGIN} => 401`, async ()=>{
const res = await app.inject({
method: 'POST',
url: _routes.API_AUTH_LOGIN,
body: {
login: userTest.login,
password: userTest.password
}
});
expect(res.statusCode).toEqual(401);
});
it(`POST ${_routes.API_AUTH_LOGIN} => 201`, async ()=>{
const userId = (await adminUsersManager.createUserOrGuest({
...userTest
}, _user.USER_ROLE.USER)).id;
expect(userId).toBeDefined();
userTest.id = userId;
const res = await app.inject({
method: 'POST',
url: _routes.API_AUTH_LOGIN,
body: {
login: userTest.login,
password: userTest.password
}
});
expect(res.statusCode).toEqual(201);
expect(Object.keys(res.json())).toEqual(expect.arrayContaining([
'user',
'token'
]));
expect(res.headers['set-cookie']).toHaveLength(4);
const cookies = getCookies(res.headers['set-cookie']);
/* Access cookie
[
'sync-in-access=value,
'Max-Age=3600',
'Path=/',
'HttpOnly',
'Secure',
'SameSite=Strict'
]
*/ /* Refresh cookie
[
'sync-in-refresh=value,
'Max-Age=14400',
'Path=/api/auth/refresh',
'HttpOnly',
'Secure',
'SameSite=Strict'
]
*/ /* WS cookie
[
'sync-in-ws=value,
'Max-Age=14400',
'Path=/socket.io',
'HttpOnly',
'Secure',
'SameSite=Strict'
]
*/ /* CSRF cookie
[
'sync-in-csrf=value,
'Max-Age=14400',
'Path=/',
'Secure',
'SameSite=Strict'
]
*/ cookiesChecks(cookies);
// Verify token
for (const cookie of cookies){
const token = cookie.content[0].substring(cookie.content[0].indexOf('=') + 1);
if (cookie.type === _tokeninterface.TOKEN_TYPE.CSRF) {
// needed for the following tests
csrfToken = (0, _shared.decodeUrl)(token);
continue;
}
const decodedToken = await jwtService.verifyAsync(token, {
secret: authConfig.token[cookie.type].secret
});
expect(decodedToken.iat).toBeCloseTo((0, _shared.currentTimeStamp)(), -1);
expect(decodedToken.exp).toBeCloseTo((0, _shared.currentTimeStamp)() + (0, _functions.convertHumanTimeToSeconds)(authConfig.token[cookie.type].expiration), -1);
expect(decodedToken.identity.id).toBe(userTest.id);
if (cookie.type === _tokeninterface.TOKEN_TYPE.REFRESH) {
// needed for the following tests
refreshToken = token;
}
}
});
it(`POST ${_routes.API_AUTH_LOGOUT} => 201`, async ()=>{
const res = await app.inject({
method: 'POST',
url: _routes.API_AUTH_LOGOUT,
body: null
});
expect(res.statusCode).toEqual(201);
expect(res.headers['set-cookie']).toHaveLength(4);
const cookies = getCookies(res.headers['set-cookie']);
/* Access cookie
[
'sync-in-access=',
'Max-Age=0',
'Path=/',
'Expires=Thu, 01 Jan 1970 00:00:00 GMT',
'HttpOnly',
'Secure',
'SameSite=Strict'
]
*/ /* Refresh cookie
[
'sync-in-refresh=',
'Max-Age=0',
'Path=/api/auth/refresh',
'Expires=Thu, 01 Jan 1970 00:00:00 GMT',
'HttpOnly',
'Secure',
'SameSite=Strict'
]
*/ /* WS cookie
[
'sync-in-ws=',
'Max-Age=0',
'Path=/socket.io',
'Expires=Thu, 01 Jan 1970 00:00:00 GMT',
'HttpOnly',
'Secure',
'SameSite=Strict'
]
*/ /* CSRF cookie
[
'sync-in-csrf=',
'Max-Age=0',
'Path=/api/auth/refresh',
'Expires=Thu, 01 Jan 1970 00:00:00 GMT',
'HttpOnly',
'Secure',
'SameSite=Strict'
]
*/ cookiesChecks(cookies, true);
});
it(`POST ${_routes.API_AUTH_REFRESH} => 201`, async ()=>{
const res = await app.inject({
method: 'POST',
headers: {
[authConfig.token.csrf.name]: csrfToken
},
url: _routes.API_AUTH_REFRESH,
cookies: {
[authConfig.token.refresh.name]: refreshToken
}
});
expect(res.statusCode).toEqual(201);
const cookies = getCookies(res.headers['set-cookie']);
cookiesChecks(cookies);
});
it(`POST ${_routes.API_AUTH_REFRESH} => 401 (with CSRF)`, async ()=>{
const res = await app.inject({
method: 'POST',
url: _routes.API_AUTH_REFRESH,
headers: {
[authConfig.token.csrf.name]: csrfToken
},
cookies: {
[authConfig.token.refresh.name]: 'bar'
}
});
expect(res.statusCode).toEqual(401);
});
it(`POST ${_routes.API_AUTH_REFRESH} => 403 (without CSRF)`, async ()=>{
const res = await app.inject({
method: 'POST',
url: _routes.API_AUTH_REFRESH,
cookies: {
[authConfig.token.refresh.name]: refreshToken
}
});
expect(res.statusCode).toEqual(403);
expect(res.json().message).toEqual(_auth.CSRF_ERROR.MISSING_HEADERS);
});
it(`POST ${_routes.API_AUTH_TOKEN} => 401`, async ()=>{
const res = await app.inject({
method: 'POST',
url: _routes.API_AUTH_TOKEN,
body: {
login: userTest.login,
password: 'bar'
}
});
expect(res.statusCode).toEqual(401);
});
it(`POST ${_routes.API_AUTH_TOKEN} => 201`, async ()=>{
const res = await app.inject({
method: 'POST',
url: _routes.API_AUTH_TOKEN,
body: {
login: userTest.login,
password: userTest.password
}
});
expect(res.statusCode).toEqual(201);
const content = res.json();
expect(()=>(0, _functions.transformAndValidate)(_tokenresponsedto.TokenResponseDto, content)).not.toThrow();
for (const type of _auth.TOKEN_TYPES.filter((p)=>p === _tokeninterface.TOKEN_TYPE.ACCESS || p === _tokeninterface.TOKEN_TYPE.REFRESH)){
expect(content[type]).toBeDefined();
expect(content[`${type}_expiration`]).toBeCloseTo((0, _shared.currentTimeStamp)() + (0, _functions.convertHumanTimeToSeconds)(authConfig.token[type].expiration), -1);
}
});
it(`POST ${_routes.API_AUTH_TOKEN_REFRESH} => 401`, async ()=>{
const res = await app.inject({
method: 'POST',
url: _routes.API_AUTH_TOKEN_REFRESH,
headers: {
authorization: 'Bearer bar'
}
});
expect(res.statusCode).toEqual(401);
});
it(`POST ${_routes.API_AUTH_TOKEN_REFRESH} => 201`, async ()=>{
const res = await app.inject({
method: 'POST',
url: _routes.API_AUTH_TOKEN_REFRESH,
headers: {
authorization: `Bearer ${refreshToken}`
}
});
expect(res.statusCode).toEqual(201);
expect(()=>(0, _functions.transformAndValidate)(_tokenresponsedto.TokenResponseDto, res.json())).not.toThrow();
});
function getCookies(setCookie) {
const cookies = [];
for (const c of setCookie){
const cookieName = c.split('=')[0];
const cookieValues = c.split('; ');
switch(cookieName){
case authConfig.token.access.name:
cookies.push({
type: _tokeninterface.TOKEN_TYPE.ACCESS,
content: cookieValues
});
break;
case authConfig.token.refresh.name:
cookies.push({
type: _tokeninterface.TOKEN_TYPE.REFRESH,
content: cookieValues
});
break;
case authConfig.token.ws.name:
cookies.push({
type: _tokeninterface.TOKEN_TYPE.WS,
content: cookieValues
});
break;
case authConfig.token.csrf.name:
cookies.push({
type: _tokeninterface.TOKEN_TYPE.CSRF,
content: cookieValues
});
break;
}
}
return cookies;
}
function cookiesChecks(cookies, clear = false) {
for (const cookie of cookies){
expect(cookie.content[0].split('=')[0]).toBe(authConfig.token[cookie.type].name);
expect(cookie.content[2].split('=')[1]).toBe(_auth.TOKEN_PATHS[cookie.type]);
if (cookie.type === _tokeninterface.TOKEN_TYPE.CSRF) {
expect(cookie.content).not.toContain('HttpOnly');
} else {
expect(cookie.content).toContain('HttpOnly');
}
expect(cookie.content).not.toContain('Secure');
expect(cookie.content[cookie.content.length - 1].split('=')[1].toLowerCase()).toBe(authConfig.sameSite);
if (clear) {
expect(cookie.content[0].split('=')[1]).toBe('');
expect(cookie.content[1].split('=')[1]).toBe('0');
expect(cookie.content[3].split('=')[1]).toBe('Thu, 01 Jan 1970 00:00:00 GMT');
} else {
expect(parseInt(cookie.content[1].split('=')[1])).toBeCloseTo((0, _functions.convertHumanTimeToSeconds)(authConfig.token[cookie.type].cookieMaxAge), -1);
expect(cookie.content[0].split('=')[1]).not.toBe('');
}
}
}
});
//# sourceMappingURL=auth.e2e-spec.js.map