refactor: include session ids prefix to have distinct tokens
This commit is contained in:
parent
5e991ca7e5
commit
fb9251d16e
5 changed files with 42 additions and 13 deletions
|
@ -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;
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue