diff --git a/Tsi1.Api/Tsi1.Api/Controllers/UserController.cs b/Tsi1.Api/Tsi1.Api/Controllers/UserController.cs
index 1fbbe5ca1f7cdb42755c0717dbfc42a9035983aa..e38a6b37106c031c4edc5f14d9544d7b54d0e8aa 100644
--- a/Tsi1.Api/Tsi1.Api/Controllers/UserController.cs
+++ b/Tsi1.Api/Tsi1.Api/Controllers/UserController.cs
@@ -23,21 +23,27 @@ namespace Tsi1.Api.Controllers
         private readonly IUserService _userService;
         private readonly IUserTypeService _userTypeService;
         private readonly ITenantService _tenantService;
-
-        public UserController(IJwtAuthManager jwtAuthManager, IUserService userService,
-            IUserTypeService userTypeService, ITenantService tenantService)
+        private readonly IEmailService _emailService;
+
+        public UserController(
+            IJwtAuthManager jwtAuthManager,
+            IUserService userService,
+            IUserTypeService userTypeService,
+            ITenantService tenantService,
+            IEmailService emailService)
         {
             _jwtAuthManager = jwtAuthManager;
             _userService = userService;
             _userTypeService = userTypeService;
             _tenantService = tenantService;
+            _emailService = emailService;
         }
 
         [AllowAnonymous]
         [HttpPost("Login")]
         public async Task<IActionResult> Login(LoginRequest request)
         {
-            var resultSplit = request.UserName.Split("@");
+            var resultSplit = request.Username.Split("@");
 
             if (resultSplit.Count() != 2)
             {
@@ -45,9 +51,7 @@ namespace Tsi1.Api.Controllers
             }
 
             var userName = resultSplit[0];
-
             var tenantName = resultSplit[1];
-
             var tenantId = await _tenantService.GetByName(tenantName);
 
             if (tenantId.HasError)
@@ -56,7 +60,6 @@ namespace Tsi1.Api.Controllers
             }
 
             var result = await _userService.Authenticate(userName, request.Password, tenantId.Data);
-
             if (result.HasError)
             {
                 return BadRequest(result.Message);
@@ -217,5 +220,108 @@ namespace Tsi1.Api.Controllers
             return Ok(result.Data);
         }
 
+        [AllowAnonymous]
+        [HttpGet("ForgotPassword/{username}")]
+        public async Task<IActionResult> ForgotPassword(string username)
+        {
+            var resultSplit = username.Split("@");
+
+            if (resultSplit.Count() != 2)
+            {
+                return BadRequest(ErrorMessages.InvalidUsername);
+            }
+
+            username = resultSplit[0];
+            var tenantName = resultSplit[1];
+            var tenantId = await _tenantService.GetByName(tenantName);
+
+            if (tenantId.HasError)
+            {
+                return BadRequest(tenantId.Message);
+            }
+
+            var userResult = await _userService.GetByUsername(username, tenantId.Data);
+
+            if (userResult.HasError)
+            {
+                return BadRequest(userResult.Message);
+            }
+
+            var code = _jwtAuthManager.GenerateVerificationCode(username, DateTime.Now);
+
+            var result = await _emailService.SendVerificationCode(userResult.Data.Email, code);
+            if (result.HasError)
+            {
+                return BadRequest("Ha ocurrido un error");
+            }
+
+            return Ok();
+        }
+
+        [AllowAnonymous]
+        [HttpGet("VerificationCode/{username}/{code}")]
+        public async Task<IActionResult> VerificationCode(string username, int code)
+        {
+            var resultSplit = username.Split("@");
+
+            if (resultSplit.Count() != 2)
+            {
+                return BadRequest(ErrorMessages.InvalidUsername);
+            }
+
+            username = resultSplit[0];
+            var tenantName = resultSplit[1];
+            var tenantId = await _tenantService.GetByName(tenantName);
+
+            if (tenantId.HasError)
+            {
+                return BadRequest(tenantId.Message);
+            }
+
+            if (!_jwtAuthManager.ValidateVerificationCode(username, code))
+            {
+                return BadRequest("Código de verificación incorrecto");
+            }
+
+            var userResult = await _userService.GetByUsername(username, tenantId.Data);
+            if (userResult.HasError)
+            {
+                return BadRequest(userResult.Message);
+            }
+
+            var user = userResult.Data;
+            var claims = new[]
+            {
+                new Claim("Id", user.Id.ToString()),
+                new Claim("Username", user.Username),
+                new Claim("TenantId", user.TenantId.ToString()),
+                new Claim(ClaimTypes.Role, user.UserType.Name)
+            };
+
+            var jwtResult = _jwtAuthManager.GenerateTokens(user.Username, claims, DateTime.Now);
+
+            return Ok(new LoginResult
+            {
+                Id = user.Id,
+                UserName = user.Username,
+                Role = user.UserType.Name,
+                AccessToken = jwtResult.AccessToken,
+                RefreshToken = jwtResult.RefreshToken.TokenString
+            });
+        }
+
+        [HttpPost("RestorePassword")]
+        public async Task<IActionResult> RestorePassword(RestorePasswordDto dto)
+        {
+            var userId = int.Parse(HttpContext.User.Claims.FirstOrDefault(x => x.Type == "Id").Value);
+
+            var result = await _userService.UpdatePassword(userId, dto.Password);
+            if (result.HasError)
+            {
+                return BadRequest(result.Message);
+            }
+
+            return Ok();
+        }
     }
 }
