Skip to content

Commit 9c46c4d

Browse files
committed
feat(auth): add resetPasswordByEmail method
added password/email and password/reset router
1 parent 31dfa0c commit 9c46c4d

File tree

8 files changed

+96
-88
lines changed

8 files changed

+96
-88
lines changed

src/modules/auth/auth.controller.ts

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
AuthPlatformRequest,
1515
AuthRefreshToken,
1616
AuthRefreshTokenRequest,
17+
AuthResetPasswordByEmailRequest,
1718
} from './auth.type';
1819
import { AuthTokenDto } from './dto';
1920
import { IAuthController, IAuthService } from './interface';
@@ -188,33 +189,32 @@ export class AuthController extends ControllerCore implements IAuthController {
188189
return this.sendJson(res, data);
189190
}
190191

191-
// /**
192-
// * @openapi
193-
// * /api/auth/password/reset:
194-
// * post:
195-
// * tags: [Auth]
196-
// * summary: Reset password
197-
// * description: ''
198-
// * requestBody:
199-
// * $ref: '#/components/requestBodies/ResetPasswordRequest'
200-
// * responses:
201-
// * 200:
202-
// * $ref: '#/components/responses/HttpOk'
203-
// * 422:
204-
// * $ref: '#/components/responses/HttpUnprocessableEntity'
205-
// * 500:
206-
// * $ref: '#/components/responses/HttpInternalServerError'
207-
// */
208-
// async resetPassword(
209-
// req: Request<any, any, ResetPasswordRequest>,
210-
// res: Response,
211-
// ) {
212-
// await this.service.resetPassword(req.body);
213-
214-
// this.response(res, {
215-
// data: Exception.getOk(HttpCode.OK, {
216-
// message: i18n()['message.passwordReset.successfully'],
217-
// }),
218-
// });
219-
// }
192+
/**
193+
* @openapi
194+
* /api/auth/password/reset:
195+
* post:
196+
* tags: [Auth]
197+
* summary: Reset password
198+
* description: ''
199+
* requestBody:
200+
* $ref: '#/components/requestBodies/ResetPasswordRequest'
201+
* responses:
202+
* 200:
203+
* $ref: '#/components/responses/HttpOk'
204+
* 422:
205+
* $ref: '#/components/responses/HttpUnprocessableEntity'
206+
* 500:
207+
* $ref: '#/components/responses/HttpInternalServerError'
208+
*/
209+
async resetPasswordByEmail(
210+
req: AuthResetPasswordByEmailRequest,
211+
res: ExpressResponse,
212+
) {
213+
await this.service.resetPasswordByEmail(req.body);
214+
const message = this.getMessage(
215+
i18n()['message.passwordReset.successfully'],
216+
);
217+
218+
return this.sendJson(res, message);
219+
}
220220
}

src/modules/auth/auth.router.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,20 @@ export class AuthRouter extends RouterCore {
5858
),
5959
);
6060

61-
// this.router.post(
62-
// '/password/email',
63-
// ValidateMiddleware.handler(ForgotPasswordSchema),
64-
// AsyncMiddleware(this.controller.forgotPassword.bind(this.controller)),
65-
// );
66-
// this.router.post(
67-
// '/password/reset',
68-
// ValidateMiddleware.handler(ResetPasswordSchema),
69-
// AsyncMiddleware(this.controller.resetPassword.bind(this.controller)),
70-
// );
61+
this.router.post(
62+
AuthRouterLink.PASSWORD_EMAIL,
63+
this.validateMiddleware.handler(this.schema.forgotPasswordByEmail()),
64+
this.asyncMiddleware.handler(
65+
this.controller.forgotPasswordByEmail.bind(this.controller),
66+
),
67+
);
68+
69+
this.router.post(
70+
AuthRouterLink.PASSWORD_RESET,
71+
this.validateMiddleware.handler(this.schema.resetPasswordByEmail()),
72+
this.asyncMiddleware.handler(
73+
this.controller.resetPasswordByEmail.bind(this.controller),
74+
),
75+
);
7176
}
7277
}

src/modules/auth/auth.schema.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,18 @@ export class AuthSchema extends SchemaCore implements IAuthSchema {
6464
};
6565
}
6666

