public ActionResult <MilvanethProtocol> AuthFinish(MilvanethProtocol data) { if (!(data?.Data is ClientResponse response) || !response.Check()) { return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.DATA_INVALID_INPUT, ReportTime = _time.SafeNow, } }); } try { if (!_srp.DoServerValidate(response.SessionId, response.ClientToken, response.ClientEvidence, out var accountId)) { if (accountId != 0) { var accountData = _context.AccountData.Single(x => x.AccountId == accountId); if (accountData.PasswordRetry > GlobalConfig.ACCOUNT_PASSWORD_RETRY_TOLERANCE && accountData.LastRetry.HasValue && (_time.UtcNow - accountData.LastRetry.Value).Seconds < GlobalConfig.ACCOUNT_PASSWORD_RETRY_COOLDOWN) { return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.OP_PASSWORD_RETRY_TOO_MUCH, ReportTime = _time.SafeNow, } }); } if (accountData.PasswordRetry == null) { accountData.PasswordRetry = 1; } accountData.PasswordRetry += 1; accountData.LastRetry = _time.UtcNow; _context.AccountData.Update(accountData); _context.SaveChanges(); } return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.DATA_RECORD_MISMATCH, ReportTime = _time.SafeNow, } }); } var account = _context.AccountData.Include(x => x.PrivilegeLevelNavigation).Single(x => x.AccountId == accountId); if (account.PasswordRetry > GlobalConfig.ACCOUNT_PASSWORD_RETRY_TOLERANCE && account.LastRetry.HasValue && (_time.UtcNow - account.LastRetry.Value).Seconds < GlobalConfig.ACCOUNT_PASSWORD_RETRY_COOLDOWN) { return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.OP_PASSWORD_RETRY_TOO_MUCH, ReportTime = _time.SafeNow, } }); } account.PasswordRetry = 0; _context.AccountData.Update(account); _context.AccountLog.Add(new AccountLog { ReportTime = _time.UtcNow, AccountId = account.AccountId, Message = GlobalOperation.AUTH_FINISH, Detail = "Finish login via auth/finish", IpAddress = _accessor.GetIp() }); _context.SaveChanges(); var key = _api.Sign(_authToken, 1, account, _time.UtcNow, _time.UtcNow.AddSeconds(GlobalConfig.TOKEN_ACCOUNT_LIFE_TIME)); return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.OK_SUCCESS, ReportTime = _time.SafeNow, AuthToken = key.Key } }); } catch (Exception e) { Log.Error(e, "Error in AUTH/FINISH"); return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.OP_INVALID, ReportTime = _time.SafeNow, } }); } }
public ActionResult <MilvanethProtocol> SessionCreate(MilvanethProtocol data) { if (!(data?.Data is AuthRequest request) || !request.Check()) { return(new MilvanethProtocol { Context = null, Data = new AuthResponse { Message = GlobalMessage.DATA_INVALID_INPUT, ReportTime = _time.SafeNow, } }); } try { var key = _context.KeyStore .Include(x => x.HoldingAccountNavigation) .ThenInclude(x => x.PrivilegeLevelNavigation) .Include(x => x.UsageNavigation) .Single(x => x.Key.SequenceEqual(request.AuthToken)); _auth.EnsureKey(key, new KeyUsage { CreateSession = true }, GlobalOperation.SESSION_CREATE, 0, "Create session via session/create", _accessor.GetIp()); var account = key.HoldingAccountNavigation; _auth.EnsureAccount(account, new PrivilegeConfig { AccessData = true }, GlobalOperation.SESSION_CREATE, 0, "Create session via session/create", _accessor.GetIp()); var renew = _api.Sign(_renewToken, 1, account, _time.UtcNow, _time.UtcNow.AddSeconds(GlobalConfig.TOKEN_RENEW_LIFE_TIME)); var access = _token.Sign(new TokenPayload(_time.UtcNow.AddSeconds(GlobalConfig.TOKEN_DATA_LIFE_TIME), account.AccountId, TokenPurpose.AccessToken, renew.KeyId)); _context.SaveChanges(); return(new MilvanethProtocol { Context = null, Data = new AuthResponse { Message = GlobalMessage.OK_SUCCESS, ReportTime = _time.SafeNow, RenewToken = renew.Key, SessionToken = access } }); } catch (Exception e) { Log.Error(e, "Error in SESSION/CREATE"); return(new MilvanethProtocol { Context = null, Data = new ServerChallenge { Message = GlobalMessage.OP_INVALID, ReportTime = _time.SafeNow, } }); } }
public ActionResult <MilvanethProtocol> AccountRecoveryEmail(MilvanethProtocol data) { if (!(data?.Data is RecoveryEmail email) || !email.Check()) { return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.DATA_INVALID_INPUT, ReportTime = _time.SafeNow, } }); } try { var user = _context.AccountData.Include(x => x.PrivilegeLevelNavigation).SingleOrDefault(x => x.AccountName == email.Username); if (user == null) { return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.DATA_NO_SUCH_USER, ReportTime = _time.SafeNow, } }); } _auth.EnsureAccount(user, new PrivilegeConfig { AccountOperation = true }, GlobalOperation.ACCOUNT_RECOVERY_EMAIL, 0, "Recovery account via account/recovery with email", _accessor.GetIp()); if (user.Email == null) { return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.DATA_NO_EMAIL_RECORDED, ReportTime = _time.SafeNow, } }); } var record = _context.EmailVerifyCode.OrderByDescending(x => x.SendTime).FirstOrDefault(); if (string.IsNullOrEmpty(email.Code)) { if (record != null && record.SendTime.AddSeconds(GlobalConfig.ACCOUNT_VERIFY_CODE_COOLDOWN) > _time.UtcNow) { return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.RATE_LIMIT, ReportTime = _time.SafeNow, } }); } if (user.Email.Equals(email.Email, StringComparison.InvariantCultureIgnoreCase)) { string code; if (record != null && record.ValidTo > _time.UtcNow.AddSeconds(3 * GlobalConfig.ACCOUNT_VERIFY_CODE_COOLDOWN)) { code = record.Code; record.SendTime = _time.UtcNow; _context.EmailVerifyCode.Update(record); } else { using (var cryptoRng = new RNGCryptoServiceProvider()) { var seed = new byte[5]; cryptoRng.GetBytes(seed); code = Helper.ToCode(seed); } _context.EmailVerifyCode.Add(new EmailVerifyCode { AccountId = user.AccountId, Email = user.Email, FailedRetry = 0, ValidTo = _time.UtcNow.AddSeconds(GlobalConfig.ACCOUNT_VERIFY_CODE_LIFE_TIME), Code = code, SendTime = _time.UtcNow }); } _context.SaveChanges(); _mail.SendCode(user.Email, user.DisplayName, code); } else { var rand = new Random(); // prevent timing attack Thread.Sleep(1000 + rand.Next(3000)); } return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.OK_SUCCESS, ReportTime = _time.SafeNow, } }); } if (record == null || record.ValidTo < _time.UtcNow || record.FailedRetry >= 5) { return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.DATA_INVALID_INPUT, ReportTime = _time.SafeNow, } }); } if (record.Code != email.Code.ToUpperInvariant()) { record.FailedRetry += 1; _context.EmailVerifyCode.Update(record); _context.SaveChanges(); return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.DATA_INVALID_CAPTCHA, ReportTime = _time.SafeNow, } }); } var recovery = _api.Sign(_changeToken, 1, user, _time.UtcNow, _time.UtcNow.AddSeconds(GlobalConfig.TOKEN_ACCOUNT_LIFE_TIME)); return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.OK_SUCCESS, ReportTime = _time.SafeNow, AuthToken = recovery.Key, } }); } catch (Exception e) { Log.Error(e, "Error in ACCOUNT/RECOVERYMAIL"); return(new MilvanethProtocol { Context = null, Data = new ServerResponse { Message = GlobalMessage.OP_INVALID, ReportTime = _time.SafeNow, } }); } }