Skip to content

Commit c7353bd

Browse files
committed
refactored to move bcrypt into infra layer
1 parent c3c186a commit c7353bd

File tree

9 files changed

+58
-23
lines changed

9 files changed

+58
-23
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import type { User } from '@/src/entities/models/user';
1+
import type { User, CreateUser } from '@/src/entities/models/user';
22
import type { ITransaction } from '@/src/entities/models/transaction.interface';
33

44
export interface IUsersRepository {
55
getUser(id: string): Promise<User | undefined>;
66
getUserByUsername(username: string): Promise<User | undefined>;
7-
createUser(input: User, tx?: ITransaction): Promise<User>;
7+
createUser(input: CreateUser, tx?: ITransaction): Promise<User>;
88
}

src/application/services/authentication.service.interface.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ export interface IAuthenticationService {
77
validateSession(
88
sessionId: Session['id']
99
): Promise<{ user: User; session: Session }>;
10+
validatePasswords(
11+
inputPassword: string,
12+
usersHashedPassword: string
13+
): Promise<boolean>;
1014
createSession(user: User): Promise<{ session: Session; cookie: Cookie }>;
1115
invalidateSession(sessionId: Session['id']): Promise<{ blankCookie: Cookie }>;
1216
}

src/application/use-cases/auth/sign-in.use-case.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { compare } from 'bcrypt-ts';
2-
31
import { AuthenticationError } from '@/src/entities/errors/auth';
42
import { Cookie } from '@/src/entities/models/cookie';
53
import { Session } from '@/src/entities/models/session';
@@ -30,9 +28,9 @@ export const signInUseCase =
3028
throw new AuthenticationError('User does not exist');
3129
}
3230

