diff --git a/src/configuration/config.ts b/src/configuration/config.ts
index 9cc7f1d62e222b124eebb7f4acfbcbb33275000e..b6d29acca6ec81c264ea73c4c482790a914a3c36 100644
--- a/src/configuration/config.ts
+++ b/src/configuration/config.ts
@@ -17,5 +17,9 @@ export const config = {
       ejs: 'changeEmail.ejs',
       json: 'changeEmail.json',
     },
+    resetPassword: {
+      ejs: 'resetPassword.ejs',
+      json: 'resetPassword.json',
+    },
   },
 };
diff --git a/src/mailer/mail-templates/resetPassword.ejs b/src/mailer/mail-templates/resetPassword.ejs
new file mode 100644
index 0000000000000000000000000000000000000000..7143e2421b42f6cf5acd2b6b81a7d4372d8e847b
--- /dev/null
+++ b/src/mailer/mail-templates/resetPassword.ejs
@@ -0,0 +1,12 @@
+Bonjour<br />
+<br />
+Vous avez demandé une réinitialisation de votre mot de passe pour le
+<em>Réseau des Acteurs de la Médiation Numérique de la Métropole de Lyon</em>. Pour changer de mot de passe, merci de
+cliquer sur le lien suivant :
+<a
+  href="<%= config.protocol %>://<%= config.host %><%= config.port ? ':' + config.port : '' %>/reset-password?token=<%= token %>"
+  >ce lien</a
+><br />
+Si vous n'avez pas demander de réinitiallisation de votre mot de passe, merci d'ignorer cet email.
+<br />
+Ce mail est un mail automatique. Merci de ne pas y répondre.
diff --git a/src/mailer/mail-templates/resetPassword.json b/src/mailer/mail-templates/resetPassword.json
new file mode 100644
index 0000000000000000000000000000000000000000..c5fe69522794c8f40ea20e57eb340aec8e9728f5
--- /dev/null
+++ b/src/mailer/mail-templates/resetPassword.json
@@ -0,0 +1,3 @@
+{
+  "subject": "Réinitialisation de mot de passe"
+}
diff --git a/src/users/change-password.dto.ts b/src/users/change-password.dto.ts
index 25ff3cc07eef7d4866593b93e390f30e23e5ea1d..73e54f82c21801158042fc060d76ed969e20d5c1 100644
--- a/src/users/change-password.dto.ts
+++ b/src/users/change-password.dto.ts
@@ -1,6 +1,14 @@
-import { IsNotEmpty } from 'class-validator';
+import { IsNotEmpty, IsString } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
 
 export class PasswordChangeDto {
-  @IsNotEmpty() readonly newPassword: string;
-  @IsNotEmpty() readonly oldPassword: string;
+  @ApiProperty({ type: String })
+  @IsNotEmpty()
+  @IsString()
+  readonly newPassword: string;
+
+  @ApiProperty({ type: String })
+  @IsNotEmpty()
+  @IsString()
+  readonly oldPassword: string;
 }
diff --git a/src/users/create-user.dto.ts b/src/users/create-user.dto.ts
index c94b2bd1147be3fbad0ce3c8becf62f06998735f..477d79a2a56bef1bdb62a325e4877c12957792e8 100644
--- a/src/users/create-user.dto.ts
+++ b/src/users/create-user.dto.ts
@@ -1,6 +1,14 @@
-import { IsEmail, IsNotEmpty } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
 
 export class CreateUserDto {
-  @IsNotEmpty() readonly password: string;
-  @IsNotEmpty() @IsEmail() email: string;
+  @ApiProperty({ type: String })
+  @IsNotEmpty()
+  @IsString()
+  readonly password: string;
+
+  @IsNotEmpty()
+  @IsEmail()
+  @ApiProperty({ type: String })
+  email: string;
 }
