Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: TraxSharp/Trax.Samples
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.23.1
Choose a base ref
...
head repository: TraxSharp/Trax.Samples
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v1.24.0
Choose a head ref
  • 1 commit
  • 2 files changed
  • 1 contributor

Commits on May 14, 2026

  1. feat(GameServer): demonstrate [TraxAuthorize] on a [TraxQueryModel] e…

    …ntity (#57)
    
    * feat(GameServer): demonstrate [TraxAuthorize] on a [TraxQueryModel] entity
    
    Gates MatchRecord with [TraxAuthorize(Roles = nameof(GameRole.Admin))] so
    the sample shows how to expose a query-model entity to admins only. The
    attribute attaches @authorize at both the entry field and ObjectType, so
    top-level discover.matches.matchRecords and any transitive navigation
    that resolves a MatchRecord are gated uniformly; totalCount and pageInfo
    side channels are blocked too.
    
    Pins the default authentication scheme to api-key so HC's @authorize
    directive sees an authenticated principal when more than one scheme is
    registered. With a single scheme, ASP.NET Core auto-selects it as
    default; with multiple, no scheme is auto-default and HC reads an
    anonymous principal — every gated query rejects every caller, including
    admins. The pin is conditional on neither scheme nor environment, so it
    applies consistently regardless of whether the Google JWT scheme is
    configured.
    
    Tests:
      - Existing MatchRecord query / filter / pagination tests now use the
        Admin key.
      - New rejection tests cover Player and anonymous direct access plus
        the totalCount-only side-channel case (proves field-level gating
        closes the loophole that type-level alone would leave open).
    
    * feat(GameServer): demonstrate [TraxAuthorize] on a [TraxQueryModel] entity
    
    Gate MatchRecord with [TraxAuthorize(Roles = Admin)] so the sample demonstrates
    per-model authorization end-to-end. The attribute attaches the @authorize
    directive at both the entry field (discover.matches.matchRecords) and the
    ObjectType level — Connection scalars like totalCount and pageInfo are blocked
    too, so a Player API key cannot even enumerate match cardinality.
    
    Existing MatchRecord query tests switched from PlayerKey to AdminKey. Three
    new rejection-path tests pin the contract: Player and anonymous callers see
    TRAX_AUTHORIZATION; totalCount-only queries are blocked even though they
    never resolve a node of the gated type.
    
    * test(GameServer.E2E): warmup MatchRecord auth cache before assertions
    
    The three MatchRecords_*_ReturnsAuthorizationError tests fired the first
    request against a [TraxAuthorize]-gated entity in the fixture lifecycle.
    HotChocolate validates and caches operation documents on first execution.
    If the validation pipeline runs while any part of the request pipeline
    is still settling, the cached operation can capture a pre-gated state,
    and that specific document permanently bypasses the directive. This
    showed up in CI as a deterministic-on-cold-start, passes-on-rerun flake:
    3 failures clustered in the first 400ms of GameServer.E2E execution,
    then 102 other tests against the same entity passed for the next 2 min.
    
    WarmupAuthorizationCache polls both gated query shapes with an anonymous
    caller in [OneTimeSetUp]. Each shape is exercised until it returns
    "Not authorized." once, which proves the directive is wired AND seeds
    the operation cache with a validated entry that enforces it. Tests then
    assert against a known steady-state cache.
    
    Bounded by a 30s timeout that throws TimeoutException with a clear
    diagnostic if the directive never attaches. Per the project's
    determinism rule: polls the actual condition, fails loudly on timeout,
    never sleeps as a stand-in for synchronisation.
    
    Verified: 8 consecutive runs of the fixture pass in 253-273ms with no
    variance.
    Theauxm authored May 14, 2026
    Configuration menu
    Copy the full SHA
    08c6c23 View commit details
    Browse the repository at this point in the history
Loading