diff --git a/package-lock.json b/package-lock.json
index 9cbd9ccb691588704b567e79dd12534926bcd2d6..f17f7e9a4f8289237269c9e65597749e5ecbd160 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2711,8 +2711,7 @@
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
-      "dev": true
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
     },
     "at-least-node": {
       "version": "1.0.0",
@@ -3724,7 +3723,6 @@
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
       "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
       "requires": {
         "delayed-stream": "~1.0.0"
       }
@@ -4726,8 +4724,7 @@
     "delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
-      "dev": true
+      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
     },
     "delegates": {
       "version": "1.0.0",
@@ -6009,13 +6006,12 @@
       }
     },
     "form-data": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
-      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
-      "dev": true,
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz",
+      "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==",
       "requires": {
         "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.6",
+        "combined-stream": "^1.0.8",
         "mime-types": "^2.1.12"
       }
     },
@@ -11360,6 +11356,17 @@
         "uuid": "^3.3.2"
       },
       "dependencies": {
+        "form-data": {
+          "version": "2.3.3",
+          "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+          "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+          "dev": true,
+          "requires": {
+            "asynckit": "^0.4.0",
+            "combined-stream": "^1.0.6",
+            "mime-types": "^2.1.12"
+          }
+        },
         "qs": {
           "version": "6.5.2",
           "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
diff --git a/package.json b/package.json
index 35186b8b3707f6b468d07db0d8bcabf1f294e6b1..939149dd38dac70d8bc804a2f26b17a844f6663f 100644
--- a/package.json
+++ b/package.json
@@ -34,6 +34,7 @@
     "class-validator": "^0.12.2",
     "dotenv": "^8.2.0",
     "ejs": "^3.1.5",
+    "form-data": "^3.0.0",
     "luxon": "^1.25.0",
     "mongoose": "^5.10.15",
     "passport": "^0.4.1",
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/mailer/mailer.service.ts b/src/mailer/mailer.service.ts
index 0862f91eec026235b6774ee6dee91977a0cde77d..5dbe73f2fbce258441e1695bf88d9caf8dd50387 100644
--- a/src/mailer/mailer.service.ts
+++ b/src/mailer/mailer.service.ts
@@ -2,6 +2,7 @@ import { HttpService, Injectable, Logger } from '@nestjs/common';
 import { AxiosResponse } from 'axios';
 import * as fs from 'fs';
 import * as path from 'path';
+import * as FormData from 'form-data';
 import { ConfigurationService } from '../configuration/configuration.service';
 @Injectable()
 export class MailerService {
@@ -20,22 +21,27 @@ export class MailerService {
    * @param {string} html
    * @param {string} text
    */
-  public send(to: string, subject: string, html: string): Promise<AxiosResponse<any>> {
+  public async send(to: string, subject: string, html: string): Promise<AxiosResponse<any>> {
+    const formData = new FormData();
     const data = JSON.stringify({
-      from: this.config.from,
+      // eslint-disable-next-line camelcase
+      from_email: this.config.from,
       // eslint-disable-next-line camelcase
       from_name: this.config.from_name,
-      to: to,
+      to: [{ email: to }],
       subject: subject,
       content: html,
     });
+    formData.append('metadata', data);
+    const contentLength = formData.getLengthSync();
     Logger.log(`Send mail : ${subject}`, 'Mailer');
     return new Promise((resolve, reject) => {
       this.httpService
-        .post(process.env.MAIL_URL, data, {
+        .post(process.env.MAIL_URL, formData, {
           headers: {
-            'Content-Type': 'application/json',
+            'Content-Length': contentLength,
             Authorization: 'Bearer ' + process.env.MAIL_TOKEN,
+            ...formData.getHeaders(),
           },
         })
         .subscribe(
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 704569733e0657f065a6598cdc4dcfc7bcdf1954..93aec2489ff596ca7894df6cd4dc8ca2de07fbbd 100644
--- a/src/users/user.interface.ts
+++ b/src/users/user.interface.ts
@@ -6,6 +6,7 @@ export interface IUser extends Document {
   password: string;
   emailVerified: boolean;
   validationToken: string;
+  resetPasswordToken: string;
   role: number;
   changeEmailToken: string;
   newEmail: string;
diff --git a/src/users/user.schema.ts b/src/users/user.schema.ts
index c41262a381bd19c00b266be3a8345cb91a9c2c51..b429332fe8b17d06eb53488ccf6285ffb7224d19 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 9742d630f379c944bccd3fc94b13f510b34082f8..e37378c591129af0f1e7ba5577eee6ac31d1b235 100644
--- a/src/users/users.controller.ts
+++ b/src/users/users.controller.ts
@@ -4,6 +4,8 @@ import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
 import { PasswordChangeDto } from './change-password.dto';
 import { EmailChangeDto } from './change-email.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')
@@ -61,4 +63,16 @@ export class UsersController {
   public async verifyAndUpdateEmail(@Request() req, @Query('token') token: string) {
     return this.usersService.verifyAndUpdateUserEmail(token);
   }
+
+  @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 ced92c8a244f82ee32f73992a14ada14fe34136f..c8e1e0c15d4ed0239e868b76151b887da19ceaf9 100644
--- a/src/users/users.service.spec.ts
+++ b/src/users/users.service.spec.ts
@@ -41,6 +41,7 @@ describe('UsersService', () => {
         password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.yazDArYIpf2WnQF1jRHOjBxADEjUEA3',
         newEmail: '',
         changeEmailToken: '',
+        resetPasswordToken: null,
       };
       const userDto: CreateUserDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
       jest.spyOn(service, 'create').mockImplementation(async (): Promise<User> => result);
@@ -76,6 +77,7 @@ describe('UsersService', () => {
         role: 0,
         newEmail: '',
         changeEmailToken: '',
+        resetPasswordToken: null,
       };
       const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
       jest.spyOn(service, 'findByLogin').mockImplementation(async (): Promise<User> => result);
@@ -132,6 +134,7 @@ describe('UsersService', () => {
         password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.yazDArYIpf2WnQF1jRHOjBxADEjUEA3',
         role: 0,
         newEmail: 'test.dupont@mail.com',
+        resetPasswordToken: '',
         changeEmailToken:
           '9bb3542bdc5ca8801ad4cee00403c1052bc95dee768dcbb65b1f719870578ed79f71f52fdc3e6bf02fd200a72b8b6f56fc26950df30c8cd7e427a485f80181b9',
       };
@@ -159,24 +162,71 @@ describe('UsersService', () => {
         password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.yazDArYIpf2WnQF1jRHOjBxADEjUEA3',
         role: 0,
         newEmail: '',
+        resetPasswordToken: '',
         changeEmailToken: '',
       };
-      const token: string =
+      const token =
         '9bb3542bdc5ca8801ad4cee00403c1052bc95dee768dcbb65b1f719870578ed79f71f52fdc3e6bf02fd200a72b8b6f56fc26950df30c8cd7e427a485f80181b9'; //NOSONAR
       jest.spyOn(service, 'verifyAndUpdateUserEmail').mockImplementation(async (): Promise<User> => result);
       expect(await service.verifyAndUpdateUserEmail(token)).toBe(result);
     });
     it('should not change email', async () => {
       const result: HttpException = new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
-      const token: string = '9bb3542bdc5ca8801aa72b8b6f56fc26950df30c8cd7e427a485f80181b9FAKETOKEN'; //NOSONAR
+      const token = '9bb3542bdc5ca8801aa72b8b6f56fc26950df30c8cd7e427a485f80181b9FAKETOKEN'; //NOSONAR
       jest.spyOn(service, 'verifyAndUpdateUserEmail').mockImplementation(async (): Promise<any> => result);
       expect(await service.verifyAndUpdateUserEmail(token)).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 not change email', async () => {
-    //   const result = new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
-    //   jest.spyOn(service, 'changeUserEmail').mockImplementation(async (): Promise<HttpException> => result);
-    //   expect(await service.changeUserEmail('test@test.fr', 'oldTest@test.fr')).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 2c17d0e3518917ad73eafed52d2818982df2c173..d05f8d5dea0d016dae3a2648c8838826dbb89d46 100644
--- a/src/users/users.service.ts
+++ b/src/users/users.service.ts
@@ -187,4 +187,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);
+  }
 }