AUTH4D-125 Steam and Discord provider hints in the browser SDK and Unity package

Games can now start Steam or Discord directly from their own buttons (“Sign in with Steam”, “Save progress with Steam”). The SDKs send a provider hint. Hosted login and the guest upgrade page (AUTH4D-124 (repository file docs/api-spec/auth4d-124.md)) act on it. A hint is only a display choice: it is not stored with the flow and grants nothing. If the application has the provider turned off, the page shows its usual sign-in methods.

Authorization request

GET /oauth/authorize accepts one more optional parameter:

Parameter Values Effect
provider_hint discord or steam Passed to hosted login as provider. Nothing is stored.

As with every authorize parameter, a repeated provider_hint returns 400 invalid_request. Any other value is redirected to the registered redirect URI as error=invalid_request with the state, and no flow is created. A valid hint adds provider to the hosted login URL:

HTTP/1.1 302 Found
Location: https://auth.auth4.dev/login?tenant_id=ten_…&client_id=cli_…&flow_id=flw_…&provider=steam

Hosted login (/login)

With provider=discord or provider=steam, hosted login skips the method list and starts that provider, as if the player had chosen it. This happens only when:

  • the login context reports the provider as on;
  • the page has no proof, provider error or prompt=consent.

Before leaving, the page removes provider from the address bar, so Back from the provider shows the method list. When the player comes back with an error (for example, they cancelled on Steam), the page shows the method list and names the provider. It does not start again. The page ignores unknown values.

Guest upgrade page (/login/upgrade)

The guest upgrade page (window mode, and intent mode for native games) accepts an optional provider=discord|steam. The hinted provider’s “Link a … account” button comes first, as the primary choice. Email and any other enabled provider follow as secondary choices. The page does not redirect on its own. The player still chooses, and in intent mode still confirms the link, as before. The page ignores unknown, repeated or disabled providers. A provider round trip drops the hint, so the page offers the usual order when the player returns.

Browser SDK (@auth4/sdk-browser)

  • export type SocialProvider = "discord" | "steam".
  • LoginOptions.provider?: SocialProvider. login() and authorizationUrl() add provider_hint.
  • GuestUpgradeOptions.provider?: SocialProvider. upgradeGuest() adds provider to the /login/upgrade URL. No token is ever added to the URL.
  • Any other value rejects with configuration_error before navigating or opening a window.
  • GuestUpgradeResult.identity.provider can be "steam".
await auth4.login({ provider: "steam" });
auth4.upgradeGuest({ provider: "steam" }); // call directly in the click handler

Unity package (dev.auth4.sdk 0.4.0)

  • enum Auth4Provider { Discord, Steam }.
  • UpgradeGuestAsync(Auth4Provider provider, Action<GuestUpgradeProgress> onProgress = null, CancellationToken cancellationToken = default). On native platforms it appends &provider=… to the upgrade_uri the API returned before opening the system browser. In WebGL builds it appends it to the popup URL. The existing UpgradeGuestAsync(onProgress, cancellationToken) is unchanged.
  • An undefined enum value throws ArgumentOutOfRangeException before any request.
  • LinkedIdentity.Provider can be steam.

Tests

  • pnpm --filter @auth4/sdk-browser test: provider hint on authorizationUrl() and upgradeGuest(), and rejection of unknown providers.
  • dotnet test packages/sdk-unity/Tests~/Auth4.Protocol.Tests/Auth4.Protocol.Tests.csproj: OpensTheHostedPageWithTheChosenProviderFirst, RejectsAnUndefinedProviderBeforeCreatingAnIntent (native) and OpensTheWindowWithTheChosenProviderFirst (WebGL).
  • cd tests/integration && pnpm exec vitest run auth4d-125.provider-hint.test.ts: authorize forwards valid hints and rejects invalid or repeated ones.
  • pnpm exec playwright test -c tests/e2e/auth4d-125.playwright.config.ts (port 5251): hosted login goes straight to Steam, does not start again after a cancel, falls back when Steam is off; the upgrade page offers Steam first, keeps confirmation, and fits 320px.

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