Skip to content

Commit

Permalink
DEV - reset password put
Browse files Browse the repository at this point in the history
  • Loading branch information
juliecoust committed Jan 24, 2024
1 parent 6b32ef0 commit 527f7ff
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 25 deletions.
3 changes: 3 additions & 0 deletions src/domain/entities/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface AuthJwtResponseModel {
jwt: string;
jwt_refresh: string
}

export interface AuthJwtRefreshedResponseModel {
jwt: string;
}
Expand All @@ -20,10 +21,12 @@ export interface DecodedToken extends UserResponseModel, JwtPayload { }
export interface CustomRequest extends Request {
token: DecodedToken;
}

export interface ChangeCredentialsModel {
user_id: number;
password: string;
new_password: string;
password_hash?: string;
reset_password_code?: string | null;
reset_password_token?: string | null;
}
1 change: 1 addition & 0 deletions src/domain/interfaces/repositories/user-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface UserRepository {
generateValidationToken(user: UserRequestModel): string;
verifyValidationToken(confirmation_token: string): DecodedToken | null;
generateResetPasswordToken(user: UserRequestModel): string;
verifyResetPasswordToken(reset_password_token: string): DecodedToken | null;
setResetPasswordCode(user: UserUpdateModel): Promise<number>;
toPublicUser(createdUser: UserResponseModel): UserResponseModel;
}
4 changes: 4 additions & 0 deletions src/domain/interfaces/use-cases/auth/reset-password.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { ChangeCredentialsModel } from "../../../entities/auth";
export interface ResetPasswordUseCase {
execute(credentials: ChangeCredentialsModel): Promise<void>;
}
17 changes: 13 additions & 4 deletions src/domain/repositories/user-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,20 @@ export class UserRepositoryImpl implements UserRepository {
return false;
}
}
// TODO IMPROVE ERROR HANDLING

verifyValidationToken(confirmation_token: string): DecodedToken | null {
return this.verifyToken(confirmation_token, this.VALIDATION_TOKEN_SECRET)
}

verifyResetPasswordToken(reset_password_token: string): DecodedToken | null {
return this.verifyToken(reset_password_token, this.RESET_PASSWORD_TOKEN_SECRET)
}

