AUTH4D-68 Unity guest upgrade and the native login context

The Unity package upgrades a guest through an AUTH4D-67 (repository file docs/api-spec/auth4d-67.md) upgrade intent (Auth4Client.UpgradeGuestAsync). The hosted upgrade page needs to know which sign-in methods it can offer, so this ticket changes one existing endpoint. No new endpoints are added.

Login context inside a native guest-linking flow

GET /t/{tenantId}/login/context?client_id={clientId}&flow_id={flowId}
Cookie: __Host-auth4_flow_{flowId}=…

login_methods used to be { "email": false, "discord": false } for every native client, because native clients sign in through device authorization and never on the hosted page. That also hid the methods on the intent-mode upgrade page, so no native game could complete an upgrade.

Now, when flow_id names a live flow that belongs to this browser (the flow cookie must match, as before) and the flow is a native guest-linking flow, login_methods reports the tenant’s enabled methods. A guest-linking flow is the one that POST /t/{tenantId}/guest/upgrade-intent/begin creates: its redirect target is the hosted /login page itself.

  • Without flow_id, native clients still get false for both methods.
  • A missing or mismatched flow cookie still returns 403 access_denied.
  • Browser clients are unchanged.
  • The response stays Cache-Control: no-store and never contains tokens.

The intent-mode page (/login/upgrade?…&intent=…) loads the context without a flow, binds the intent with /begin, and then reads the context again with the bound flow_id before it shows the link buttons. It does the same after the Discord round trip.

Unity client behaviour

UpgradeGuestAsync(onProgress, cancellationToken). The player chooses email or Discord on the hosted page.

  1. Throws PlatformNotSupportedException before any request on WebGL or when no browser launcher is configured.
  2. Creates the intent with the stored guest access token and opens upgrade_uri with Application.OpenURL.
  3. Polls /guest/upgrade-intent/token at the server interval. It adds 5 seconds on slow_down and stops at expires_in.
  4. On success it writes the new token set to secure storage, then to memory, and raises one SessionChanged. The user ID does not change.

Failures raise Auth4GuestUpgradeException with Reason set to Conflict (identity_in_use), Expired (expired_token), Denied (access_denied) or Cancelled (the cancellation token).

Tests: tests/integration/auth4d-68.login-context.test.ts, and the C# unit tests in packages/sdk-unity/Tests~/Auth4.Protocol.Tests/UpgradeIntentTests.cs.

Source: docs/api-spec/auth4d-68.md