Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AcManager.Tools/AcManager.Tools.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
Expand Down Expand Up @@ -503,6 +503,7 @@
<Compile Include="Helpers\Api\Rsr\RsrApiProvider.cs" />
<Compile Include="Helpers\Api\SteamWebProvider.cs" />
<Compile Include="Helpers\CarDataComparing.cs" />
<Compile Include="Helpers\SteamTicketProvider.cs" />
<Compile Include="Helpers\CookieAwareWebClient.cs">
<SubType>Component</SubType>
</Compile>
Expand Down
7 changes: 7 additions & 0 deletions AcManager.Tools/Helpers/CookieAwareWebClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,13 @@ protected override WebRequest GetWebRequest(Uri address) {
Logging.Debug("Cookies:\n" + cookie);
}

if (SteamTicketProvider.UrlRequiresSteamTicket(address.ToString())) {
var ticket = SteamTicketProvider.GetTicketHex();
if (!string.IsNullOrEmpty(ticket)) {
webRequest.Headers.Set("Authorization", "Bearer " + ticket);
}
}

if (_autoRedirect.HasValue) {
webRequest.AllowAutoRedirect = _autoRedirect.Value;
}
Expand Down
56 changes: 56 additions & 0 deletions AcManager.Tools/Helpers/SteamTicketProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using FirstFloor.ModernUI.Helpers;
using JetBrains.Annotations;
using Steamworks;

namespace AcManager.Tools.Helpers {
/// <summary>
/// Provides Steam auth tickets for download requests. Used when URLs contain auth=steam
/// to prove identity to servers that validate via Steam (BeginAuthSession or Web API).
/// </summary>
public static class SteamTicketProvider {
/// <summary>
/// Query param that indicates the server requires a Steam ticket.
/// When present in URL, CM adds Authorization: Bearer &lt;hex-ticket&gt; to the request.
/// </summary>
public const string AuthParam = "auth=steam";

/// <summary>
/// Generates a Steam auth session ticket and returns it as a hex string for Bearer auth.
/// Returns null if Steam is not initialized or ticket generation fails.
/// Uses GetAuthSessionTicket (game session). Compatible with Steamworks.NET 5.x+ (3-param overload).
/// </summary>
[CanBeNull]
public static string GetTicketHex() {
try {
if (!SteamAPI.Init()) return null;

var ticketBuffer = new byte[1024];
var ticketHandle = SteamUser.GetAuthSessionTicket(ticketBuffer, ticketBuffer.Length, out var ticketSize);
if (ticketHandle == HAuthTicket.Invalid || ticketSize == 0) return null;

try {
var ticketBytes = new byte[ticketSize];
Array.Copy(ticketBuffer, ticketBytes, (int)ticketSize);
return BitConverter.ToString(ticketBytes).Replace("-", "");
} finally {
SteamUser.CancelAuthTicket(ticketHandle);
}
} catch (Exception e) {
Logging.Warning("Steam ticket generation failed: " + e.Message);
return null;
}
}

/// <summary>
/// Checks if the URL contains the auth=steam param (case-insensitive).
/// </summary>
public static bool UrlRequiresSteamTicket([NotNull] string url) {
if (string.IsNullOrEmpty(url)) return false;
var queryStart = url.IndexOf('?');
if (queryStart < 0) return false;
var query = url.Substring(queryStart);
return query.IndexOf(AuthParam, StringComparison.OrdinalIgnoreCase) >= 0;
}
}
}