diff --git a/Tsi1.Api/Tsi1.Api/Infrastructure/IJwtAuthManager.cs b/Tsi1.Api/Tsi1.Api/Infrastructure/IJwtAuthManager.cs
index e9342c6dbb7d95fbf866b95abdd4a4fe6142e254..ab8bf680e9aa66084d608abaa9cc6b9a57c182ee 100644
--- a/Tsi1.Api/Tsi1.Api/Infrastructure/IJwtAuthManager.cs
+++ b/Tsi1.Api/Tsi1.Api/Infrastructure/IJwtAuthManager.cs
@@ -14,5 +14,8 @@ namespace Tsi1.Api.Infrastructure
         void RemoveExpiredRefreshTokens(DateTime now);
         void RemoveRefreshTokenByUserName(string userName);
         (ClaimsPrincipal, JwtSecurityToken) DecodeJwtToken(string token);
+        int GenerateVerificationCode(string username, DateTime now);
+        bool ValidateVerificationCode(string username, int code);
+        public void RemoveExpiredVerificationCodes(DateTime now);
     }
 }
diff --git a/Tsi1.Api/Tsi1.Api/Infrastructure/JwtAuthManager.cs b/Tsi1.Api/Tsi1.Api/Infrastructure/JwtAuthManager.cs
index 672247e7bf45a0cd68789cd77290da0f2f2bf606..f1bf057d038a817c83884e80eb99bece1e185bf2 100644
--- a/Tsi1.Api/Tsi1.Api/Infrastructure/JwtAuthManager.cs
+++ b/Tsi1.Api/Tsi1.Api/Infrastructure/JwtAuthManager.cs
@@ -15,6 +15,7 @@ namespace Tsi1.Api.Infrastructure
     {
         public IImmutableDictionary<string, RefreshToken> UsersRefreshTokensReadOnlyDictionary => _usersRefreshTokens.ToImmutableDictionary();
         private readonly ConcurrentDictionary<string, RefreshToken> _usersRefreshTokens;  // can store in a database or a distributed cache
+        private readonly ConcurrentDictionary<string, VerificationCode> _usersVerificationCodes;
         private readonly JwtTokenConfig _jwtTokenConfig;
         private readonly byte[] _secret;
 
@@ -22,6 +23,7 @@ namespace Tsi1.Api.Infrastructure
         {
             _jwtTokenConfig = jwtTokenConfig;
             _usersRefreshTokens = new ConcurrentDictionary<string, RefreshToken>();
+            _usersVerificationCodes = new ConcurrentDictionary<string, VerificationCode>();
             _secret = Encoding.ASCII.GetBytes(jwtTokenConfig.Secret);
         }
 
@@ -118,6 +120,36 @@ namespace Tsi1.Api.Infrastructure
             return (principal, validatedToken as JwtSecurityToken);
         }
 