67-
resetPassword(): JsonSchemaOptions {
67+
resetPasswordByEmail(): JsonSchemaOptions {
6868
return {
6969
body: {
70-
$id: this.getIdKey('resetPassword'),
70+
$id: this.getIdKey('resetPasswordByEmail'),
7171
$schema: 'http://json-schema.org/draft-07/schema#',
7272
type: 'object',
7373
additionalProperties: false,
74-
required: ['token', 'newPassword'],
74+
required: ['token', 'password', 'email'],
7575
properties: {
76+
...this.getEmail(),
7677
...this.getPassword('token'),
77-
...this.getPassword('newPassword'),
78+
...this.getPassword('password'),
7879
},
7980
},
8081
};

src/modules/auth/auth.type.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ export type AuthPlatform = { platform: PlatformName; token: string };
99
export type AuthRefreshToken = { refreshToken: Token };
1010

1111
export type AuthForgotPasswordByEmail = { email: Email };
12-
export type AuthResetPassword = { newPassword: Password; token: Token };
12+
export type AuthResetPasswordByEmail = AuthForgotPasswordByEmail & {
13+
password: Password;
14+
token: Token;
15+
};
1316

1417
export type AuthVerifyEmail = { token: Token };
1518

@@ -38,6 +41,11 @@ export type AuthForgotPasswordByEmailRequest = ExpressRequest<
3841
unknown,
3942
AuthForgotPasswordByEmail
4043
>;
44+
export type AuthResetPasswordByEmailRequest = ExpressRequest<
45+
unknown,
46+
unknown,
47+
AuthResetPasswordByEmail
48+
>;
4149

4250
//email/verify - GET send link to email
4351
//email/verify/{token} - GET

src/modules/auth/interface/auth.controller.interface.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
AuthLogoutRequest,
77
AuthPlatformRequest,
88
AuthRefreshTokenRequest,
9+
AuthResetPasswordByEmailRequest,
910
} from '../auth.type';
1011

1112
export interface IAuthController {
@@ -20,4 +21,8 @@ export interface IAuthController {
2021
req: AuthRefreshTokenRequest,
2122
res: ExpressResponse,
2223
): Promise<void>;
24+
resetPasswordByEmail(
25+
req: AuthResetPasswordByEmailRequest,
26+
res: ExpressResponse,
27+
): Promise<void>;
2328
}

src/modules/auth/interface/auth.schema.interface.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ export interface IAuthSchema {
55
login(): JsonSchemaOptions;
66
platform(): JsonSchemaOptions;
77
refreshToken(): JsonSchemaOptions;
8-
resetPassword(): JsonSchemaOptions;
8+
resetPasswordByEmail(): JsonSchemaOptions;
99
}

src/modules/auth/interface/service/auth.service.interface.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
AuthLogout,
66
AuthPlatform,
77
AuthRefreshToken,
8+
AuthResetPasswordByEmail,
89
AuthToken,
910
} from '../../auth.type';
1011

@@ -14,5 +15,5 @@ export interface IAuthService {
1415
logout(data: AuthLogout): Promise<void>;
1516
platform(data: AuthPlatform, ctx?: AuthCtx): Promise<AuthToken>;
1617
refreshToken(data: AuthRefreshToken, ctx?: AuthCtx): Promise<AuthToken>;
17-
// resetPassword(data: AuthResetPassword): Promise<void>;
18+
resetPasswordByEmail(data: AuthResetPasswordByEmail): Promise<void>;
1819
}

src/modules/auth/service/auth.service.ts

Lines changed: 30 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { inject as Inject, injectable as Injectable } from 'tsyringe';
22

