Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/sessions #1

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions __tests__/data/usecases/db-signout.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ const makeSut = () => {
describe('Signout Usecases', () => {
it('Should call signoutRepository with correct accountId', async () => {
const { sut, signoutRepository } = makeSut();
await sut('any_account_id');
expect(signoutRepository).toHaveBeenCalledWith('any_account_id');
await sut('any_account_id', 'valid_accessToken');
expect(signoutRepository).toHaveBeenCalledWith(
'any_account_id',
'valid_accessToken'
);
});

it('Should throw if signoutRepository throws', async () => {
const { sut, signoutRepository } = makeSut();
signoutRepository.mockRejectedValue(new Error());
const promise = sut('any_account_id');
const promise = sut('any_account_id', 'valid_accessToken');
await expect(promise).rejects.toThrow();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('LoadAccountByTokenMongoRepository', () => {

it('Should return null account loaded not found', async () => {
const sut = makeSut();
const account = await sut(faker.random.uuid());
const account = await sut(faker.random.alphaNumeric(10));
expect(account).toBeNull();
});

Expand All @@ -29,10 +29,15 @@ describe('LoadAccountByTokenMongoRepository', () => {
lastName: faker.name.lastName(),
email: faker.internet.email(),
password: faker.internet.password(),
accessToken,
sessions: [
{
accessToken,
createdAt: new Date(2023, 9, 24),
updatedAt: new Date(2023, 9, 24),
},
],
});
const account = await sut(accessToken);
expect(account).toBeTruthy();
expect(account.accessToken).toBe(accessToken);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@ describe('SignoutRepository', () => {
lastName: faker.name.lastName(),
email: faker.internet.email(),
password: faker.internet.password(),
accessToken,
sessions: [
{
accessToken,
createdAt: new Date(2023, 9, 24),
updatedAt: new Date(2023, 9, 24),
},
],
});
expect(account.accessToken).toBe(accessToken);
await sut(account.id);
expect(account.sessions[0].accessToken).toBe(accessToken);
await sut(account.id, accessToken);
const result = await AccountModel.findById(account.id);
expect(result.accessToken).toBeNull();
expect(result.sessions).toHaveLength(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ describe('UpdateAccessTokenMongoRepository', () => {
email: faker.internet.email(),
password: faker.internet.password(),
});
expect(account.accessToken).toBeFalsy();
expect(account.sessions).toHaveLength(0);
const accessToken = faker.datatype.uuid();
await sut(account.id, accessToken);
const result = await AccountModel.findById(account.id);
expect(result.accessToken).toBe(accessToken);
expect(result.sessions).toHaveLength(1);
});
});
5 changes: 4 additions & 1 deletion __tests__/interface/controllers/signout-controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { mockSignout } from '@test-suite/interface';
const makeSut = () => {
const mockHttpRequest = (): HttpRequest => ({
accountId: 'any_account_id',
headers: {
'x-access-token': 'Bearer any_token',
},
});
const signout = jest.fn(mockSignout());
const sut = signoutController({ signout });
Expand All @@ -16,7 +19,7 @@ describe('SignoutController', () => {
it('Should call signout with correct accountId', async () => {
const { sut, signout, mockHttpRequest } = makeSut();
await sut(mockHttpRequest());
expect(signout).toHaveBeenCalledWith('any_account_id');
expect(signout).toHaveBeenCalledWith('any_account_id', 'any_token');
});

it('Should return 500 if signout throws', async () => {
Expand Down
8 changes: 6 additions & 2 deletions __tests__/main/routes/signout-route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,16 @@ describe('Delete /signout', () => {

it('Should return 204 on signout success', async () => {
const accessToken = await mockAuthenticateUser();
const accountBeforeSignout = await AccountModel.findOne({ accessToken });
const accountBeforeSignout = await AccountModel.findOne({
'sessions.accessToken': accessToken,
});
await request(app)
.delete('/api/v1/signout')
.set('x-access-token', accessToken)
.expect(204);
const accountAfterSignout = await AccountModel.findOne({ accessToken });
const accountAfterSignout = await AccountModel.findOne({
'sessions.accessToken': accessToken,
});
expect(accountBeforeSignout).toBeTruthy();
expect(accountAfterSignout).toBeFalsy();
});
Expand Down
2 changes: 1 addition & 1 deletion globalConfig.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"mongoUri":"mongodb://127.0.0.1:32875/"}
{"mongoUri":"mongodb://127.0.0.1:46787/"}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "auth-microservice",
"version": "2.1.0",
"version": "2.2.0",
"description": "",
"main": "index.js",
"scripts": {
Expand Down
3 changes: 2 additions & 1 deletion src/data/protocols/db/signout-repository.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Signout } from '@src/domain/usecases';

export type SignoutRepository = (
accountId: string
accountId: string,
accessToken: string
) => SignoutRepository.Response;

export namespace SignoutRepository {
Expand Down
4 changes: 2 additions & 2 deletions src/data/usecases/db-signout/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import { BuildDbSignoutRepository } from './protocols';

export const dbSignout: BuildDbSignoutRepository =
({ signoutRepository }) =>
async (accountId: string) => {
await signoutRepository(accountId);
async (accountId: string, accessToken: string) => {
await signoutRepository(accountId, accessToken);
};
16 changes: 11 additions & 5 deletions src/domain/models/account.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
type Session = {
accessToken: string;
createdAt: Date;
updatedAt: Date;
};

export type AccountModel = {
id: string;
firstName: string;
lastName: string;
email: string;
password: string;
accessToken?: string;
forgotPasswordAccessToken?: number;
forgotPasswordExpiresIn?: Date;
createdAt?: Date;
updatedAt?: Date;
sessions: Session[];
forgotPasswordAccessToken: number;
forgotPasswordExpiresIn: Date;
createdAt: Date;
updatedAt: Date;
};
5 changes: 4 additions & 1 deletion src/domain/usecases/signout.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export type Signout = (accountId: string) => Signout.Response;
export type Signout = (
accountId: string,
accessToken: string
) => Signout.Response;

export namespace Signout {
export type Response = Promise<void>;
Expand Down
15 changes: 13 additions & 2 deletions src/infra/db/mongoose/models/account-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@ import {
Schemas,
} from './models-protocols';

const SessionSchema = new Schema(
{
accessToken: {
type: String,
required: true,
},
},
{ timestamps: true }
);

const AccountSchema = new Schema<AccountDocument>(
{
firstName: {
Expand All @@ -29,8 +39,9 @@ const AccountSchema = new Schema<AccountDocument>(
required: true,
minlength: [6, 'password must be at least 6 characters'],
},
accessToken: {
type: String,
sessions: {
type: [SessionSchema],
default: [],
},
forgotPasswordAccessToken: {
type: Number,
Expand Down
19 changes: 14 additions & 5 deletions src/infra/db/mongoose/repositories/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,21 @@ export const loadAccountByIdRepository: LoadAccountByIdRepository = async (

export const loadAccountByTokenRepository: LoadAccountByTokenRepository =
async (accessToken) => {
const account = await AccountModel.findOne({ accessToken }).lean();
const account = await AccountModel.findOne({
'sessions.accessToken': accessToken,
}).lean();
return MongoHelper.serialize(account);
};

export const updateAccessTokenRepository: UpdateAccessTokenRepository = async (
id,
token
) => {
await AccountModel.findByIdAndUpdate(id, { accessToken: token });
await AccountModel.findByIdAndUpdate(id, {
$push: {
sessions: { accessToken: token },
},
});
};

export const updateForgotPasswordAccessTokenRepository: UpdateForgotPasswordAccessTokenRepository =
Expand All @@ -68,8 +74,11 @@ export const updatePasswordRepository: UpdatePasswordRepository = async (
});
};

export const signoutRepository: SignoutRepository = async (id) => {
await AccountModel.findByIdAndUpdate(id, {
accessToken: null,
export const signoutRepository: SignoutRepository = async (
accountId,
accessToken
) => {
await AccountModel.findByIdAndUpdate(accountId, {
$pull: { sessions: { accessToken } },
});
};
6 changes: 5 additions & 1 deletion src/interface/controllers/signout/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { BuildSignoutController } from './protocols';
const buildSignoutController: BuildSignoutController =
({ signout }) =>
async (httpRequest) => {
await signout(httpRequest.accountId);
const header =
httpRequest.headers?.['x-access-token'] ||
httpRequest.headers?.authorization;
const accessToken = header.replace(/^Bearer\s+/, '');
await signout(httpRequest.accountId, accessToken);
return noContent();
};

Expand Down
10 changes: 9 additions & 1 deletion test-suite/domain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,16 @@ export const mockAccount = (): AccountModel => {
lastName: 'valid_last_name',
email: 'valid_email',
password: 'valid_password',
accessToken: 'valid_accessToken',
sessions: [
{
accessToken: 'valid_accessToken',
createdAt: new Date(2023, 9, 24),
updatedAt: new Date(2023, 9, 24),
},
],
forgotPasswordAccessToken: 123456,
forgotPasswordExpiresIn: date,
createdAt: new Date(2023, 9, 24),
updatedAt: new Date(2023, 9, 24),
};
};
13 changes: 12 additions & 1 deletion test-suite/helper/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,18 @@ export const mockCreateAccountOnDb =
export const mockAuthenticateUser = async (): Promise<string> => {
const { accountId } = await mockCreateAccountOnDb();
const accessToken = sign({ accountId }, env.jwtSecret);
await AccountModel.updateOne({ _id: accountId }, { accessToken });
await AccountModel.updateOne(
{ _id: accountId },
{
sessions: [
{
accessToken,
createdAt: new Date(),
updatedAt: new Date(),
},
],
}
);
return accessToken;
};

Expand Down