← Hive AxylCONTENT HISTORY
WHAT CHANGED
Update to Hive Axyl
Snapshot Oct 24, 2026 · 13:42 UTC · version 2.1.0
Collection source: downloaded plugin package.
First saved snapshot
No earlier snapshot is available to establish a change.
Save Hive Axyl and check future changes in Following.
Up to 10 plugins free · no card required.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "Implements native Steam ticket login through a Hive Axyl session in Unity on Windows/macOS. Use for Steam client prerequisites, ticket validation lifetime, ReleaseTicket and Axyl token/session handoff. Steam browser OpenID uses WebAuth.",
"included_files": [],
"name": "axyl-provider-login-steam",
"skill_md_contents": "---\nname: axyl-provider-login-steam\ndescription: \"Implements native Steam ticket login through a Hive Axyl session in Unity on Windows/macOS. Use for Steam client prerequisites, ticket validation lifetime, ReleaseTicket and Axyl token/session handoff. Steam browser OpenID uses WebAuth.\"\nlicense: Apache-2.0\nmetadata:\n author: Com2uS Platform Corporation\n sdk-version: 1.0.0\n engine: Unity 6000.0+\n---\n\n# Native Steam login (Windows/macOS)\n\nA Steam Web API session ticket, then the Axyl provider join and session.\nThe Installed SDK, SDK results, Axyl session, Login metadata and Provider join\nsections below apply to every step.\n\n**Inputs:** a running, signed-in Steam client, the configured ticket identity, the\napp's SteamID64, Axyl `clientId` and `deviceKey`.\n**App owns:** Steamworks initialization (`SteamAPI.Init()` once and\n`SteamAPI.RunCallbacks()` every frame; the SDK does neither), UI and result types.\n\n## Flow\n\n1. Register `AddSteamAuth()` behind the package's platform guards and resolve\n `ISteamPlugin` (`Hive.Axyl.Auth.Addon.Steam`) with `HiveCore.TryResolve`; if it is\n unavailable, this route ends.\n2. `steam.GetAuthTicketForWebApiAsync(new GetAuthTicketForWebApiRequest {\n Identity = configuredIdentity }, ct)`. `NotAuthenticated` means the player must\n sign into the Steam client first.\n3. On `SteamServiceGetAuthTicketForWebApiResult.Success`, join with `Provider.Steam`,\n `ProviderToken = Data.TicketHex` and `ProviderUserId` = the SteamID64. The recipe\n allows an empty SteamID but leaves server acceptance unconfirmed: never\n fabricate one, and omit it only after confirming the server accepts that.\n4. Keep the ticket and its plugin until backend validation settles (success,\n failure or cancellation), then call `steam.ReleaseTicket(ticketHex)` on the Unity\n main thread. Plugin disposal is only a shutdown backstop.\n\nSteam's browser OpenID assertion is a different route.\n\n## Verify\n\n- [ ] The configured identity yields `TicketHex` for `Provider.Steam`; a missing addon\n or `NotAuthenticated` stops the route; an omitted SteamID needs confirmed server\n support.\n- [ ] The ticket is released on the main thread only after its use settles, on\n success, failure and cancellation alike.\n- [ ] Steamworks initialization, callback pumping and server ticket acceptance are\n checked in the real integration, separately from test doubles.\n\n<!-- axyl-shared: core/installed-sdk.md -->\n\n## Installed SDK\n\n- Read `Packages/manifest.json`, the lock file and each resolved `package.json`.\n All `com.com2usplatform.hiveaxyl.*` packages must be the same release (UPM does\n not enforce this); `com.hive.axyl.*` packages are a release before 1.0.0-rc.9.\n- The installed SDK wins for APIs, types and contracts. If its release differs\n from this skill's `sdk-version`, apply a step only after its fields, result\n variants, platform support and failure behaviour match; otherwise stop at that\n step and report what is missing. Never change the installed version.\n- The app owns UI, storage, the device key and orchestration. Sample and recipe\n classes are not SDK APIs.\n\n<!-- /axyl-shared -->\n\n<!-- axyl-shared: core/results.md -->\n\n## SDK results\n\nMatch each call's concrete result and read `Success.Data` only after checking the\nfields the next step needs. The markers live in `Hive.Axyl.Contracts.Result`.\n\n| Result | Handling |\n|---|---|\n| `IUserCanceledOutcome` | The user dismissed OS or provider UI: stop this attempt quietly |\n| `IUnknownOutcome` | Keep `Code` and `RawJson`; never treat it as a known refusal or a success |\n| `IUntypedProblem` | Technical failure in `Problem` (`HiveError`): network, timeout or caller `Cancelled` |\n| Other typed variant | Only that call's documented meaning |\n\nPass `new ApiCallContext { Token = ct }` and check cancellation before each\nfollow-up call; a request cancelled in flight may still have taken effect. Never\nbranch on HTTP status, exception text or raw bodies, and never log tokens or\ncredentials.\n\n<!-- /axyl-shared -->\n\n<!-- axyl-shared: core/auth-session.md -->\n\n## Axyl session\n\n### Setup and inputs\n\n- Initialize once on the main thread with\n `HiveBootstrap.Initialize(config, b => b.AddAuth().AddToken())`; the default\n host is `https://core-api.hiveaxyl.com` (use `AddAuth(baseUrl)`/`AddToken(baseUrl)`\n only for a provisioned host). Call `AuthTokenRefresh.Enable(tokenHost, clientId)`\n once afterwards with the Token host. Never initialize inside a login.\n- Resolve `IAuthService`, `ITokenService` and `ISessionManager`; if one is missing,\n stop before any auth call.\n- `clientId` is the Axyl client ID, not a provider's. `deviceKey` is created once\n per device, persisted and reused: 22–64 printable ASCII (`^[!-~]{22,64}$`, UUID v4\n recommended); the SDK does not check it and the server rejects anything else.\n- Each attempt uses a fresh PKCE pair (`CodeChallengeMethod.S256`, base64url\n SHA-256 of a random verifier). Keep the verifier for this attempt's exchange only.\n\n### Authorization code to session\n\n1. `tokens.IssueTokenAsync(new AuthorizationCodeTokenRequest { ClientId,\n AuthorizationCode, CodeVerifier }, ctx)`.\n2. Continue only on `TokenIssueTokenResult.Success` with a nonempty `AccessToken`\n and `RefreshToken`. Expiry is Unix seconds now + `ExpiresIn`; if `ExpiresIn` is\n not positive, use the access token's JWT `exp`; with neither, the response is\n unusable.\n3. Check cancellation, then call\n `session.SetSession(accessToken, refreshToken, playerId, expiresAtSec)` once. A\n failed, unknown, cancelled or superseded attempt never overwrites the current\n session.\n\n`InvalidGrant`, `InvalidGrantExpired` and `InvalidGrantCodeChallenge` mean this\ncode or verifier is spent: start a new attempt, never replay the code.\n`TemporarilyUnavailable` is the only retryable issue outcome: back off before\nretrying (the interval is the app's choice). `InvalidClient` and `AppNotFound` are\nconfiguration errors.\n\n### Persistence and refresh\n\nSubscribe to `session.OnSessionRefreshed` before `SetSession` and persist each\nsnapshot in order. Core refreshes a live session; never add a refresh loop.\n`OnSessionExpired` means invalidation or an explicit clear: do not delete stored\ncredentials on app quit, transient or unknown failures, or storage `AccessDenied`.\n\n<!-- /axyl-shared -->\n\n<!-- axyl-shared: core/auth-account.md -->\n\n## Login metadata\n\n- `LoginResponseData` carries `PlayerId`, `CreatedAt`, `AuthorizationCode`,\n `ProviderList` (`ProviderId`, `ProviderUserId`, `ProviderIndex`) and `IsBlock`.\n Other responses carry only their own fields. Account creation\n (`GuestCreateResponseData`, `UsernameCreateResponseData`) returns `PlayerId`,\n `CreatedAt` and `AuthorizationCode` (guest also `GuestToken`), with no\n `ProviderList` or `IsBlock`. Token refresh returns tokens only, so earlier\n metadata stays stale and block status unknown. Never invent missing fields.\n- Return the supplied fields in an app-owned result tied to that player and\n attempt; the SDK session keeps only tokens, player ID and expiry. The app decides\n what to persist; authorization codes stay transient.\n- `ProviderList` lists the links at that response; it is not the provider catalog\n from `GetProviderListAsync`. `Guest` appears only for a pure guest. An empty list\n or a default value is not proof of no links.\n- `CreatedAt` is not a new-player flag. `IsBlock` is account status for the app to\n handle, not a transport failure.\n\n<!-- /axyl-shared -->\n\n<!-- axyl-shared: core/provider-join.md -->\n\n## Provider join\n\nTurns an acquired provider credential into an Axyl session. Acquisition, and any\nprovider-code exchange, must have succeeded first; a cancelled, failed or unknown\nacquisition stops before this join.\n\n| Route | `Provider` | `ProviderToken` | `ProviderUserId` |\n|---|---|---|---|\n| Apple native | `SigninApple` | `IdentityToken` | `UserIdentifier` |\n| Google Credential Manager | `Google` | `IdToken` | `UniqueId` |\n| Google/X/Apple browser, Play Games | `Google`, `X`, `SigninApple`, `GooglePlayGames` | `ProviderToken` from `ExchangeProviderTokenAsync` (direct exchange: Google `id_token`, X access token) | its `ProviderUserId` (direct exchange: `sub`, `data.id`) |\n| Steam native | `Steam` | `TicketHex` | the app's SteamID64; confirm server support before omitting it |\n| Steam OpenID | `Steam` | the callback query without the leading `?` or fragment, bytes unchanged | SteamID64 from `openid.claimed_id` |\n\nNever cast another enum to `Provider` by number.\n\n1. New Axyl PKCE pair, separate from any provider OAuth PKCE or state.\n2. `auth.LoginProviderAsync(new ProviderLoginRequest { ProviderId, ProviderUserId,\n ProviderToken, ClientId, DeviceKey, CodeChallenge,\n CodeChallengeMethod = CodeChallengeMethod.S256 }, ctx)`.\n3. On `AuthLoginProviderResult.Success`, exchange `Data.AuthorizationCode` with this\n Axyl verifier and install the session.\n\n`ProviderTokenError`, `ProviderConfigNotFound`, `ProviderClientInfoNotExists`,\n`ProviderRequestFailed` and app, service or IP outcomes keep their own meaning;\nnever switch route or create a guest because of them. Login has no typed\n`ProviderNotSupported`: that code arrives as `IUnknownOutcome`. Show block UI only\nfrom `IsBlock`. A native Steam ticket stays alive until backend validation settles,\nthen its owner releases it on the main thread.\n\n<!-- /axyl-shared -->\n"
}SHA-256 of public snapshot: 58e0f89fcc972c4158c87ddd91eacd02f779d793834f60ee55e75bebdb990fc5