Files
atakanozbancom/Controllers/loginController.cs
T
Atakan Doğan ÖzbanandCursor fc45c35dbe Add support page, admin features, and auth hardening.
Rename public affiliate route to /support with localized nav labels, and include affiliate/social/wallpaper admin, 2FA login, and related site updates.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 20:25:55 +02:00

198 lines
6.4 KiB
C#

using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Security;
using atakanozbancom.Models.classes;
namespace atakanozbancom.Controllers
{
public class loginController : Controller
{
private readonly Context c = new Context();
private const int MaxPasswordFailures = 8;
private const int MaxTotpFailures = 5;
private const int LockMinutes = 15;
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(admin ad)
{
if (ad == null || string.IsNullOrWhiteSpace(ad.username) || string.IsNullOrWhiteSpace(ad.password))
{
ViewBag.Error = "Username and password are required.";
return View();
}
var user = c.admins.FirstOrDefault(x => x.username == ad.username);
if (user == null)
{
ViewBag.Error = "Invalid username or password.";
return View();
}
if (user.login_lock_until.HasValue && user.login_lock_until.Value > DateTime.UtcNow)
{
ViewBag.Error = "Account temporarily locked. Try again later.";
return View();
}
if (!PasswordHasher.Verify(ad.password, user.password))
{
user.login_failed_count++;
if (user.login_failed_count >= MaxPasswordFailures)
{
user.login_lock_until = DateTime.UtcNow.AddMinutes(LockMinutes);
user.login_failed_count = 0;
}
c.SaveChanges();
ViewBag.Error = "Invalid username or password.";
return View();
}
user.login_failed_count = 0;
user.login_lock_until = null;
if (!PasswordHasher.IsHashed(user.password))
{
user.password = PasswordHasher.Hash(ad.password);
}
c.SaveChanges();
ClearPending2Fa();
if (user.two_factor_enabled && !string.IsNullOrWhiteSpace(user.two_factor_secret))
{
if (user.totp_lock_until.HasValue && user.totp_lock_until.Value > DateTime.UtcNow)
{
ViewBag.Error = "Too many failed 2FA attempts. Try again later.";
return View();
}
Session["Pending2FaUserId"] = user.id;
Session["Pending2FaUsername"] = user.username;
Session["Pending2FaExpiresUtc"] = DateTime.UtcNow.AddMinutes(10);
return RedirectToAction("Verify");
}
CompleteLogin(user.username);
return RedirectToAction("Index", "admin");
}
public ActionResult Verify()
{
if (!HasValidPending2Fa())
{
ClearPending2Fa();
return RedirectToAction("Index");
}
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Verify(string code)
{
if (!HasValidPending2Fa())
{
ClearPending2Fa();
return RedirectToAction("Index");
}
var pendingId = Convert.ToInt32(Session["Pending2FaUserId"]);
var pendingUsername = Session["Pending2FaUsername"].ToString();
var user = c.admins.FirstOrDefault(x => x.id == pendingId && x.username == pendingUsername);
if (user == null || !user.two_factor_enabled || string.IsNullOrWhiteSpace(user.two_factor_secret))
{
ClearPending2Fa();
return RedirectToAction("Index");
}
if (user.totp_lock_until.HasValue && user.totp_lock_until.Value > DateTime.UtcNow)
{
ViewBag.Error = "Too many failed attempts. Try again later.";
return View();
}
var secret = ResolveTotpSecret(user);
if (string.IsNullOrWhiteSpace(secret) || !TotpHelper.VerifyCode(secret, code))
{
user.totp_failed_count++;
if (user.totp_failed_count >= MaxTotpFailures)
{
user.totp_lock_until = DateTime.UtcNow.AddMinutes(LockMinutes);
user.totp_failed_count = 0;
ViewBag.Error = "Too many failed attempts. Try again later.";
}
else
{
ViewBag.Error = "Invalid authentication code.";
}
c.SaveChanges();
return View();
}
user.totp_failed_count = 0;
user.totp_lock_until = null;
c.SaveChanges();
ClearPending2Fa();
CompleteLogin(user.username);
return RedirectToAction("Index", "admin");
}
public ActionResult logout()
{
ClearPending2Fa();
Session.Remove("username");
Session.Abandon();
FormsAuthentication.SignOut();
return RedirectToAction("index", "login");
}
private void CompleteLogin(string username)
{
FormsAuthentication.SetAuthCookie(username, false);
Session["username"] = username;
}
private bool HasValidPending2Fa()
{
if (Session["Pending2FaUserId"] == null || Session["Pending2FaUsername"] == null)
return false;
var expires = Session["Pending2FaExpiresUtc"] as DateTime?;
if (expires == null || expires.Value < DateTime.UtcNow)
return false;
return true;
}
private static string ResolveTotpSecret(admin user)
{
if (string.IsNullOrWhiteSpace(user.two_factor_secret))
return null;
var unprotected = SecretProtector.Unprotect(user.two_factor_secret);
if (!string.IsNullOrWhiteSpace(unprotected))
return unprotected;
return user.two_factor_secret;
}
private void ClearPending2Fa()
{
Session.Remove("Pending2FaUserId");
Session.Remove("Pending2FaUsername");
Session.Remove("Pending2FaSetupSecret");
Session.Remove("Pending2FaExpiresUtc");
}
}
}