diff --git a/src/users/reset-password-apply.dto.ts b/src/users/reset-password-apply.dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7fd1ead4fe52ed3831fb8883b6fd3cbe597b8099
--- /dev/null
+++ b/src/users/reset-password-apply.dto.ts
@@ -0,0 +1,13 @@
+import { IsNotEmpty, IsString } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class PasswordResetApplyDto {
+  @ApiProperty({ type: String })
+  @IsNotEmpty()
+  @IsString()
+  readonly password: string;
+  @ApiProperty({ type: String })
+  @IsNotEmpty()
+  @IsString()
+  readonly token: string;
+}
diff --git a/src/users/reset-password.dto.ts b/src/users/reset-password.dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1f3e8e1b14e784f835316063e35627e07559fc7b
--- /dev/null
+++ b/src/users/reset-password.dto.ts
@@ -0,0 +1,9 @@
+import { IsNotEmpty, IsEmail } from 'class-validator';
+import { ApiProperty } from '@nestjs/swagger';
+
+export class PasswordResetDto {
+  @ApiProperty({ type: String, example: 'toto@mii.com' })
+  @IsNotEmpty()
+  @IsEmail()
+  readonly email: string;
+}
diff --git a/src/users/user.interface.ts b/src/users/user.interface.ts
index 0fe05c6f856caafb64c23ff4dfbaaa778d6b5773..84dd109cee9d506ff30248a93ade10eebe9886a2 100644
--- a/src/users/user.interface.ts
+++ b/src/users/user.interface.ts
@@ -6,5 +6,6 @@ export interface IUser extends Document {
   password: string;
   emailVerified: boolean;
   validationToken: string;
+  resetPasswordToken: string;
   role: number;
 }
diff --git a/src/users/user.schema.ts b/src/users/user.schema.ts
index abb11e91aaac1bac06d07ab6c513567b89a6791c..a37539e4df4cb2c1091f183cb125cb36ac2b976e 100644
--- a/src/users/user.schema.ts
+++ b/src/users/user.schema.ts
@@ -14,6 +14,9 @@ export class User {
   @Prop({ default: null })
   validationToken: string;
 
+  @Prop({ default: null })
+  resetPasswordToken: string;
+
   @Prop({ enum: [UserRole.admin, UserRole.user], default: UserRole.user })
   role: number;
 }
diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts
index c7120670e093cfe37ea2cdd5c81586ec2afc6590..c870c5551a64a72b02f0bbc1ace87ec28e0666e1 100644
--- a/src/users/users.controller.ts
+++ b/src/users/users.controller.ts
@@ -3,6 +3,8 @@ import { ApiOperation, ApiParam, ApiResponse } from '@nestjs/swagger';
 import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
 import { PasswordChangeDto } from './change-password.dto';
 import { CreateUserDto } from './create-user.dto';
+import { PasswordResetApplyDto } from './reset-password-apply.dto';
+import { PasswordResetDto } from './reset-password.dto';
 import { UsersService } from './users.service';
 
 @Controller('users')
@@ -44,4 +46,16 @@ export class UsersController {
       passwordChangeDto.newPassword
     );
   }
+
+  @Post('reset-password')
+  @ApiResponse({ status: 200, description: 'Email sent if account exist' })
+  public async resetPassword(@Body() passwordReset: PasswordResetDto) {
+    return this.usersService.sendResetPasswordEmail(passwordReset.email);
+  }
+
+  @Post('reset-password/apply')
+  @ApiResponse({ status: 200, description: 'Email sent if account exist' })
+  public async resetPasswordApply(@Body() passwordResetApplyDto: PasswordResetApplyDto) {
+    return this.usersService.validatePasswordResetToken(passwordResetApplyDto.password, passwordResetApplyDto.token);
+  }
 }
diff --git a/src/users/users.service.spec.ts b/src/users/users.service.spec.ts
index 938f5cb0a46c7dbdaba535c160a01a379959a38c..3ce659dfd93d0d596c0ed0dba9a948f20233056f 100644
--- a/src/users/users.service.spec.ts
+++ b/src/users/users.service.spec.ts
@@ -38,6 +38,7 @@ describe('UsersService', () => {
         emailVerified: false,
         email: 'jacques.dupont@mii.com',
         password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.yazDArYIpf2WnQF1jRHOjBxADEjUEA3',
+        resetPasswordToken: null,
       };
       const userDto: CreateUserDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
       jest.spyOn(service, 'create').mockImplementation(async (): Promise<User> => result);
@@ -71,6 +72,7 @@ describe('UsersService', () => {
         email: 'jacques.dupont@mii.com',
         password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.yazDArYIpf2WnQF1jRHOjBxADEjUEA3',
         role: 0,
+        resetPasswordToken: null,
       };
       const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
       jest.spyOn(service, 'findByLogin').mockImplementation(async (): Promise<User> => result);
@@ -117,4 +119,56 @@ describe('UsersService', () => {
       expect(await service.changeUserPassword('add3d', 'azertyU1!d', 'azertyU1!d')).toBe(result);
     });
   });
