diff --git a/src/users/change-email.dto.ts b/src/users/change-email.dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eb783e4490f15c9c32b2f78d4f6dae4f679553fd
--- /dev/null
+++ b/src/users/change-email.dto.ts
@@ -0,0 +1,10 @@
+import { IsEmail, IsNotEmpty } from 'class-validator';
+
+export class EmailChangeDto {
+  @IsNotEmpty()
+  @IsEmail()
+  readonly newEmail: string;
+  @IsNotEmpty()
+  @IsEmail()
+  readonly oldEmail: string;
+}
diff --git a/src/users/user.interface.ts b/src/users/user.interface.ts
index 84dd109cee9d506ff30248a93ade10eebe9886a2..93aec2489ff596ca7894df6cd4dc8ca2de07fbbd 100644
--- a/src/users/user.interface.ts
+++ b/src/users/user.interface.ts
@@ -8,4 +8,6 @@ export interface IUser extends Document {
   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 a37539e4df4cb2c1091f183cb125cb36ac2b976e..b429332fe8b17d06eb53488ccf6285ffb7224d19 100644
--- a/src/users/user.schema.ts
+++ b/src/users/user.schema.ts
@@ -19,6 +19,12 @@ export class User {
 
   @Prop({ enum: [UserRole.admin, UserRole.user], default: UserRole.user })
   role: number;
+
+  @Prop({ default: null })
+  changeEmailToken: string;
+
+  @Prop({ default: null })
+  newEmail: string;
 }
 
 export const UserSchema = SchemaFactory.createForClass(User);
diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts
index c870c5551a64a72b02f0bbc1ace87ec28e0666e1..e37378c591129af0f1e7ba5577eee6ac31d1b235 100644
--- a/src/users/users.controller.ts
+++ b/src/users/users.controller.ts
@@ -2,6 +2,7 @@ import { Body, Controller, Get, Param, Post, Query, Request, UseGuards } from '@
 import { ApiOperation, ApiParam, ApiResponse } from '@nestjs/swagger';
 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';
@@ -47,6 +48,22 @@ export class UsersController {
     );
   }
 
