Translation middleware now extracts viewer_id/steam_id/steam_session_ticket from the decrypted msgpack dict into HttpContext.Items before the typed DTO deserialize. The Steam handler reads from there instead of re-parsing Request.Body — so authed action DTOs no longer need to inherit BaseRequest to keep the auth fields alive through the msgpack→DTO→JSON pivot. Retires the recurring footgun documented in docs/superpowers/specs/2026-06-02-baseRequest-auth-footgun-improvement.md (2026-05-25 basic-puzzle, 2026-05-28 deck-code, 2026-06-02 Phase 3 Bot, 2026-06-10 profile/index + item_acquire_history/info + user_mypage/update). Pinned by AuthDecouplingTests — posts an encrypted msgpack body to /profile/index (DTO does not inherit BaseRequest) through the real translation middleware + auth handler and asserts 200. Adds an EncryptedMsgpackHelper + useRealAuthHandler factory flag, reusable for future wire-shape tests. ProfileIndexRequest, ItemAcquireHistoryInfoRequest, and UserMyPageUpdateRequest revert to the naked shape — the per-DTO workarounds become vestigial under the new architecture. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
58 lines
2.4 KiB
C#
58 lines
2.4 KiB
C#
using System.Net.Http.Headers;
|
|
using MessagePack;
|
|
using MessagePack.Resolvers;
|
|
using SVSim.EmulatedEntrypoint.Constants;
|
|
using SVSim.EmulatedEntrypoint.Security;
|
|
|
|
namespace SVSim.UnitTests.Infrastructure;
|
|
|
|
/// <summary>
|
|
/// Builds a request that mirrors what the Unity client posts: msgpack-serialized body, AES-
|
|
/// encrypted with the viewer's UDID, plus the UDID/SID headers and Unity user-agent that the
|
|
/// translation middleware uses to recognize the wire format.
|
|
/// </summary>
|
|
internal static class EncryptedMsgpackHelper
|
|
{
|
|
private static readonly MessagePackSerializerOptions ContractlessOpts =
|
|
MessagePackSerializerOptions.Standard.WithResolver(ContractlessStandardResolver.Instance);
|
|
|
|
/// <summary>
|
|
/// Pairs a fresh UDID with a unique SID and registers the mapping on the running test host's
|
|
/// <see cref="ShadowverseSessionService"/> via the SessionidMappingMiddleware path (a GET to
|
|
/// any endpoint with both headers seeds the dict). Returns the pair for reuse on subsequent
|
|
/// POSTs in the same test.
|
|
/// </summary>
|
|
public static (Guid Udid, string Sid) NewSessionIds()
|
|
{
|
|
var udid = Guid.NewGuid();
|
|
var sid = Guid.NewGuid().ToString("N");
|
|
return (udid, sid);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Builds a POST request to <paramref name="path"/> shaped like a real Unity client call:
|
|
/// msgpack body (contractless dictionary), AES-encrypted with <paramref name="udid"/>, with
|
|
/// the Unity user-agent and UDID/SID headers wired up. Caller sends it via
|
|
/// <see cref="HttpClient.SendAsync(HttpRequestMessage)"/>.
|
|
/// </summary>
|
|
public static HttpRequestMessage BuildPost(
|
|
string path,
|
|
IReadOnlyDictionary<string, object?> body,
|
|
Guid udid,
|
|
string sid)
|
|
{
|
|
byte[] msgpackBody = MessagePackSerializer.Serialize(body, ContractlessOpts);
|
|
byte[] encryptedBody = Encryption.Encrypt(msgpackBody, udid.ToString());
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Post, path)
|
|
{
|
|
Content = new ByteArrayContent(encryptedBody),
|
|
};
|
|
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
|
|
request.Headers.UserAgent.ParseAdd("UnityPlayer/2022.3.0 (test)");
|
|
request.Headers.Add(NetworkConstants.UdidHeaderName, Encryption.Encode(udid.ToString()));
|
|
request.Headers.Add(NetworkConstants.SessionIdHeaderName, sid);
|
|
return request;
|
|
}
|
|
}
|