+
+  describe('sendResetPasswordEmail', () => {
+    it('should not send email', async () => {
+      const result = new HttpException('Email sent if account exist', HttpStatus.OK);
+      jest.spyOn(service, 'sendResetPasswordEmail').mockImplementation(async (): Promise<HttpException> => result);
+      expect(await service.sendResetPasswordEmail('test@mii.com')).toBe(result);
+    });
+
+    it('should send email', async () => {
+      const result = new HttpException('Email sent if account exist', HttpStatus.OK);
+      jest.spyOn(service, 'sendResetPasswordEmail').mockImplementation(async (): Promise<HttpException> => result);
+      expect(await service.sendResetPasswordEmail('test@mii.com')).toBe(result);
+    });
+  });
+
+  describe('validatePasswordResetToken', () => {
+    it('should not validate new password: token does`nt exist', async () => {
+      const result = new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
+      jest.spyOn(service, 'validatePasswordResetToken').mockImplementation(async (): Promise<HttpException> => result);
+      expect(
+        await service.validatePasswordResetToken(
+          'test@mii.com',
+          '5def4cb41106f89c212679e164911776618bd529e4f78e2883f7dd01776612a1b4a2ad7edabf2a3e3638aa605966c7a4b69d5f07d9617334e58332ba5f9305'
+        )
+      ).toBe(result);
+    });
+
+    it('should not validate new password: weak password', async () => {
+      const result = new HttpException(
+        'Weak password, it must contain ne lowercase alphabetical character, one uppercase alphabetical character, one numeric character, one special character and be eight characters or longer',
+        HttpStatus.UNPROCESSABLE_ENTITY
+      );
+      jest.spyOn(service, 'validatePasswordResetToken').mockImplementation(async (): Promise<HttpException> => result);
+      expect(
+        await service.validatePasswordResetToken(
+          'test@mii.com',
+          '5def4cb41106f89c212679e164911776618bd529e4f78e2883f7dd01776612a1b4a2ad7edabf2a3e3638aa605966c7a4b69d5f07d9617334e58332ba5f9305a6'
+        )
+      ).toBe(result);
+    });
+
+    it('should validate new password', async () => {
+      const result = new HttpException('Password Reset', HttpStatus.OK);
+      jest.spyOn(service, 'validatePasswordResetToken').mockImplementation(async (): Promise<HttpException> => result);
+      expect(
+        await service.validatePasswordResetToken(
+          'test@mii.com',
+          '5def4cb41106f89c212679e164911776618bd529e4f78e2883f7dd01776612a1b4a2ad7edabf2a3e3638aa605966c7a4b69d5f07d9617334e58332ba5f9305a6'
+        )
+      ).toBe(result);
+    });
+  });
 });
diff --git a/src/users/users.service.ts b/src/users/users.service.ts
index dbaed19c46e7bf14a8a71eeaf7e5562cf16aaed5..d38588f6ffd278a373f5cab356c6a7b2d4551cd2 100644
--- a/src/users/users.service.ts
+++ b/src/users/users.service.ts
@@ -149,4 +149,52 @@ export class UsersService {
     user.password = await this.hashPassword(newPassword);
     user.save();
   }
+
+  /**
+   * Send reset password email based on ejs template
+   * @param email string
+   */
+  public async sendResetPasswordEmail(email: string): Promise<HttpException> {
+    const user = await this.findOne(email);
+    if (user) {
+      const config = this.mailerService.config;
+      const ejsPath = this.mailerService.getTemplateLocation(config.templates.resetPassword.ejs);
+      const jsonConfig = this.mailerService.loadJsonConfig(config.templates.resetPassword.json);
+
+      const token = crypto.randomBytes(64).toString('hex');
+      const html = await ejs.renderFile(ejsPath, {
+        config,
+        token: token,
+      });
+      this.mailerService.send(user.email, jsonConfig.subject, html);
+
+      // Save token
+      user.resetPasswordToken = token;
+      user.save();
+    }
+    throw new HttpException('Email sent if account exist', HttpStatus.OK);
+  }
+
+  /**
+   * Change password with the given token and password
+   * Token existence and password strength are verified
+   * @param password string
+   * @param token string
+   */
+  public async validatePasswordResetToken(password: string, token: string): Promise<HttpException> {
+    const user = await this.userModel.findOne({ resetPasswordToken: token }).exec();
+    if (user) {
+      if (!this.isStrongPassword(password)) {
+        throw new HttpException(
+          'Weak password, it must contain ne lowercase alphabetical character, one uppercase alphabetical character, one numeric character, one special character and be eight characters or longer',
+          HttpStatus.UNPROCESSABLE_ENTITY
+        );
+      }
+      user.password = await this.hashPassword(password);
+      user.resetPasswordToken = null;
+      user.save();
+      throw new HttpException('Password Reset', HttpStatus.OK);
+    }
+    throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
+  }
 }