Skip to content

Commit

Permalink
fix: added future Iat to new tokens on password change
Browse files Browse the repository at this point in the history
  • Loading branch information
apsantiso committed Oct 3, 2024
1 parent d502210 commit 3236f07
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 6 deletions.
11 changes: 9 additions & 2 deletions src/middlewares/passport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ export function SignEmail(
email: string,
secret: string,
expires = false,
customIat?: number,
): string {
const payload = { email, ...(customIat ? { iat: customIat } : null) };

const token = expires
? jwt.sign({ email }, secret, { expiresIn: '14d' })
: jwt.sign({ email }, secret);
? jwt.sign(payload, secret, { expiresIn: '14d' })
: jwt.sign(payload, secret);

return token;
}
Expand All @@ -23,6 +26,10 @@ export function Sign(payload: object, secret: string, expires = false): string {
return token;
}

export function getFutureIAT() {
return Math.floor(Date.now() / 1000) + 60;
}

export function SignWithCustomDuration(
payload: object,
secret: string,
Expand Down
70 changes: 70 additions & 0 deletions src/middlewares/passsword.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import jwt from 'jsonwebtoken';
import {
SignEmail,
Sign,
getFutureIAT,
SignWithCustomDuration,
} from './passport';

describe('JWT Utility Functions', () => {
const secret = 'test_secret';
const payload = { userId: '12345' };

describe('SignEmail', () => {
it('When called with email and secret, then it returns a valid token', () => {
const token = SignEmail('[email protected]', secret);
const decoded = jwt.verify(token, secret);

expect(decoded).toHaveProperty('email', '[email protected]');
});

it('When called with custom iat, then it sets the iat', () => {
const customIat = Math.floor(Date.now() / 1000 + 60);
const token = SignEmail('[email protected]', secret, false, customIat);
const decoded = jwt.verify(token, secret);

expect(decoded).toHaveProperty('iat', customIat);
});

it('When token should expire, then it sets an expiration date', () => {
const token = SignEmail('[email protected]', secret, true);
const decoded = jwt.decode(token, { complete: true }) as any;

expect(decoded.payload).toHaveProperty('exp');
});
});

describe('Sign', () => {
it('When called with a payload and secret, then it returns a valid token', () => {
const token = Sign(payload, secret);
const decoded = jwt.verify(token, secret);

expect(decoded).toMatchObject(payload);
});

it('When token should expire, then it sets an expiration date', () => {
const token = Sign(payload, secret, true);
const decoded = jwt.decode(token, { complete: true }) as any;

expect(decoded.payload).toHaveProperty('exp');
});
});

describe('getFutureIAT', () => {
it('When called, then it returns a future IAT timestamp', () => {
const futureIAT = getFutureIAT();
const currentTime = Math.floor(Date.now() / 1000);

expect(futureIAT).toBeGreaterThan(currentTime);
});
});

describe('SignWithCustomDuration', () => {
it('When called with a custom expiration, then it sets the correct expiration', () => {
const token = SignWithCustomDuration(payload, secret, '7d');
const decoded = jwt.decode(token, { complete: true }) as any;

expect(decoded.payload).toHaveProperty('exp');
});
});
});
7 changes: 6 additions & 1 deletion src/modules/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import { CreateAttemptChangeEmailDto } from './dto/create-attempt-change-email.d
import { HttpExceptionFilter } from '../../lib/http/http-exception.filter';
import { RequestAccountUnblock } from './dto/account-unblock.dto';
import { RegisterNotificationTokenDto } from './dto/register-notification-token.dto';
import { getFutureIAT } from '../../middlewares/passport';

@ApiTags('User')
@Controller('users')
Expand Down Expand Up @@ -450,7 +451,11 @@ export class UserController {
privateKey,
encryptVersion,
});
const { token, newToken } = this.userUseCases.getAuthTokens(user);

const { token, newToken } = this.userUseCases.getAuthTokens(
user,
getFutureIAT(),
);

return { status: 'success', newToken, token };
} catch (err) {
Expand Down
57 changes: 55 additions & 2 deletions src/modules/user/user.usecase.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ import { Folder, FolderAttributes } from '../folder/folder.domain';
import { File, FileAttributes } from '../file/file.domain';
import { SequelizeAttemptChangeEmailRepository } from './attempt-change-email.repository';
import { BadRequestException, ForbiddenException } from '@nestjs/common';
import { SignWithCustomDuration } from '../../middlewares/passport';
import {
Sign,
SignEmail,
SignWithCustomDuration,
} from '../../middlewares/passport';
import { getTokenDefaultIat } from '../../lib/jwt';
import { UserNotFoundException } from './exception/user-not-found.exception';
import { AttemptChangeEmailNotFoundException } from './exception/attempt-change-email-not-found.exception';
Expand Down Expand Up @@ -600,7 +604,7 @@ describe('User use cases', () => {
});

jest.spyOn(userUseCases, 'getNewTokenPayload').mockReturnValue({} as any);
jest.spyOn(configService, 'get').mockReturnValue('a-secret-key');
jest.spyOn(configService, 'get').mockReturnValueOnce('a-secret-key');

const result = await userUseCases.acceptAttemptChangeEmail(encryptedId);

Expand Down Expand Up @@ -740,6 +744,55 @@ describe('User use cases', () => {
expect(tokens).toEqual(mockTokens);
});
});

describe('getAuthTokens', () => {
const jwtSecret = 'secret-jwt';

beforeEach(() => {
jest.spyOn(configService, 'get').mockReturnValueOnce(jwtSecret);
});

it('When called, then it should return respective tokens', async () => {
const result = userUseCases.getAuthTokens(user);

expect(result).toEqual({
token: 'token',
newToken: 'newToken',
});
});

it('When called with custom iat, then it should create the tokens with custom iat', async () => {
const customIat = 1620000000;
userUseCases.getAuthTokens(user, customIat);

expect(SignEmail).toHaveBeenCalledWith(
user.email,
jwtSecret,
true,
customIat,
);

expect(Sign).toHaveBeenCalledWith(
{
payload: {
uuid: user.uuid,
email: user.email,
name: user.name,
lastname: user.lastname,
username: user.username,
sharedWorkspace: true,
networkCredentials: {
user: user.bridgeUser,
pass: user.userId,
},
},
iat: customIat,
},
jwtSecret,
true,
);
});
});
});

const createTestingModule = (): Promise<TestingModule> => {
Expand Down
7 changes: 6 additions & 1 deletion src/modules/user/user.usecase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -717,12 +717,16 @@ export class UserUseCases {
return !hasBeenSubscribed;
}

getAuthTokens(user: User): { token: string; newToken: string } {
getAuthTokens(
user: User,
customIat?: number,
): { token: string; newToken: string } {
const expires = true;
const token = SignEmail(
user.email,
this.configService.get('secrets.jwt'),
expires,
customIat,
);
const newToken = Sign(
{
Expand All @@ -738,6 +742,7 @@ export class UserUseCases {
pass: user.userId,
},
},
...(customIat ? { iat: customIat } : null),
},
this.configService.get('secrets.jwt'),
expires,
Expand Down

0 comments on commit 3236f07

Please sign in to comment.