refactor: include session ids prefix to have distinct tokens

This commit is contained in:
Nicolas Meienberger 2023-04-07 22:32:14 +02:00 committed by Nicolas Meienberger
parent 5e991ca7e5
commit fb9251d16e
5 changed files with 42 additions and 13 deletions

View file

@ -12,5 +12,15 @@ export const createClient = jest.fn(() => {
quit: jest.fn(),
del: (key: string) => values.delete(key),
ttl: (key: string) => expirations.get(key),
keys: (key: string) => {
const keys = [];
// eslint-disable-next-line no-restricted-syntax
for (const [k] of values) {
if (k.startsWith(key)) {
keys.push(k);
}
}
return keys;
},
};
});

View file

@ -1,9 +1,14 @@
import { type GetServerSidePropsContext } from 'next';
import jwt from 'jsonwebtoken';
import { v4 } from 'uuid';
import { getConfig } from '../core/TipiConfig';
import TipiCache from '../core/TipiCache';
import { Logger } from '../core/Logger';
export const generateSessionId = (prefix: string) => {
return `${prefix}-${v4()}`;
};
export const getServerAuthSession = async (ctx: { req: GetServerSidePropsContext['req']; res: GetServerSidePropsContext['res'] }) => {
const { req } = ctx;
const token = req.headers.authorization?.split(' ')[1];

View file

@ -48,6 +48,20 @@ class TipiCache {
return client.del(key);
}
public async delByValue(value: string, prefix = '') {
const client = await this.getClient();
const keys = await client.keys(`${prefix}*`);
const promises = keys.map(async (key) => {
const val = await client.get(key);
if (val === value) {
await client.del(key);
}
});
return Promise.all(promises);
}
public async close() {
return this.client.quit();
}

View file

@ -3,8 +3,8 @@ import fs from 'fs-extra';
import * as argon2 from 'argon2';
import jwt from 'jsonwebtoken';
import { faker } from '@faker-js/faker';
import { v4 } from 'uuid';
import { TotpAuthenticator } from '@/server/utils/totp';
import { generateSessionId } from '@/server/common/get-server-auth-session';
import { encrypt } from '../../utils/encryption';
import { setConfig } from '../../core/TipiConfig';
import { createUser } from '../../tests/user.factory';
@ -90,7 +90,7 @@ describe('Test: verifyTotp', () => {
const encryptedTotpSecret = encrypt(totpSecret, salt);
const user = await createUser({ email, totp_enabled: true, totp_secret: encryptedTotpSecret, salt }, db);
const totpSessionId = v4();
const totpSessionId = generateSessionId('otp');
const otp = TotpAuthenticator.generate(totpSecret);
await TipiCache.set(totpSessionId, user.id.toString());
@ -110,7 +110,7 @@ describe('Test: verifyTotp', () => {
const totpSecret = TotpAuthenticator.generateSecret();
const encryptedTotpSecret = encrypt(totpSecret, salt);
const user = await createUser({ email, totp_enabled: true, totp_secret: encryptedTotpSecret, salt }, db);
const totpSessionId = v4();
const totpSessionId = generateSessionId('otp');
await TipiCache.set(totpSessionId, user.id.toString());
// act & assert
@ -124,7 +124,7 @@ describe('Test: verifyTotp', () => {
const totpSecret = TotpAuthenticator.generateSecret();
const encryptedTotpSecret = encrypt(totpSecret, salt);
const user = await createUser({ email, totp_enabled: true, totp_secret: encryptedTotpSecret, salt }, db);
const totpSessionId = v4();
const totpSessionId = generateSessionId('otp');
const otp = TotpAuthenticator.generate(totpSecret);
await TipiCache.set(totpSessionId, user.id.toString());
@ -135,7 +135,7 @@ describe('Test: verifyTotp', () => {
it('should throw if the user does not exist', async () => {
// arrange
const totpSessionId = v4();
const totpSessionId = generateSessionId('otp');
await TipiCache.set(totpSessionId, '1234');
// act & assert
@ -149,7 +149,7 @@ describe('Test: verifyTotp', () => {
const totpSecret = TotpAuthenticator.generateSecret();
const encryptedTotpSecret = encrypt(totpSecret, salt);
const user = await createUser({ email, totp_enabled: false, totp_secret: encryptedTotpSecret, salt }, db);
const totpSessionId = v4();
const totpSessionId = generateSessionId('otp');
const otp = TotpAuthenticator.generate(totpSecret);
await TipiCache.set(totpSessionId, user.id.toString());

View file

@ -1,9 +1,9 @@
import { PrismaClient } from '@prisma/client';
import * as argon2 from 'argon2';
import { v4 } from 'uuid';
import jwt from 'jsonwebtoken';
import validator from 'validator';
import { TotpAuthenticator } from '@/server/utils/totp';
import { generateSessionId } from '@/server/common/get-server-auth-session';
import { getConfig } from '../../core/TipiConfig';
import TipiCache from '../../core/TipiCache';
import { fileExists, unlinkFile } from '../../common/fs.helpers';
@ -46,10 +46,10 @@ export class AuthServiceClass {
throw new Error('Wrong password');
}
const session = v4();
const session = generateSessionId('auth');
if (user.totp_enabled) {
const totpSessionId = v4();
const totpSessionId = generateSessionId('otp');
await TipiCache.set(totpSessionId, user.id.toString());
return { totpSessionId };
}
@ -94,7 +94,7 @@ export class AuthServiceClass {
throw new Error('Invalid TOTP code');
}
const session = v4();
const session = generateSessionId('otp');
const token = jwt.sign({ id: user.id, session }, getConfig().jwtSecret, { expiresIn: '7d' });
await TipiCache.set(session, user.id.toString());
@ -131,7 +131,7 @@ export class AuthServiceClass {
const newTotpSecret = TotpAuthenticator.generateSecret();
if (!salt) {
salt = v4();
salt = generateSessionId('');
}
const encryptedTotpSecret = encrypt(newTotpSecret, salt);
@ -241,7 +241,7 @@ export class AuthServiceClass {
const hash = await argon2.hash(password);
const newUser = await this.prisma.user.create({ data: { username: email, password: hash, operator: true } });
const session = v4();
const session = generateSessionId('auth');
const token = jwt.sign({ id: newUser.id, session }, getConfig().jwtSecret, { expiresIn: '1d' });
await TipiCache.set(session, newUser.id.toString());
@ -294,7 +294,7 @@ export class AuthServiceClass {
// Expire token in 6 seconds
await TipiCache.set(session, userId, 6);
const newSession = v4();
const newSession = generateSessionId('auth');
const token = jwt.sign({ id: userId, session: newSession }, getConfig().jwtSecret, { expiresIn: '1d' });
await TipiCache.set(newSession, userId);