← Hive AxylCONTENT HISTORY

Update to Hive Axyl

Snapshot Oct 24, 2026 · 13:42 UTC · version 2.1.0

Collection source: downloaded plugin package.

WHAT CHANGED

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 JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Implements native Sign in with Apple through a Hive Axyl session in Unity on iOS/macOS. Use for Apple addon acquisition, nonce state, IdentityToken/UserIdentifier handoff, token exchange and session installation.",
  "included_files": [],
  "name": "axyl-provider-login-apple",
  "skill_md_contents": "---\nname: axyl-provider-login-apple\ndescription: \"Implements native Sign in with Apple through a Hive Axyl session in Unity on iOS/macOS. Use for Apple addon acquisition, nonce state, IdentityToken/UserIdentifier handoff, token exchange and session installation.\"\nlicense: Apache-2.0\nmetadata:\n  author: Com2uS Platform Corporation\n  sdk-version: 1.0.0\n  engine: Unity 6000.0+\n---\n\n# Apple login (iOS/macOS)\n\nNative Sign in with Apple, 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:** Axyl `clientId`, `deviceKey` and app-selected scopes; the app configures\nthe Apple capability and provider.\n**App owns:** Apple configuration, UI and result types.\n\n## Flow\n\n1. Register `AddAppleSignIn()` and resolve `IAppleSignInPlugin`\n   (`Hive.Axyl.Auth.Addon.Apple`) with `HiveCore.TryResolve`; if it is unavailable,\n   this route ends. Editor registration is not proof of platform support.\n2. Generate a fresh unpredictable nonce and call `apple.LoginAsync(new\n   AppleSignInServiceLoginRequest { NonceHash, RequestedScopes }, ct)` with\n   `NonceHash` = the lowercase SHA-256 hex of the nonce.\n3. On `AppleSignInServiceLoginResult.Success`, join with `Provider.SigninApple`,\n   `ProviderToken = Data.IdentityToken` and `ProviderUserId = Data.UserIdentifier`.\n   A missing token or ID is unusable; dismissal is a cancellation.\n\nApple's authorization code is never an Axyl authorization code. The recipe sends\nthe nonce hash but does not compare the ID token's nonce claim: keep the nonce if\nthe app validates that claim, and never claim the token is cryptographically\nvalidated.\n\n## Verify\n\n- [ ] `NonceHash` is the lowercase SHA-256 hex of a fresh nonce; success maps\n  `IdentityToken`/`UserIdentifier` to `Provider.SigninApple`.\n- [ ] A missing addon, token or ID, dismissal and cancellation stop before the join,\n  with no guest or provider fallback.\n- [ ] Apple's authorization code is never exchanged as an Axyl code, and the Axyl\n  PKCE pair is separate.\n- [ ] Apple platform and entitlement checks are reported 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: 32013dc4a028b12f626801212a10cd185a8d3637d1402aa7a308a588a3fb9dd4