// TODO IMPROVE ERROR HANDLING
verifyToken(token: string, secret: string): DecodedToken | null {
try {
// Verify the token using the refresh secret key
const decoded = this.userJwt.verify(confirmation_token, this.VALIDATION_TOKEN_SECRET)
// Verify the token
const decoded = this.userJwt.verify(token, secret)

// Attach the decoded token to the request object
const decoded_token = (decoded as DecodedToken);
Expand All @@ -124,7 +133,7 @@ export class UserRepositoryImpl implements UserRepository {
} catch (error) {
// An error occurred while fetching or comparing, log the error and return null
console.log(error);
console.log(" Validation token invalid or expired.");
console.log("Token invalid or expired.");
return null;
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/domain/use-cases/auth/change-password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ export class ChangePassword implements ChangePasswordUseCase {

constructor(userRepository: UserRepository) {
this.userRepository = userRepository

}

async execute(current_user: DecodedToken, credentials: ChangeCredentialsModel): Promise<void> {
let nb_of_updated_user: number = 0


// admin can update anyone password without old password
if (await this.userRepository.isAdmin(current_user.user_id)) {
nb_of_updated_user = await this.userRepository.changePassword(credentials)
Expand All @@ -31,6 +30,5 @@ export class ChangePassword implements ChangePasswordUseCase {
} else {
throw new Error("Logged user cannot update this property or user");
}

}
}
39 changes: 39 additions & 0 deletions src/domain/use-cases/auth/reset-password.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { UserRepository } from "../../interfaces/repositories/user-repository";
import { ResetPasswordUseCase } from "../../interfaces/use-cases/auth/reset-password";
import { ChangeCredentialsModel, DecodedToken } from "../../entities/auth";
import { UserResponseModel } from "../../entities/user";

export class ResetPassword implements ResetPasswordUseCase {
userRepository: UserRepository

constructor(userRepository: UserRepository) {
this.userRepository = userRepository
}
async execute(credentials: ChangeCredentialsModel): Promise<void> {
let nb_of_updated_user: number = 0
let decoded_token: DecodedToken | null = null

//is the user reset_password_token valid ?
if (credentials.reset_password_token) {
decoded_token = this.userRepository.verifyResetPasswordToken(credentials.reset_password_token)
if (!decoded_token) throw new Error("Token is not valid");
} else throw new Error("Token is not valid");

// does the user bind to reset_password_code exist ?
const preexistant_user: UserResponseModel | null = await this.userRepository.getUser(
{
user_id: decoded_token.user_id,
reset_password_code: decoded_token.reset_password_code
}
)
// if the user does not exist or the reset_password_code is not valid
if (!preexistant_user) throw new Error("User does not exist or token is not valid");

// is the user validated ?
if (!preexistant_user.valid_email) throw new Error("User email is not validated");

// change the password
nb_of_updated_user = await this.userRepository.changePassword({ ...preexistant_user, ...credentials })
if (nb_of_updated_user == 0) throw new Error("Can't change password");
}
}
4 changes: 2 additions & 2 deletions src/infra/mailer/templates/reset_password_email.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ <h1>EcoPart app</h1>
<p>But don’t worry! You can use the the button below to reset your password:</p>
<p style="text-align: center;">
<a href="{{reset_password_path}}"
style="display: inline-block; padding: 10px 20px; background-color: #1976D2; color: #ffffff; text-decoration: none; border-radius: 5px;">Validate
Account</a>
style="display: inline-block; padding: 10px 20px; background-color: #1976D2; color: #ffffff; text-decoration: none; border-radius: 5px;">Reset
password</a>
</p>
<p>If the button above does not work, you can copy and paste the following link into your browser:</p>
<p>{{reset_password_path}}</p>
Expand Down
2 changes: 2 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { RefreshToken } from './domain/use-cases/auth/refresh-token'
import { ChangePassword } from './domain/use-cases/auth/change-password'
import { ValidUser } from './domain/use-cases/user/valid-user'
import { ResetPasswordRequest } from './domain/use-cases/auth/reset-password-request'
import { ResetPassword } from './domain/use-cases/auth/reset-password'

import { UserRepositoryImpl } from './domain/repositories/user-repository'
import { AuthRepositoryImpl } from './domain/repositories/auth-repository'
Expand Down Expand Up @@ -93,6 +94,7 @@ async function getSQLiteDS() {
new RefreshToken(new UserRepositoryImpl(dataSource, bcryptAdapter, jwtAdapter, config.VALIDATION_TOKEN_SECRET, config.RESET_PASSWORD_TOKEN_SECRET), new AuthRepositoryImpl(jwtAdapter, config.ACCESS_TOKEN_SECRET, config.REFRESH_TOKEN_SECRET)),
new ChangePassword(new UserRepositoryImpl(dataSource, bcryptAdapter, jwtAdapter, config.VALIDATION_TOKEN_SECRET, config.RESET_PASSWORD_TOKEN_SECRET)),
new ResetPasswordRequest(new UserRepositoryImpl(dataSource, bcryptAdapter, jwtAdapter, config.VALIDATION_TOKEN_SECRET, config.RESET_PASSWORD_TOKEN_SECRET), transporter, mailerAdapter),
new ResetPassword(new UserRepositoryImpl(dataSource, bcryptAdapter, jwtAdapter, config.VALIDATION_TOKEN_SECRET, config.RESET_PASSWORD_TOKEN_SECRET)),
)

server.use("/users", userMiddleWare)
Expand Down
34 changes: 18 additions & 16 deletions src/presentation/routers/auth-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { LoginUserUseCase } from '../../domain/interfaces/use-cases/auth/login'
import { RefreshTokenUseCase } from '../../domain/interfaces/use-cases/auth/refresh-token'
import { ChangePasswordUseCase } from '../../domain/interfaces/use-cases/auth/change-password'
import { ResetPasswordRequestUseCase } from '../../domain/interfaces/use-cases/auth/reset-password-request'
import { ResetPasswordUseCase } from '../../domain/interfaces/use-cases/auth/reset-password'

// password securituy rules //HTTPS //SALTING before hashing //rate limiting //timeout //SSO
export default function AuthRouter(
Expand All @@ -18,8 +19,7 @@ export default function AuthRouter(
refreshTokenUseCase: RefreshTokenUseCase,
changePasswordUseCase: ChangePasswordUseCase,
resetPasswordRequestUseCase: ResetPasswordRequestUseCase,


resetPasswordUseCase: ResetPasswordUseCase
) {
const router = express.Router()

Expand Down Expand Up @@ -70,7 +70,6 @@ export default function AuthRouter(
}
})


router.post('/logout', async (req: Request, res: Response) => {
try {
res
Expand Down Expand Up @@ -119,19 +118,22 @@ export default function AuthRouter(
}
})

// // reset password confirm
// router.put('/password/reset', async (req: Request, res: Response) => {
// try {
// const token = await resetPasswordUseCase.execute(req.body)
// res.statusCode = 200// to check
// res.json(token)
// } catch (err) {
// console.log(err)
// if (err.message === "") res.status(500).send({ errors: [""] })
// else res.status(500).send({ errors: ["Can't reset password"] })
// }
//})

// reset password confirm
router.put('/password/reset', async (req: Request, res: Response) => {
try {
console.log("req.body", req.body)
await resetPasswordUseCase.execute(req.body)
res
.status(200)
.json({ response: "Password sucessfully reset, please login" });
} catch (err) {
console.log(err)
if (err.message === "Token is not valid") res.status(401).send({ errors: ["Can't change password"] })
if (err.message === "User does not exist or token is not valid") res.status(404).send({ errors: ["Can't change password"] })
if (err.message === "User email is not validated") res.status(403).send({ errors: ["Can't change password"] })
else res.status(500).send({ errors: ["Can't reset password"] })
}
})

return router
}

0 comments on commit 527f7ff

Please sign in to comment.