Program.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Threading.Tasks;
  6. using Microsoft.AspNetCore.Builder;
  7. using Microsoft.AspNetCore.Http;
  8. using Microsoft.Extensions.Hosting;
  9. using Newtonsoft.Json;
  10. using SteamAuth;
  11. using SteamKit2;
  12. using SteamKit2.Authentication;
  13. var builder = WebApplication.CreateBuilder(args);
  14. var app = builder.Build();
  15. app.MapPost("/login", async (HttpContext http) =>
  16. {
  17. var req = await GetRequestBodyAsync(http);
  18. if (req == null)
  19. {
  20. http.Response.StatusCode = 400;
  21. await http.Response.WriteAsync("invalid request");
  22. return;
  23. }
  24. if (!File.Exists(req.MaFilePath))
  25. {
  26. http.Response.StatusCode = 400;
  27. await http.Response.WriteAsync("maFile not found");
  28. return;
  29. }
  30. SteamGuardAccount? account;
  31. try
  32. {
  33. var contents = await File.ReadAllTextAsync(req.MaFilePath);
  34. account = JsonConvert.DeserializeObject<SteamGuardAccount>(contents);
  35. }
  36. catch (Exception ex)
  37. {
  38. http.Response.StatusCode = 500;
  39. await http.Response.WriteAsync("failed to read maFile: " + ex.Message);
  40. return;
  41. }
  42. try
  43. {
  44. // Start SteamClient and authenticate using the maFile-provided SteamGuardAccount
  45. SteamClient steamClient = new SteamClient();
  46. steamClient.Connect();
  47. var timeoutMs = 15000;
  48. var waited = 0;
  49. while (!steamClient.IsConnected && waited < timeoutMs)
  50. {
  51. await Task.Delay(200);
  52. waited += 200;
  53. }
  54. if (!steamClient.IsConnected)
  55. {
  56. http.Response.StatusCode = 500;
  57. await http.Response.WriteAsync("failed to connect to steam network");
  58. return;
  59. }
  60. CredentialsAuthSession authSession = await steamClient.Authentication.BeginAuthSessionViaCredentialsAsync(new AuthSessionDetails
  61. {
  62. Username = req.Username,
  63. Password = req.Password,
  64. IsPersistentSession = false,
  65. // PlatformType and ClientOSType are optional; omit to avoid enum symbol differences across SteamKit2 versions.
  66. Authenticator = new FileAuthenticator(account),
  67. });
  68. var pollResponse = await authSession.PollingWaitForResultAsync();
  69. var sessionData = new SessionData()
  70. {
  71. SteamID = authSession.SteamID.ConvertToUInt64(),
  72. AccessToken = pollResponse.AccessToken,
  73. RefreshToken = pollResponse.RefreshToken,
  74. };
  75. // Serialize cookies
  76. var uri = new Uri("https://steamcommunity.com");
  77. var cookies = sessionData.GetCookies().GetCookies(uri).Cast<Cookie>().Select(c => new
  78. {
  79. name = c.Name,
  80. value = c.Value,
  81. domain = c.Domain,
  82. path = c.Path,
  83. expires = c.Expires == DateTime.MinValue ? null : (DateTime?)c.Expires
  84. }).ToArray();
  85. var resp = new
  86. {
  87. session = new
  88. {
  89. steamId = sessionData.SteamID,
  90. accessToken = sessionData.AccessToken,
  91. refreshToken = sessionData.RefreshToken,
  92. sessionId = sessionData.SessionID
  93. },
  94. cookies = cookies
  95. };
  96. http.Response.ContentType = "application/json";
  97. await http.Response.WriteAsync(JsonConvert.SerializeObject(resp, Formatting.Indented));
  98. return;
  99. }
  100. catch (Exception ex)
  101. {
  102. http.Response.StatusCode = 500;
  103. await http.Response.WriteAsync("login failed: " + ex.Message);
  104. return;
  105. }
  106. });
  107. app.Run();
  108. async Task<LoginRequest?> GetRequestBodyAsync(HttpContext http)
  109. {
  110. try
  111. {
  112. using var sr = new StreamReader(http.Request.Body);
  113. var body = await sr.ReadToEndAsync();
  114. if (string.IsNullOrWhiteSpace(body)) return null;
  115. return JsonConvert.DeserializeObject<LoginRequest>(body);
  116. }
  117. catch
  118. {
  119. return null;
  120. }
  121. }
  122. record LoginRequest(string Username, string Password, string MaFilePath);
  123. internal class FileAuthenticator : IAuthenticator
  124. {
  125. private readonly SteamGuardAccount? account;
  126. private int deviceCodesGenerated = 0;
  127. public FileAuthenticator(SteamGuardAccount? account)
  128. {
  129. this.account = account;
  130. }
  131. public Task<bool> AcceptDeviceConfirmationAsync()
  132. {
  133. return Task.FromResult(false);
  134. }
  135. public async Task<string> GetDeviceCodeAsync(bool previousCodeWasIncorrect)
  136. {
  137. if (previousCodeWasIncorrect)
  138. {
  139. if (deviceCodesGenerated > 2)
  140. throw new Exception("failed to generate device code from maFile");
  141. await Task.Delay(30000);
  142. }
  143. if (account == null) return null;
  144. deviceCodesGenerated++;
  145. var code = await account.GenerateSteamGuardCodeAsync();
  146. return code ?? "";
  147. }
  148. public Task<string> GetEmailCodeAsync(string email, bool previousCodeWasIncorrect)
  149. {
  150. // This implementation does not support interactive email codes.
  151. return Task.FromResult(string.Empty);
  152. }
  153. }