EXPERIMENTAL: Move 2FA to multi-confirmations, closes #295

Some further tests would be cool to do
This commit is contained in:
JustArchi
2016-07-15 16:13:36 +02:00
parent b2a4cf5e34
commit 2c2dccec2a
4 changed files with 31 additions and 35 deletions

View File

@@ -300,9 +300,9 @@ namespace ArchiSteamFarm {
return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false); return await WebBrowser.UrlGetToHtmlDocumentRetry(request).ConfigureAwait(false);
} }
internal async Task<Steam.ConfirmationDetails> GetConfirmationDetails(string deviceID, string confirmationHash, uint time, uint confirmationID) { internal async Task<Steam.ConfirmationDetails> GetConfirmationDetails(string deviceID, string confirmationHash, uint time, MobileAuthenticator.Confirmation confirmation) {
if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmationID == 0)) { if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmation == null)) {
Logging.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmationID), Bot.BotName); Logging.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmation), Bot.BotName);
return null; return null;
} }
@@ -310,7 +310,7 @@ namespace ArchiSteamFarm {
return null; return null;
} }
string request = SteamCommunityURL + "/mobileconf/details/" + confirmationID + "?p=" + deviceID + "&a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&t=" + time + "&m=android&tag=conf"; string request = SteamCommunityURL + "/mobileconf/details/" + confirmation.ID + "?p=" + deviceID + "&a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&t=" + time + "&m=android&tag=conf";
string json = await WebBrowser.UrlGetToContentRetry(request).ConfigureAwait(false); string json = await WebBrowser.UrlGetToContentRetry(request).ConfigureAwait(false);
if (string.IsNullOrEmpty(json)) { if (string.IsNullOrEmpty(json)) {
@@ -331,13 +331,13 @@ namespace ArchiSteamFarm {
return null; return null;
} }
response.ConfirmationID = confirmationID; response.Confirmation = confirmation;
return response; return response;
} }
internal async Task<bool> HandleConfirmation(string deviceID, string confirmationHash, uint time, uint confirmationID, ulong confirmationKey, bool accept) { internal async Task<bool> HandleConfirmations(string deviceID, string confirmationHash, uint time, HashSet<MobileAuthenticator.Confirmation> confirmations, bool accept) {
if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmationID == 0) || (confirmationKey == 0)) { if (string.IsNullOrEmpty(deviceID) || string.IsNullOrEmpty(confirmationHash) || (time == 0) || (confirmations == null) || (confirmations.Count == 0)) {
Logging.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmationID) + " || " + nameof(confirmationKey), Bot.BotName); Logging.LogNullError(nameof(deviceID) + " || " + nameof(confirmationHash) + " || " + nameof(time) + " || " + nameof(confirmations), Bot.BotName);
return false; return false;
} }
@@ -345,9 +345,12 @@ namespace ArchiSteamFarm {
return false; return false;
} }
string request = SteamCommunityURL + "/mobileconf/ajaxop?op=" + (accept ? "allow" : "cancel") + "&p=" + deviceID + "&a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&t=" + time + "&m=android&tag=conf&cid=" + confirmationID + "&ck=" + confirmationKey; StringBuilder request = new StringBuilder(SteamCommunityURL + "/mobileconf/multiajaxop?op=" + (accept ? "allow" : "cancel") + "&p=" + deviceID + "&a=" + SteamID + "&k=" + WebUtility.UrlEncode(confirmationHash) + "&t=" + time + "&m=android&tag=conf");
foreach (MobileAuthenticator.Confirmation confirmation in confirmations) {
request.Append("&cid[]=" + confirmation.ID + "&ck[]=" + confirmation.Key);
}
string json = await WebBrowser.UrlGetToContentRetry(request).ConfigureAwait(false); string json = await WebBrowser.UrlGetToContentRetry(request.ToString()).ConfigureAwait(false);
if (string.IsNullOrEmpty(json)) { if (string.IsNullOrEmpty(json)) {
return false; return false;
} }

View File