+        public int GenerateVerificationCode(string username, DateTime now)
+        {
+            var code = new Random().Next(1000000);
+
+            var verficationCode = new VerificationCode()
+            {
+                Username = username,
+                Code = code,
+                ExpireAt = now.AddMinutes(_jwtTokenConfig.VerificationCodeExpiration)
+            };
+
+            _usersVerificationCodes.AddOrUpdate(username, verficationCode, (s, t) => verficationCode);
+
+            return code;
+        }
+
+        public bool ValidateVerificationCode(string username, int code)
+        {
+            return _usersVerificationCodes.TryGetValue(username, out _);
+        }
+
+        public void RemoveExpiredVerificationCodes(DateTime now)
+        {
+            var expiredCodes = _usersVerificationCodes.Where(x => x.Value.ExpireAt < now).ToList();
+            foreach (var expiredCode in expiredCodes)
+            {
+                _usersVerificationCodes.TryRemove(expiredCode.Key, out _);
+            }
+        }
+
         private static string GenerateRefreshTokenString()
         {
             var randomNumber = new byte[32];
diff --git a/Tsi1.Api/Tsi1.Api/Infrastructure/JwtTokenConfig.cs b/Tsi1.Api/Tsi1.Api/Infrastructure/JwtTokenConfig.cs
index 854d596cf716e1388a8c507ba3c9426b77031016..5eacf5d9d79ecab406e690f602e7a8789c21c357 100644
--- a/Tsi1.Api/Tsi1.Api/Infrastructure/JwtTokenConfig.cs
+++ b/Tsi1.Api/Tsi1.Api/Infrastructure/JwtTokenConfig.cs
@@ -4,19 +4,11 @@ namespace Tsi1.Api.Infrastructure
 {
     public class JwtTokenConfig
     {
-        [JsonPropertyName("secret")]
         public string Secret { get; set; }
-
-        [JsonPropertyName("issuer")]
         public string Issuer { get; set; }
-
-        [JsonPropertyName("audience")]
         public string Audience { get; set; }
-
-        [JsonPropertyName("accessTokenExpiration")]
         public int AccessTokenExpiration { get; set; }
-
-        [JsonPropertyName("refreshTokenExpiration")]
         public int RefreshTokenExpiration { get; set; }
+        public int VerificationCodeExpiration { get; set; }
     }
 }
diff --git a/Tsi1.Api/Tsi1.Api/Infrastructure/JwtVerificationCodeCache.cs b/Tsi1.Api/Tsi1.Api/Infrastructure/JwtVerificationCodeCache.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7805cedf35a8d75c22f860acd5f849ecf7d0a4fb
--- /dev/null
+++ b/Tsi1.Api/Tsi1.Api/Infrastructure/JwtVerificationCodeCache.cs
@@ -0,0 +1,41 @@
+using Microsoft.Extensions.Hosting;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Tsi1.Api.Infrastructure
+{
+    public class JwtVerificationCodeCache : IHostedService, IDisposable
+    {
+        private Timer _timer;
+        private readonly IJwtAuthManager _jwtAuthManager;
+
+        public JwtVerificationCodeCache(IJwtAuthManager jwtAuthManager)
+        {
+            _jwtAuthManager = jwtAuthManager;
+        }
+
+        public Task StartAsync(CancellationToken stoppingToken)
+        {
+            // remove expired refresh tokens from cache every minute
+            _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
+            return Task.CompletedTask;
+        }
+
+        private void DoWork(object state)
+        {
+            _jwtAuthManager.RemoveExpiredVerificationCodes(DateTime.Now);
+        }
+
+        public Task StopAsync(CancellationToken stoppingToken)
+        {
+            _timer?.Change(Timeout.Infinite, 0);
+            return Task.CompletedTask;
+        }
+
+        public void Dispose()
+        {
+            _timer?.Dispose();
+        }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.Api/Models/LoginRequest.cs b/Tsi1.Api/Tsi1.Api/Models/LoginRequest.cs
index 96e87b18d03e7185430f760fb0a5c9caefe6611f..5408e8b422bb74b8ad6257bacefdc6aedef08259 100644
--- a/Tsi1.Api/Tsi1.Api/Models/LoginRequest.cs
+++ b/Tsi1.Api/Tsi1.Api/Models/LoginRequest.cs
@@ -11,7 +11,7 @@ namespace Tsi1.Api.Models
     {
         [Required]
         [JsonPropertyName("username")]
-        public string UserName { get; set; }
+        public string Username { get; set; }
 
         [Required]
         [JsonPropertyName("password")]
diff --git a/Tsi1.Api/Tsi1.Api/Models/VerificationCode.cs b/Tsi1.Api/Tsi1.Api/Models/VerificationCode.cs
new file mode 100644
index 0000000000000000000000000000000000000000..253d88390adeeadd50843b579cc03d3e5372d153
--- /dev/null
+++ b/Tsi1.Api/Tsi1.Api/Models/VerificationCode.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Tsi1.Api.Models
+{
+    public class VerificationCode
+    {
+        public string Username { get; set; }
+        public int Code { get; set; }
+        public DateTime ExpireAt { get; set; }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.Api/Startup.cs b/Tsi1.Api/Tsi1.Api/Startup.cs
index 729f1a70cfe93cef91f9ab928d25676c97eaf073..8dd99046a3ee149eaf1ba5a977b024899657c536 100644
--- a/Tsi1.Api/Tsi1.Api/Startup.cs
+++ b/Tsi1.Api/Tsi1.Api/Startup.cs
@@ -45,6 +45,8 @@ namespace Tsi1.Api
             var postgreSqlSection = isElasticCloud ? "PostgreSqlCloud" : "PostgreSql";
             var mongoDbSection = isElasticCloud ? "Tsi1DatabaseSettingsCloud" : "Tsi1DatabaseSettings";
 
+            var jwtTokenConfig = Configuration.GetSection("jwtTokenConfig").Get<JwtTokenConfig>();
+
             services.AddDbContext<Tsi1Context>(x => x.UseNpgsql(Configuration.GetConnectionString(postgreSqlSection)));
 
             services.Configure<Tsi1DatabaseSettings>(
@@ -53,6 +55,13 @@ namespace Tsi1.Api
             services.AddSingleton<ITsi1DatabaseSettings>(sp =>
                 sp.GetRequiredService<IOptions<Tsi1DatabaseSettings>>().Value);
 
+            services.AddSingleton(jwtTokenConfig);
+
+            services.AddSingleton<IJwtAuthManager, JwtAuthManager>();
+            services.AddHostedService<JwtRefreshTokenCache>();
+            services.AddHostedService<JwtVerificationCodeCache>();
+
+
             services.AddSingleton<IMessageService, MessageService>();
 
             services.AddScoped<IUserService, UserService>();
@@ -67,10 +76,7 @@ namespace Tsi1.Api
             services.AddScoped<IEmailService, EmailService>();
 
             services.AddCors();
-
-            var jwtTokenConfig = Configuration.GetSection("jwtTokenConfig").Get<JwtTokenConfig>();
-            services.AddSingleton(jwtTokenConfig);
-
+            
             services.AddAuthentication(x =>
             {
                 x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
@@ -92,9 +98,6 @@ namespace Tsi1.Api
                 };
             });
 
-            services.AddSingleton<IJwtAuthManager, JwtAuthManager>();
-            services.AddHostedService<JwtRefreshTokenCache>();
-
             services.AddSwaggerGen(c =>
             {
                 c.SwaggerDoc("v1", new OpenApiInfo { Title = "Tsi1 api", Version = "v1" });
@@ -125,7 +128,7 @@ namespace Tsi1.Api
                 mc.AddProfile(new MappingProfile());
             });
 
-            IMapper mapper = mappingConfig.CreateMapper();
+            var mapper = mappingConfig.CreateMapper();
             services.AddSingleton(mapper);
         }
 
diff --git a/Tsi1.Api/Tsi1.Api/appsettings.json b/Tsi1.Api/Tsi1.Api/appsettings.json
index 45af97fda74df094689e136d9938c4e8affeab61..810e99c8b712f19dba9b1aa60a3397e8533f5881 100644
--- a/Tsi1.Api/Tsi1.Api/appsettings.json
+++ b/Tsi1.Api/Tsi1.Api/appsettings.json
@@ -14,12 +14,13 @@
     "ConnectionString": "mongodb://mongo:27017",
     "DatabaseName": "Tsi1Db"
   },
-  "jwtTokenConfig": {
-    "secret": "1234567890123456789",
-    "issuer": "https://localhost:5000",
-    "audience": "https://localhost:5000",
-    "accessTokenExpiration": 20,
-    "refreshTokenExpiration": 60
+  "JwtTokenConfig": {
+    "Secret": "1234567890123456789",
+    "Issuer": "https://localhost:5000",
+    "Audience": "https://localhost:5000",
+    "AccessTokenExpiration": 20,
+    "RefreshTokenExpiration": 60,
+    "VerificationCodeExpiration": 5
   },
   "MailSettings": {
     "Mail": "tsi1.grupo2.2020@gmail.com",
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/RestorePasswordDto.cs b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/RestorePasswordDto.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b1e4c4d35789e8edc7413f1e4fe3e8eb245e4dfe
--- /dev/null
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/RestorePasswordDto.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Tsi1.BusinessLayer.Dtos
+{
+    public class RestorePasswordDto
+    {
+        public string Password { get; set; }
+    }
+}
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IEmailService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IEmailService.cs
index a97ea5c312cf5448689c731402cf293ea49ebca4..3cafba23552d1fb40adb046cebae313398b60098 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IEmailService.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IEmailService.cs
@@ -14,5 +14,7 @@ namespace Tsi1.BusinessLayer.Interfaces
         Task<ServiceResult<bool>> SendEmailAsync(MimeMessage message);
 
         Task<ServiceResult<bool>> NotifyNewPostOrMessage(PostCreateDto postCreateDto, List<string> mails);
+
+        public Task<ServiceResult<bool>> SendVerificationCode(string mail, int code);
     }
 }
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IUserService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IUserService.cs
index 12444e66d7fe3e492bbcbdb8b0d7353730c89beb..cc1a12076c6cf6467484ac7fecdb336dee221980 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IUserService.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IUserService.cs
@@ -17,5 +17,9 @@ namespace Tsi1.BusinessLayer.Interfaces
         Task<ServiceResult<List<UserPreviewDto>>> GetAll(int tenantId);
 
         Task<ServiceResult<UserPreviewDto>> GetById(int userId);
+
+        Task<ServiceResult<User>> GetByUsername(string username, int tenantId);
+
+        Task<ServiceResult<bool>> UpdatePassword(int userId, string password);
     }
 }
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/EmailService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/EmailService.cs
index d00e398d81ef6e05d34547d90d87d5a6e11f7ffd..9519fc5b0cf3037535e101a866a297392b786825 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Services/EmailService.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/EmailService.cs
@@ -86,9 +86,22 @@ namespace Tsi1.BusinessLayer.Services
             };
 
             var result = await SendEmailAsync(message);
-
             return result;
         }
 