33-
const validPassword = await instrumentationService.startSpan(
34-
{ name: 'verify password hash', op: 'function' },
35-
() => compare(input.password, existingUser.password_hash)
31+
const validPassword = await authenticationService.validatePasswords(
32+
input.password,
33+
existingUser.password_hash
3634
);
3735

3836
if (!validPassword) {

src/application/use-cases/auth/sign-up.use-case.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
import { hash } from 'bcrypt-ts';
2-
31
import { AuthenticationError } from '@/src/entities/errors/auth';
42
import { Cookie } from '@/src/entities/models/cookie';
53
import { Session } from '@/src/entities/models/session';
64
import { User } from '@/src/entities/models/user';
75
import type { IInstrumentationService } from '@/src/application/services/instrumentation.service.interface';
86
import type { IAuthenticationService } from '@/src/application/services/authentication.service.interface';
97
import type { IUsersRepository } from '@/src/application/repositories/users.repository.interface';
10-
import { PASSWORD_SALT_ROUNDS } from '@/config';
118

129
export type ISignUpUseCase = ReturnType<typeof signUpUseCase>;
1310

@@ -35,17 +32,12 @@ export const signUpUseCase =
3532
throw new AuthenticationError('Username taken');
3633
}
3734

38-
const passwordHash = await instrumentationService.startSpan(
39-
{ name: 'hash password', op: 'function' },
40-
() => hash(input.password, PASSWORD_SALT_ROUNDS)
41-
);
42-
4335
const userId = authenticationService.generateUserId();
4436

4537
const newUser = await usersRepository.createUser({
4638
id: userId,
4739
username: input.username,
48-
password_hash: passwordHash,
40+
password: input.password,
4941
});
5042

5143
const { cookie, session } =

src/entities/models/user.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,9 @@ export const userSchema = z.object({
77
});
88

99
export type User = z.infer<typeof userSchema>;
10+
11+
export const createUserSchema = userSchema
12+
.pick({ id: true, username: true })
13+
.merge(z.object({ password: z.string().min(6).max(255) }));
14+
15+
export type CreateUser = z.infer<typeof createUserSchema>;

src/infrastructure/repositories/users.repository.mock.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { hashSync } from 'bcrypt-ts';
22

33
import { IUsersRepository } from '@/src/application/repositories/users.repository.interface';
4-
import { User } from '@/src/entities/models/user';
4+
import type { CreateUser, User } from '@/src/entities/models/user';
55
import { PASSWORD_SALT_ROUNDS } from '@/config';
66

77
export class MockUsersRepository implements IUsersRepository {
@@ -35,8 +35,13 @@ export class MockUsersRepository implements IUsersRepository {
3535
const user = this._users.find((u) => u.username === username);
3636
return user;
3737
}
38-
async createUser(input: User): Promise<User> {
39-
this._users.push(input);
40-
return input;
38+
async createUser(input: CreateUser): Promise<User> {
39+
const newUser: User = {
40+
id: this._users.length.toString(),
41+
username: input.username,
42+
password_hash: input.password,
43+
};
44+
this._users.push(newUser);
45+
return newUser;
4146
}
4247
}

src/infrastructure/repositories/users.repository.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { eq } from 'drizzle-orm';
2+
import { hash } from 'bcrypt-ts';
23

34
import { db } from '@/drizzle';
45
import { users } from '@/drizzle/schema';
56
import { IUsersRepository } from '@/src/application/repositories/users.repository.interface';
67
import { DatabaseOperationError } from '@/src/entities/errors/common';
7-
import { User } from '@/src/entities/models/user';
8+
import type { CreateUser, User } from '@/src/entities/models/user';
89
import type { IInstrumentationService } from '@/src/application/services/instrumentation.service.interface';
910
import type { ICrashReporterService } from '@/src/application/services/crash-reporter.service.interface';
11+
import { PASSWORD_SALT_ROUNDS } from '@/config';
1012

1113
export class UsersRepository implements IUsersRepository {
1214
constructor(
@@ -65,12 +67,22 @@ export class UsersRepository implements IUsersRepository {
6567
}
6668
);
6769
}
68-
async createUser(input: User): Promise<User> {
70+
async createUser(input: CreateUser): Promise<User> {
6971
return await this.instrumentationService.startSpan(
7072
{ name: 'UsersRepository > createUser' },
7173
async () => {
7274
try {
73-
const query = db.insert(users).values(input).returning();
75+
const password_hash = await this.instrumentationService.startSpan(
76+
{ name: 'hash password', op: 'function' },
77+
() => hash(input.password, PASSWORD_SALT_ROUNDS)
78+
);
79+
80+
const newUser: User = {
81+
id: input.id,
82+
username: input.username,
83+
password_hash,
84+
};
85+
const query = db.insert(users).values(newUser).returning();
7486

7587
const [created] = await this.instrumentationService.startSpan(
7688
{

src/infrastructure/services/authentication.service.mock.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ export class MockAuthenticationService implements IAuthenticationService {
1313
this._sessions = {};
1414
}
1515

16+
async validatePasswords(
17+
inputPassword: string,
18+
usersHashedPassword: string
19+
): Promise<boolean> {
20+
return inputPassword === usersHashedPassword;
21+
}
22+
1623
async validateSession(
1724
sessionId: string
1825
): Promise<{ user: User; session: Session }> {

src/infrastructure/services/authentication.service.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { generateIdFromEntropySize, Lucia } from 'lucia';
2+
import { compare } from 'bcrypt-ts';
23

34
import { SESSION_COOKIE } from '@/config';
45
import { luciaAdapter } from '@/drizzle';
@@ -33,6 +34,16 @@ export class AuthenticationService implements IAuthenticationService {
3334
});
3435
}
3536

37+
validatePasswords(
38+
inputPassword: string,
39+
usersHashedPassword: string
40+
): Promise<boolean> {
41+
return this._instrumentationService.startSpan(
42+
{ name: 'verify password hash', op: 'function' },
43+
() => compare(inputPassword, usersHashedPassword)
44+
);
45+
}
46+
3647
async validateSession(
3748
sessionId: string
3849
): Promise<{ user: User; session: Session }> {

0 commit comments

Comments
 (0)