@@ -271,28 +271,20 @@ namespace ArchiSteamFarm {
if ((acceptedSteamID != 0) || ((acceptedTradeIDs != null) && (acceptedTradeIDs.Count > 0))) { if ((acceptedSteamID != 0) || ((acceptedTradeIDs != null) && (acceptedTradeIDs.Count > 0))) {
Steam.ConfirmationDetails[] detailsResults = await Task.WhenAll(confirmations.Select(BotDatabase.MobileAuthenticator.GetConfirmationDetails)).ConfigureAwait(false); Steam.ConfirmationDetails[] detailsResults = await Task.WhenAll(confirmations.Select(BotDatabase.MobileAuthenticator.GetConfirmationDetails)).ConfigureAwait(false);
HashSet<uint> ignoredConfirmationIDs = new HashSet<uint>(); HashSet<MobileAuthenticator.Confirmation> ignoredConfirmations = new HashSet<MobileAuthenticator.Confirmation>(detailsResults.Where(details => (details != null) && (
foreach (Steam.ConfirmationDetails details in detailsResults.Where(details => (details != null) && (
((acceptedSteamID != 0) && (details.OtherSteamID64 != 0) && (acceptedSteamID != details.OtherSteamID64)) || ((acceptedSteamID != 0) && (details.OtherSteamID64 != 0) && (acceptedSteamID != details.OtherSteamID64)) ||
((acceptedTradeIDs != null) && (details.TradeOfferID != 0) && !acceptedTradeIDs.Contains(details.TradeOfferID)) ((acceptedTradeIDs != null) && (details.TradeOfferID != 0) && !acceptedTradeIDs.Contains(details.TradeOfferID))
))) { )).Select(details => details.Confirmation));
ignoredConfirmationIDs.Add(details.ConfirmationID);
}
if (ignoredConfirmationIDs.Count > 0) { if (ignoredConfirmations.Count > 0) {
if (confirmations.RemoveWhere(confirmation => ignoredConfirmationIDs.Contains(confirmation.ID)) > 0) { confirmations.ExceptWith(ignoredConfirmations);
if (confirmations.Count == 0) { if (confirmations.Count == 0) {
return; return;
}
} }
} }
} }
// This could be done in parallel, but for some reason Steam allows only one confirmation being accepted at the time await BotDatabase.MobileAuthenticator.HandleConfirmations(confirmations, accept).ConfigureAwait(false);
// Therefore, even though no physical barrier stops us from doing so, we execute this synchronously
foreach (MobileAuthenticator.Confirmation confirmation in confirmations) {
await BotDatabase.MobileAuthenticator.HandleConfirmation(confirmation, accept).ConfigureAwait(false);
}
} }
internal async Task<bool> RefreshSession() { internal async Task<bool> RefreshSession() {

View File

@@ -367,17 +367,17 @@ namespace ArchiSteamFarm.JSON {
Other Other
} }
private uint _ConfirmationID; private MobileAuthenticator.Confirmation _Confirmation;
internal uint ConfirmationID { internal MobileAuthenticator.Confirmation Confirmation {
get { return _ConfirmationID; } get { return _Confirmation; }
set { set {
if (value == 0) { if (value == null) {
Logging.LogNullError(nameof(value)); Logging.LogNullError(nameof(value));
return; return;
} }
_ConfirmationID = value; _Confirmation = value;
} }
} }

View File

@@ -25,6 +25,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@@ -94,9 +95,9 @@ namespace ArchiSteamFarm {
DeviceID = deviceID; DeviceID = deviceID;
} }
internal async Task<bool> HandleConfirmation(Confirmation confirmation, bool accept) { internal async Task<bool> HandleConfirmations(HashSet<Confirmation> confirmations, bool accept) {
if (confirmation == null) { if ((confirmations == null) || (confirmations.Count == 0)) {
Logging.LogNullError(nameof(confirmation), Bot.BotName); Logging.LogNullError(nameof(confirmations), Bot.BotName);
return false; return false;
} }
@@ -108,7 +109,7 @@ namespace ArchiSteamFarm {
string confirmationHash = GenerateConfirmationKey(time, "conf"); string confirmationHash = GenerateConfirmationKey(time, "conf");
if (!string.IsNullOrEmpty(confirmationHash)) { if (!string.IsNullOrEmpty(confirmationHash)) {
return await Bot.ArchiWebHandler.HandleConfirmation(DeviceID, confirmationHash, time, confirmation.ID, confirmation.Key, accept).ConfigureAwait(false); return await Bot.ArchiWebHandler.HandleConfirmations(DeviceID, confirmationHash, time, confirmations, accept).ConfigureAwait(false);
} }
Logging.LogNullError(nameof(confirmationHash), Bot.BotName); Logging.LogNullError(nameof(confirmationHash), Bot.BotName);
@@ -133,7 +134,7 @@ namespace ArchiSteamFarm {
return null; return null;
} }
Steam.ConfirmationDetails response = await Bot.ArchiWebHandler.GetConfirmationDetails(DeviceID, confirmationHash, time, confirmation.ID).ConfigureAwait(false); Steam.ConfirmationDetails response = await Bot.ArchiWebHandler.GetConfirmationDetails(DeviceID, confirmationHash, time, confirmation).ConfigureAwait(false);
if ((response == null) || !response.Success) { if ((response == null) || !response.Success) {
return null; return null;
} }