3-
import { OtpType, TokenType } from '@common/enums';
3+
import { OtpType, TemplatePath, TokenType } from '@common/enums';
44
import { ServiceCore } from '@core/service';
5+
import {
6+
INotificationService,
7+
NotificationInject,
8+
} from '@modules/notification';
59
import { IOtpService, OtpInject } from '@modules/otp';
610
import { IPlatformService, PlatformInject } from '@modules/platform';
711
import {
@@ -23,6 +27,7 @@ import {
2327
AuthLogout,
2428
AuthPlatform,
2529
AuthRefreshToken,
30+
AuthResetPasswordByEmail,
2631
AuthToken,
2732
} from '../auth.type';
2833
import { IAuthService, IAuthTokenService } from '../interface';
@@ -33,6 +38,8 @@ export class AuthService extends ServiceCore implements IAuthService {
3338
@Inject(AuthInject.TOKEN_SERVICE)
3439
private authTokenService: IAuthTokenService,
3540
@Inject(LoggerInject.SERVICE) protected readonly logger: ILoggerService,
41+
@Inject(NotificationInject.SERVICE)
42+
private readonly notificationService: INotificationService,
3643
@Inject(OtpInject.SERVICE) private otpService: IOtpService,
3744
@Inject(PlatformInject.SERVICE) private platformService: IPlatformService,
3845
@Inject(RefreshTokenInject.SERVICE)
@@ -82,6 +89,28 @@ export class AuthService extends ServiceCore implements IAuthService {
8289
return this.getTokens(user);
8390
}
8491

92+
async resetPasswordByEmail({
93+
email,
94+
token,
95+
password,
96+
}: AuthResetPasswordByEmail) {
97+
const user = await this.userService.getOne({ email });
98+
99+
if (user) {
100+
await this.otpService.verifyCode({
101+
code: token,
102+
type: OtpType.RESET_PASSWORD_BY_EMAIL,
103+
user,
104+
});
105+
await this.userService.update({ id: user.id }, { password });
106+
107+
this.notificationService.send(
108+
{ email },
109+
{ templatePath: TemplatePath.PASSWORD_CHANGED },
110+
);
111+
}
112+
}
113+
85114
protected async getTokens(user: FullUser): Promise<AuthToken> {
86115
const [accessToken, refreshToken] = await Promise.all([
87116
this.authTokenService.getAccessToken(user.id, user.getPayload()),
@@ -94,45 +123,4 @@ export class AuthService extends ServiceCore implements IAuthService {
94123
refreshToken,
95124
};
96125
}
97-
98-
// async resetPassword({ password, token }: ResetPasswordRequest) {
99-
// const { jti, email } = await Crypto.verifyJwt<JwtPayload>(
100-
// token,
101-
// JwtConfig.secretToken,
102-
// );
103-
104-
// await this.userService.update({ id: user.id }, { password: password });
105-
106-
// void this.notificationService.send(query, {
107-
// template: TemplateType.PASSWORD_CHANGED,
108-
// });
109-
110-
// try {
111-
// await this.userService.update(
112-
// { email, resetPasswordCode: jti },
113-
// { password: password },
114-
// );
115-
// void this.notificationService.addToQueue({
116-
// email,
117-
// template: TemplateType.PASSWORD_CHANGED,
118-
// });
119-
// } catch {
120-
// throw Exception.getError(HttpCode.UNPROCESSABLE_ENTITY, {
121-
// errors: { token: i18n()['validate.token.resetPassword'] },
122-
// });
123-
// }
124-
// }
125126
}
126-
127-
// Создаем таблице OTP (one time password) для хранения временных кодов
128-
// для восстановления пароля, подтверждения email, подтверждения телефона, подтверждения смены email и т.д.
129-
// В таблице должны быть поля: id, user_id, code, type, expired_at, created_at, updated_at.
130-
// Поле type должно быть enum со значениями: reset_password, confirm_email, confirm_phone, change_email.
131-
// Поле code должно содержать код, который будет отправляться на email или телефон.
132-
// Поле expired_at должно содержать время, когда код перестанет быть действительным.
133-
// Отправлять код на email будем как jwt токен, в котором будет зашифрован id пользователя, код и тип кода.
134-
// Далее добавить env OTP_RESET_PASSWORD_EXPIRED_AT, OTP_CONFIRM_EMAIL_EXPIRED_AT, OTP_CONFIRM_PHONE_EXPIRED_AT, OTP_CHANGE_EMAIL_EXPIRED_AT и тд
135-
// В сервисе Auth добавить методы: forgotPasswordByEmail, resetPasswordByEmail
136-
// В методе forgotPasswordByEmail создаем код, сохраняем его в таблицу OTP и отправляем на email.
137-
// В методе resetPasswordByEmail проверяем код, если код не найден или просрочен, то выкидываем ошибку.
138-
// Если код найден и не просрочен, то обновляем пароль пользователя и удаляем код из таблицы OTP.

0 commit comments

Comments
 (0)