+        public async Task<ServiceResult<bool>> SendVerificationCode(string mail, int code)
+        {
+            var message = new MimeMessage();
+
+            message.To.Add(MailboxAddress.Parse(mail));
+            message.Subject = "Código de verificación";
+            message.Body = new TextPart("html")
+            {
+                Text = $"<p>Su código de verificación es {code}</p>"
+            };
+
+            var result = await SendEmailAsync(message);
+            return result;
+        }
     }
 }
diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/UserService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/UserService.cs
index 35b14f29c797130271be0d4adacb9190d0ffbc70..a32af3a601986d0160ff4fec651a6b9510a62e62 100644
--- a/Tsi1.Api/Tsi1.BusinessLayer/Services/UserService.cs
+++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/UserService.cs
@@ -138,5 +138,61 @@ namespace Tsi1.BusinessLayer.Services
 
             return result;
         }
+
+        public async Task<ServiceResult<User>> GetByUsername(string username, int tenantId)
+        {
+            var result = new ServiceResult<User>();
+
+            var user = await _context.Users
+                .Include(x => x.UserType)
+                .Include(x => x.Student)
+                .Include(x => x.Professor)
+                .Where(x => x.TenantId == tenantId
+                    && x.Username == username)
+                .FirstOrDefaultAsync();
+
+            if (user == null)
+            {
+                result.HasError = true;
+                result.Message = string.Format(ErrorMessages.UserDoesNotExist, username);
+                return result;
+            }
+
+            var userType = user.UserType.Name;
+
+            if (userType == UserTypes.Student && user.Student == null)
+            {
+                result.HasError = true;
+                result.Message = string.Format(ErrorMessages.StudentDoesNotExist, username);
+                return result;
+            }
+            else if (userType == UserTypes.Professor && user.Professor == null)
+            {
+                result.HasError = true;
+                result.Message = string.Format(ErrorMessages.ProffesorDoesNotExist, username);
+                return result;
+            }
+
+            result.Data = user;
+            return result;
+        }
+
+        public async Task<ServiceResult<bool>> UpdatePassword(int userId, string password)
+        {
+            var result = new ServiceResult<bool>();
+            var user = await _context.Users.FirstOrDefaultAsync();
+
+            if (user == null)
+            {
+                result.HasError = true;
+                result.Message = string.Format(ErrorMessages.UserDoesNotExist, userId);
+                return result;
+            }
+
+            user.Password = password;
+            await _context.SaveChangesAsync();
+
+            return result;
+        }
     }
 }