+  @UseGuards(JwtAuthGuard)
+  @Post('change-email')
+  @ApiResponse({ status: 201, description: 'Email confirmation send' })
+  @ApiResponse({ status: 401, description: 'Invalid Email' })
+  public async changeEmail(@Request() req, @Body() emailChangeDto: EmailChangeDto) {
+    return this.usersService.changeUserEmail(emailChangeDto);
+  }
+
+  @UseGuards(JwtAuthGuard)
+  @Post('verify-change-email')
+  @ApiResponse({ status: 201, description: 'Email changed' })
+  @ApiResponse({ status: 401, description: 'Invalid Token' })
+  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) {
diff --git a/src/users/users.service.spec.ts b/src/users/users.service.spec.ts
index 3ce659dfd93d0d596c0ed0dba9a948f20233056f..c8e1e0c15d4ed0239e868b76151b887da19ceaf9 100644
--- a/src/users/users.service.spec.ts
+++ b/src/users/users.service.spec.ts
@@ -6,6 +6,7 @@ import { getModelToken } from '@nestjs/mongoose';
 import { CreateUserDto } from './create-user.dto';
 import { HttpException, HttpStatus } from '@nestjs/common';
 import { LoginDto } from '../auth/login-dto';
+import { EmailChangeDto } from './change-email.dto';
 
 describe('UsersService', () => {
   let service: UsersService;
@@ -38,6 +39,8 @@ describe('UsersService', () => {
         emailVerified: false,
         email: 'jacques.dupont@mii.com',
         password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.yazDArYIpf2WnQF1jRHOjBxADEjUEA3',
+        newEmail: '',
+        changeEmailToken: '',
         resetPasswordToken: null,
       };
       const userDto: CreateUserDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
@@ -72,6 +75,8 @@ describe('UsersService', () => {
         email: 'jacques.dupont@mii.com',
         password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.yazDArYIpf2WnQF1jRHOjBxADEjUEA3',
         role: 0,
+        newEmail: '',
+        changeEmailToken: '',
         resetPasswordToken: null,
       };
       const loginDto: LoginDto = { email: 'jacques.dupont@mii.com', password: 'test1A!!' }; //NOSONAR
@@ -120,6 +125,59 @@ describe('UsersService', () => {
     });
   });
 
+  describe('changeUserEmail', () => {
+    it('should find and add token', async () => {
+      const result = {
+        validationToken: '',
+        emailVerified: true,
+        email: 'jacques.dupont@mii.com',
+        password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.yazDArYIpf2WnQF1jRHOjBxADEjUEA3',
+        role: 0,
+        newEmail: 'test.dupont@mail.com',
+        resetPasswordToken: '',
+        changeEmailToken:
+          '9bb3542bdc5ca8801ad4cee00403c1052bc95dee768dcbb65b1f719870578ed79f71f52fdc3e6bf02fd200a72b8b6f56fc26950df30c8cd7e427a485f80181b9',
+      };
+      const emailDto: EmailChangeDto = { newEmail: 'test.dupont@mail.com', oldEmail: 'jacques.dupont@mii.com' }; //NOSONAR
+      jest.spyOn(service, 'changeUserEmail').mockImplementation(async (): Promise<User> => result);
+      expect(await service.changeUserEmail(emailDto)).toBe(result);
+    });
+    it('user does not exist, should be unauthorized issue', async () => {
+      const result: HttpException = new HttpException('Email sent if account exist', HttpStatus.UNAUTHORIZED);
+      const emailDto: EmailChangeDto = { newEmail: 'test.dupont@mail.com', oldEmail: 'jacques.dupont@mii.com' }; //NOSONAR
+      jest.spyOn(service, 'changeUserEmail').mockImplementation(async (): Promise<any> => result);
+      expect(await service.changeUserEmail(emailDto)).toBe(result);
+    });
+    it('email already used, should be not acceptable issue', async () => {
+      const result: HttpException = new HttpException('Email already used', HttpStatus.NOT_ACCEPTABLE);
+      const emailDto: EmailChangeDto = { newEmail: 'jacques.dupont@mii.com', oldEmail: 'jacques.dupont@mii.com' }; //NOSONAR
+      jest.spyOn(service, 'changeUserEmail').mockImplementation(async (): Promise<any> => result);
+      expect(await service.changeUserEmail(emailDto)).toBe(result);
+    });
+    it('should change email', async () => {
+      const result = {
+        validationToken: '',
+        emailVerified: true,
+        email: 'test.dupont@mail.com',
+        password: '$2a$12$vLQjJ9zAWyUwiXLeQDa6w.yazDArYIpf2WnQF1jRHOjBxADEjUEA3',
+        role: 0,
+        newEmail: '',
+        resetPasswordToken: '',
+        changeEmailToken: '',
+      };
+      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 = '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);
diff --git a/src/users/users.service.ts b/src/users/users.service.ts
index d38588f6ffd278a373f5cab356c6a7b2d4551cd2..d05f8d5dea0d016dae3a2648c8838826dbb89d46 100644
--- a/src/users/users.service.ts
+++ b/src/users/users.service.ts
@@ -9,6 +9,7 @@ import { CreateUserDto } from './create-user.dto';
 import { User } from './user.schema';
 import { MailerService } from '../mailer/mailer.service';
 import { IUser } from './user.interface';
+import { EmailChangeDto } from './change-email.dto';
 
 @Injectable()
 export class UsersService {
@@ -134,6 +135,43 @@ export class UsersService {
     }
   }
 
+  public async changeUserEmail(emailDto: EmailChangeDto): Promise<any> {
+    const user = await this.findOne(emailDto.oldEmail);
+    const alreadyUsed = await this.findOne(emailDto.newEmail);
+    if (user) {
+      if (!alreadyUsed) {
+        const config = this.mailerService.config;
+        const ejsPath = this.mailerService.getTemplateLocation(config.templates.changeEmail.ejs);
+        const jsonConfig = this.mailerService.loadJsonConfig(config.templates.changeEmail.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);
+        user.changeEmailToken = token;
+        user.newEmail = emailDto.newEmail;
+        user.save();
+        return user;
+      }
+      throw new HttpException('Email already used', HttpStatus.NOT_ACCEPTABLE);
+    }
+    throw new HttpException('Email sent if account exist', HttpStatus.UNAUTHORIZED);
+  }
+
+  public async verifyAndUpdateUserEmail(token: string): Promise<any> {
+    const user = await this.userModel.findOne({ changeEmailToken: token }).exec();
+    if (user) {
+      user.email = user.newEmail;
+      user.newEmail = null;
+      user.changeEmailToken = null;
+      user.save();
+      return user;
+    } else {
+      throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
+    }
+  }
+
   public async changeUserPassword(userId: string, oldPassword: string, newPassword: string): Promise<any> {
     const user = await this.findById(userId, true);
     const arePasswordEqual = await this.comparePassword(oldPassword, user.password);