refactor(auth): decouple Steam handler from request DTO shape

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>
This commit is contained in:
gamer147
2026-06-10 12:29:10 -04:00
parent b18b24b085
commit 1960e28298
9 changed files with 247 additions and 67 deletions

View File

@@ -3,8 +3,10 @@ using MessagePack;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.ItemAcquireHistory;
/// <summary>
/// Empty request body. The endpoint takes no parameters; this DTO exists so model binding
/// resolves the envelope correctly.
/// Empty request body — the endpoint takes no parameters. Does not inherit BaseRequest: the
/// translation middleware stashes the auth tuple into HttpContext.Items before the typed DTO
/// deserialize, so the Steam handler reads them from there. See ProfileIndexRequest +
/// AuthDecouplingTests for the pattern.
/// </summary>
[MessagePackObject(true)]
public sealed class ItemAcquireHistoryInfoRequest

View File

@@ -3,8 +3,12 @@ using MessagePack;
namespace SVSim.EmulatedEntrypoint.Models.Dtos.Profile;
/// <summary>
/// Empty request body. The endpoint takes no parameters (client task uses BaseParam directly);
/// this DTO exists so model binding resolves the envelope correctly.
/// Empty request body — the endpoint takes no parameters and deliberately does NOT inherit
/// BaseRequest. The translation middleware pulls the auth tuple
/// (viewer_id / steam_id / steam_session_ticket) straight out of the decrypted msgpack dict
/// into <c>HttpContext.Items[AuthFields.ContextKey]</c> before deserializing into this DTO,
/// so the Steam handler reads them from there rather than re-parsing the rewritten body.
/// See <c>AuthDecouplingTests</c> for the integration test that pins this contract down.
/// </summary>
[MessagePackObject(true)]
public sealed class ProfileIndexRequest

View File

@@ -6,7 +6,9 @@ namespace SVSim.EmulatedEntrypoint.Models.Dtos.UserMyPage;
/// <summary>
/// Body of <c>POST /user_mypage/update</c>. Client task: <c>MyPageSettingUpdateTask</c>
/// (Shadowverse_Code_2026-05-23/Wizard/MyPageSettingUpdateTask.cs). Note that
/// <c>select_type</c> is the only int on the wire — id fields are strings.
/// <c>select_type</c> is the only int on the wire — id fields are strings. Does not inherit
/// BaseRequest: the translation middleware stashes the auth tuple into HttpContext.Items
/// before the typed DTO deserialize, so the Steam handler reads them from there.
/// </summary>
[MessagePackObject]
public sealed class UserMyPageUpdateRequest