# FIFA 17 Blaze — Authentication (component 0x0001) login flow **Date:** 2026-07-30 · **Scope:** what the client needs so it believes it is logged in. --- ## 0. Provenance legend | Flag | Meaning | |---|---| | **(A) binary** | Reflected/disassembled out of **our own `FIFA17.exe`** (live `/proc//mem`, PID 19517). Authoritative for FIFA 17. | | **(A) wire** | Our own captured bytes (`fifa17-recon/captures/blaze/session/`). | | **(A) clean-room 3P** | Third-party clean-room reimplementations cloned in the scratchpad. `grid-blaze` states explicitly it is "a clean-room implementation based entirely on network analysis"; `pamplona-future`/`catalyst-mitm` are the same Beat-Revival lineage (packet captures + `zeroKilo`/`jacobtread` public TDF work). Mirror's Edge Catalyst = **Blaze 15.1.1.0.5**, FIFA 17 = **Blaze 15.1.1.3.0** — same SDK generation. | | **(B?) unverified 3P** | `refs/z7_*.txt` in the scratchpad — pre-existing reference captures of *unknown* origin, **not** produced by us. Treated as a hint only; every z7 claim below was independently re-derived from (A). Where z7 disagrees with our binary, **our binary wins** (see §6). | **No EA/FIFA leaked source was consulted.** Nothing below is derived from the 2021 leak. --- ## 1. THE HEADLINE: the client is not stalling — it is **logging out** Observed frame #9 (`captures/blaze/session/auth_cmd0x46.bin`): ``` 00 00 00 00 | 00 00 | 00 01 | 00 46 | 00 00 10 | 00 | 00 00 payload=0 meta=0 comp=1 cmd=0x46 msgNum=16 MESSAGE ``` `component 0x0001 (Authentication), command 0x0046 = 70 decimal, EMPTY payload.` **Command 70 = `logout`.** Evidence: 1. **(A) clean-room 3P** — `pamplona-future/src/blaze/components/authentication.ts:10-46` is a full BlazeSDK-15.1.1 `Authentication` command enum, and it lists `logout = 70`. That enum is independently corroborated on three other entries by our own data: - `login = 10` ↔ z7 capture `comp=0x0001 cmd=0x000A [LoginRequest]` - `getAuthToken = 36` ↔ z7 capture `comp=0x0001 cmd=0x0024 [GetAuthTokenResponse]` - `listUserEntitlements2 = 29` ↔ `grid-blaze/src/main.rs:35` routes `1, 29 => list_entitlments` 2. **(A) binary** — FIFA 17's own Authentication RPC-name literal pool at `0x14389d6xx–0x14389d928` contains **exactly the same 33 RPC names** as the pamplona enum (`acceptLegalDocs … logout … upgradeAccount`), i.e. FIFA 17 and MEC ship the *same* Authentication component definition. `"logout"` is at `0x14389d8d8`. 3. `logout` is the only Authentication RPC in that set that takes **no request parameters**, which matches the observed 0-byte payload. (`getAuthToken` is also parameterless but is `0x24`, not `0x46`.) > Confidence: **high**, cross-validated three ways, but *not* 100% binary-pinned — FIFA 17's `getCommandName` for Authentication is not emitted as the `lea rax,[rip+str]; ret` stub pattern that yielded the Util table, and the name pool has **zero** code xrefs (searched every `lea/mov rip-rel` and every aligned+unaligned 8-byte pointer, module-wide). So the id↔name mapping itself is inherited from (A) clean-room 3P, not re-derived from FIFA 17. ### What this changes The client **never attempted `login` (cmd 10)**. It went: `preAuth → ping → 6× fetchClientConfig (all answered EMPTY) → logout → give up` So it is *not* waiting on an Authentication response we failed to send. It decided, **before** issuing any login, that it had nothing to log in with, and tore the Blaze session down. That is consistent with the two-layer gate: it had **no Origin auth code** to put in `LoginRequest.AUTH`, and/or the OSDK layer failed to initialise from the empty configs. **(A) binary corroboration of layer 1:** `FIFA17.exe` contains the localisation key **`TXT_NOT_LOGIN_TO_EBISU`** at `0x1439633e8` (Ebisu = EA's internal codename for Origin), sitting immediately beside `TXT_ORIGIN_GAME_VERSION_OUT_OF_DATE`, `OSDK_PRESENCE_OFFLINE`, `OSDK_OL_STATE_NONE`. The UI flow events `checkOriginConnected` / `OriginIsOnline` / `OriginIsOffline` live at `0x143b4cb58`. This is almost certainly the on-screen message, and it is produced by the **Origin/LSX layer**, not by Blaze. --- ## 2. Authentication component — command ids Component id **0x0001**. Ids **(A) clean-room 3P** (pamplona enum); every name **(A) binary**-confirmed present in FIFA 17's RPC name pool unless noted. | Cmd | RPC | Cmd | RPC | |---|---|---|---| | 10 (0x0A) | **`login`** | 54 | `disableOptIn` | | 11 (0x0B) | `trustedLogin` | 60 (0x3C) | `expressLogin` | | 20 | `updateAccount` | **70 (0x46)** | **`logout`** ← *observed* | | 21 | `upgradeAccount` | 90 (0x5A) | `getPersona` | | 29 (0x1D) | **`listUserEntitlements2`** | 100 | `listPersonas` | | 30 | `getAccount` | 101 | `expressCreateAccount` | | 31 | `grantEntitlement` | 230 | `createWalUserSession` | | 32 | `listEntitlements` | 241 | `acceptLegalDocs` | | 34 | `getUseCount` | 242 | `getEmailOptInSettings` | | 35 | `decrementUseCount` | 246 | `getTermsOfServiceContent` | | 36 (0x24) | **`getAuthToken`** | 260 | `getOriginPersona` | | 38 | `getPasswordRules` | 270 | `checkEmail` | | 39 | `grantEntitlement2` | 280 | `getPersonaNameSuggestions` | | 43 | `modifyEntitlement2` | 290 | `guestLogin` | | 44 | `consumecode` | | | | 45 | `passwordForgot` | | | | 47 | `getPrivacyPolicyContent` | | | | 48 | `listPersonaEntitlements2` | | | | 51 | `checkAgeReq` | | | | 52 | `getOptIn` | | | | 53 | `enableOptIn` | | | FIFA 17 additionally ships types with no id in the MEC enum — `GetUserAccessTokenRequest/Response`, `GetUserXblTokenRequest/Response`, `StressLoginRequest`, `CheckLegalDocRequest/Response`, `GetSuggestionsRequest` — so FIFA's component is a **superset**. Ids for those are unknown. **Error-code encoding (A) binary:** Blaze error codes are `(index << 16) | componentId`. Confirmed by the `0x000N7802` constant block at `0x146de08xx` sitting alongside the `USER_ERR_*` name stubs (component `0x7802` = UserSessions). So an Authentication error is `0xNNNN0001`. --- ## 3. `login` (1/10) request + response TDF — **(A) binary, authoritative** Reflected from FIFA 17's own TDF type descriptors. **Fields MUST be emitted in ascending packed-tag order**, which for A–Z tags is plain alphabetical with `' '` (pad) sorting first — the member tables below are already in that order. ### Request — `Blaze::Authentication::LoginRequest` @ `0x14487ca10` (3 members) | Tag | Member | Type | |---|---|---| | `AUTH` | `authCode` | string ← **the Origin/Nucleus auth code** | | `EXTB` | `externalBlob` | blob | | `EXTI` | `externalId` | uint64 | > z7's `LoginRequest` also carried `ACHT{SHID,SKID}` — **FIFA 17 has no `ACHT` member**. z7 is a different title/version. Ignore it. ### Response — `Blaze::Authentication::LoginResponse` @ `0x14487d170` (**5 members**) | Tag | Member | Type | |---|---|---| | `ANON` | `isAnonymous` | bool | | `NTOS` | `needsLegalDoc` | bool | | `SESS` | `userLoginInfo` | struct `UserLoginInfo` | | `SPAM` | `isOfLegalContactAge` | bool | | `UNDR` | `isUnderage` | bool | > **Important divergence from the MEC emulators.** `grid-blaze/src/models/authentication.rs:23-50` and `pamplona-future/.../authentication.ts:78-128` both emit `CNTX`, `ERRC` and a top-level `SKEY` in the login *payload*. **FIFA 17's `LoginResponse` has none of those.** `CNTX`/`ERRC` are the Blaze **error metadata** block (see `grid-blaze/src/packet.rs:62-71` `ErrorBody`, and its own `// TODO: move ErrorBody to metadata`; pamplona sets `metadataSize: 75` on the login reply). Emit **exactly the 5 members above** in the payload; leave metadata empty on success. The session key lives at `SESS.KEY`, not at top level. ### `SESS` — `Blaze::Authentication::UserLoginInfo` @ `0x14487cb00` (8 members) | Tag | Member | Type | |---|---|---| | `1CON` | `isFirstConsoleLogin` | bool | | `BUID` | `blazeUserId` | int64 | | `FRST` | `isFirstLogin` | bool | | `KEY` | `sessionKey` | string ← **the forged session key** | | `LLOG` | `lastLoginDateTime` | int64 | | `MAIL` | `email` | string | | `PDTL` | `personaDetails` | struct `PersonaDetails` | | `UID` | `userId` | int64 | (`1` = 0x31 → packed 0x11, which is **below** `A` = 0x21, so `1CON` correctly sorts first.) ### `PDTL` — `Blaze::Authentication::PersonaDetails` @ `0x14487cab0` (6 members) | Tag | Member | Type | |---|---|---| | `DSNM` | `displayName` | string | | `LAST` | `lastAuthenticated` | uint32 | | `PID` | `personaId` | int64 | | `PLAT` | `clientPlatform` | enum `ClientPlatformType` | | `STAS` | `status` | enum `PersonaStatus::Code` | | `XREF` | `extId` | uint64 | ### `getAuthToken` (1/36) response — `GetAuthTokenResponse` @ `0x14487d080` Single member: `AUTH` `authToken` : string. (Matches z7 exactly.) ### `listUserEntitlements2` (1/29) response — `Blaze::Authentication::Entitlements` @ `0x14487d4e0` Single member `NLST` : `list`. `Blaze::Authentication::Entitlement` @ `0x14487d490` (16 members, tag order): `DEVI` deviceUri(str), `GDAY` grantDate(str), `GNAM` groupName(str), `ID` id(u64), `ISCO` isConsumable(bool), `PID` personaId(i64), `PJID` projectId(str), `PRCA` productCatalog(enum), `PRID` productId(str), `STAT` status(enum `EntitlementStatus::Code`), `STRC` statusReasonCode(enum), `TAG` entitlementTag(str), `TDAY` terminationDate(str), `TYPE` entitlementType(enum `EntitlementType::Code`), `UCNT` useCount(u32), `VER` version(u32). **(A) binary** enum literals: `EntitlementType::Code` = `ONLINE_ACCESS`, `TRIAL_ONLINE_ACCESS`, `SUBSCRIPTIONS`, `PARENTAL_APPROVAL` (@`0x1438991e8`). `EntitlementStatus::Code` includes `ACTIVE`/`USED`/`UNUSED`/`BANNED`/`DISABLED`. --- ## 4. Forging a session with no Nucleus — the recipe **(A) clean-room 3P** for the *shape* of the forgery, **(A) binary** for every tag. ### 4.1 What a fake session consists of `grid-blaze/src/routes/authentication.rs:17-133` is the canonical minimal pattern: 1. Ignore whatever is in `LoginRequest.AUTH` (it never validates it against Nucleus — it swaps in Discord OAuth; an offline emulator just skips validation entirely). 2. Mint a `User { user_id, persona_id, username }` from local storage / config. 3. Store it on the session (`session.data.set_user`). 4. **Push the `UserAuthenticated` notification** (see §4.3). 5. Reply with `LoginResponse`. ### 4.2 Session key Both 3P emulators use a **canned literal**; the client does not verify it: - `grid-blaze` uses simply `"0"` for both `SKEY` and `SESS.KEY`. - `pamplona-future` uses a realistic-looking `"0540000031e5dde8_wT9NlhYTUidv3EMiZo7kaRMYV0x3$x72YrtOC*QU1v"`. Real Blaze session keys look like `<16 hex>_<44 random base64-ish chars>`. Recommend generating that shape once per session and reusing the **same string** in `LoginResponse.SESS.KEY` **and** in the `UserAuthenticated` notification's `KEY` — they must match. ### 4.3 The `UserAuthenticated` notification — **(A) binary, exact** Component **`0x7802` (30722, UserSessions)**, command **`0x0008`**, msgType = **NOTIFICATION (2)**, msgNum = 0. Payload type is **`Blaze::UserSessionLoginInfo`** @ `0x14486f920`, **16 members**: | Tag | Member | Type | |---|---|---| | `1CON` | `isFirstConsoleLogin` | bool | | `ALOC` | `accountLocale` | uint32 | | `BUID` | `blazeUserId` | int64 | | `CGID` | `connectionGroupObjectId` | ObjectId (triple) | | `DSNM` | `displayName` | string | | `FRST` | `isFirstLogin` | bool | | `KEY` | `sessionKey` | string | | `LAST` | `lastAuthenticated` | uint32 | | `LLOG` | `lastLoginDateTime` | int64 | | `MAIL` | `email` | string | | `NASP` | `personaNamespace` | string | | `PID` | `personaId` | int64 | | `PLAT` | `clientPlatform` | enum | | `UID` | `userId` | int64 | | `USTP` | `userSessionType` | enum `UserSessionType` | | `XREF` | `extId` | uint64 | > This is a **hard confirmation** of the z7 reference: `z7_userauth_notif.txt` (`comp=0x7802 cmd=0x0008 [UserSessions::UserAuthenticated]`) carries exactly these 16 tags. It also **corrects both MEC emulators**, which call notification 30722/8 `updateHardwareFlags` — that is the name of *command* 8, not *notification* 8. The payload they build is right; the name is wrong. ### 4.4 `UserSessionExtendedDataUpdate` — **(A) binary** `Blaze::UserSessionExtendedDataUpdate` @ `0x1448703e0`, 3 members: `DATA` (struct `UserSessionExtendedData`), `SUBS` (bool), `USID` (int64). This is what pamplona/grid-blaze mislabel `validateSessionKey` and send as **notification 30722/1**. `Blaze::UserSessionExtendedData` @ `0x144870390` — **12 members** (tag order): `ADDR` address(`NetworkAddress`, union), `BPS` bestPingSiteAlias(str), `CTY` country(str), `CVAR` clientData(variable), `DMAP` dataMap(`map`), `HWFG` hardwareFlags, `ISP` iSP(str), **`PSLM` latencyList(`list`)**, `QDAT` qosData, `TZ` timeZone(str), `UATT` userInfoAttribute(u64), `ULST` blazeObjectIdList(`list`). > Two FIFA-17-specific deltas vs the MEC emulators: FIFA has **`PSLM`** (they don't), and FIFA has **`BPS` as a top-level string member** (they wrap it inside the `ADDR` union as `BPS `). Follow the FIFA layout. `Blaze::Util::NetworkQosData` (`QDAT`) @ `0x14486e680`: `BWHR` u32, `DBPS` u32, `NAHR` u32, `NATT` enum `NatType`, `UBPS` u32. ### 4.5 Persona identity must match the Origin emu From `stp-origin_emu.ini [Globals]`: `PersonaId=33068179`, `PersonaName=CAGE`, `Language=en_US`. Use `BUID = PID = 33068179`, `DSNM = "CAGE"`, `NASP = "cem_ea_id"` (namespace confirmed by our own PreAuthResponse being accepted), `PLAT = 4` (pc), `USTP = 0`. Mismatch trips `AUTH_ERR_INVALID_PERSONA` / `AUTH_ERR_USER_DOES_NOT_MATCH_PERSONA` / `AUTH_ERR_PERSONA_NOT_FOUND` — all present in the binary at `0x146e0ed91`, `0x146e0eebf`, `0x146e0ecf8`. --- ## 5. `Util::fetchClientConfig` (9/1) — what to return Request: `Blaze::Util::FetchClientConfigRequest` = `{ CFID: string }`. Response: `Blaze::Util::FetchConfigResponse` @ `0x1448752e0` = **single member `CONF` : `map`**. (Note: **not** wrapped in an extra struct — the extra nesting only exists inside `PreAuthResponse`, where `CONF` is a `FetchConfigResponse` struct whose own single member is also called `CONF`. Easy to get wrong.) ### 5.1 The `BlazeSDK` section (returned inside `PreAuthResponse.CONF.CONF`) Our capture shows the client asks for this **inside the preAuth request**: `FCCR { CFID = 'BlazeSDK' }`. **(A) binary** — these are the config keys FIFA 17 actually parses (string literals present in the exe; the "absent" ones are MEC-only and are silently ignored by FIFA 17): | Key | Present in FIFA17.exe | Owner (from adjacent literals) | |---|---|---| | `connIdleTimeout` | ✅ `0x1438a0a58` | `ConnectionManager` | | `defaultRequestTimeout` | ✅ `0x1438a0a40` | `ConnectionManager` | | `pingPeriod` | ✅ `0x1438a0a30` | `ConnectionManager` | | `autoReconnectEnabled` | ✅ `0x1438a0a68` | `ConnectionManager` | | `maxReconnectAttempts` | ✅ `0x1438a0a80` | `ConnectionManager` | | `enableQosFirewallTest` | ✅ `0x1438a09f0` | `ConnectionManager`/`QosManager` | | `enableQosBandwidthTest` | ✅ `0x1438a0a08` | `ConnectionManager`/`QosManager` | | **`nucleusConnect`** | ✅ `0x14389fef8` | **`LoginStateMachineImpl`** | | **`nucleusConnectTrusted`** | ✅ `0x14389fdf8` | **`LoginStateMachineImpl`** | | `associationListSkipInitialSet` | ✅ `0x143b6eb88` | `AssociationListAPI` | | `userManagerMaxCachedUsers` | ✅ | UserManager | | `voipHeadsetUpdateRate` | ✅ | VoIP | | `nucleusPortal` | ❌ absent | MEC-only | | `nucleusProxy` | ❌ absent | MEC-only | | `bytevaultHostname` / `bytevaultPort` / `bytevaultSecure` | ❌ absent | MEC-only | | `xblTokenUrn`, `xboxOneStringValidationUri`, `xlspConnectionIdleTimeout` | ❌ absent | MEC/Xbox-only | **How `nucleusConnect` is used — (A) binary.** The `LoginManagerImpl` / `LoginStateMachineImpl` string cluster at `0x14389fd50–0x14389fef8` reads, in order: ``` LoginManagerImpl · LoginData · LoginStateMachineImpl · LoginStateMachine nucleusConnectTrusted · "%s/connect/token" · "grant_type=client_credentials" recvBuf · "NEXUS_S2S " · "\"access_token\" : \"" · headers LoginStateBase::buffer LoginStateAuthenticated::mTermsOfServiceBuffer LoginStateAuthenticated::mPrivacypolicyBuffer nucleusConnect ``` So the client builds **`/connect/token`**, POSTs `grant_type=client_credentials`, and scrapes `"access_token" : "` out of the JSON reply. **Point `nucleusConnect` / `nucleusConnectTrusted` at our own HTTPS listener and serve a canned OAuth token JSON** — that is the Blaze-side half of defeating auth. There is a `NEXUS_S2S` header value involved. `LoginStateAuthenticated` also buffers ToS + privacy-policy text, which is why `NTOS` (`needsLegalDoc`) in `LoginResponse` should be **0**. Recommended `BlazeSDK` map (keep it minimal — FIFA 17 ignores unknown keys): ``` associationListSkipInitialSet = 1 autoReconnectEnabled = 0 connIdleTimeout = 90000000 defaultRequestTimeout = 30000000 enableQosBandwidthTest = false enableQosFirewallTest = false maxReconnectAttempts = 0 nucleusConnect = https://accounts.ea.com <- repoint to us nucleusConnectTrusted = https://accounts2s.ea.com <- repoint to us pingPeriod = 20000000 userManagerMaxCachedUsers = 0 ``` ### 5.2 The `OSDK_*` sections — **(A) binary** `OSDK_CORE`, `OSDK_CLIENT`, `OSDK_NUCLEUS`, `OSDK_WEBOFFER`, `OSDK_ABUSE_REPORTING`, `OSDK_TICKER` are literals at `0x143962be8..0x143962c40`, and they sit **inside the `ResourceLoader` / `NETRESOURCE` / `LoadResourceFromMultiUrl` / `netres` string cluster**. Adjacent source path: `.../extern/OSDK/8.01.03.00-fifa.01/source/common/presencedownloadmanagerabstract.cpp` → the game embeds **OSDK 8.01.03.00-fifa.01**. Meaning: these sections are **game-tuning key/value maps** consumed by FIFA's OSDK layer (a `ResourceLoader` with states `LOADING`/`LOADED`/`NOT_FOUND`), not Blaze plumbing. The key namespace is `OSDK_*`; confirmed examples of *real* config keys (as opposed to the many `OSDK_*` localisation ids): `OSDK_PRESENCE_DELAY`, `OSDK_PRESENCE_POLL`, `OSDK_ABUSE_NUM_TYPES`, `OSDK_ANTIGRIEFING_MAX_COUNT`, `OSDK_ARENA_ENABLED`, `OSDK_ARENA_CHALLENGE_SCHEDULE_URL`, `OSDK_ARENA_REGISTER_EMAIL_URL`, `OSDK_CLUBS_MAX_SEARCH_RESULT`, `OSDK_CLUBS_LOAD_MEMBER_PAGE_SIZE`, `OSDK_CLUBS_MAX_USERS_FOR_GAME`, `OSDK_CLUBS_LEADERBOARD_CLUB_MAX`, `OSDK_CLUBS_INCOME_SEARCH_MAX`. There is **no `nucleusConnect`-equivalent in `OSDK_NUCLEUS`** that we could find; the Nucleus URLs are BlazeSDK-level (§5.1). `OSDK_NUCLEUS` most likely holds Nucleus *tuning* (poll intervals, retry counts). > **Assessment:** returning an empty `CONF` map for the `OSDK_*` sections is probably **not** what killed us — these are tuning values that fall back to defaults. Answering them non-empty is cheap insurance, but the real blocker is layer 1 (Origin/LSX `GetAuthCode` / `OriginIsOnline`). Also note `OSDK_XMS_ABUSE_REPORTING` (which the client requested) is **not** in the literal block — so the section list is built dynamically. ### 5.3 `IdentityParams` Not requested by FIFA 17 in our capture, but both MEC emulators and z7 answer it identically: `display = console2/welcome`, `redirect_uri = http://127.0.0.1/success`. --- ## 6. Post-login RPC order **(A) wire (ours)** for everything up to `logout`. Beyond that, **(A) clean-room 3P** + z7, since we have never got past it. Observed by us (FIFA 17): ``` 1 9/7 Util::preAuth (req carries FCCR{CFID='BlazeSDK'}, CINF{CLNT='FIFA17', BSDK='15.1.1.3.0'}) 2 9/2 Util::ping -- reconnect -- 3 9/1 Util::fetchClientConfig CFID=OSDK_CORE 4 9/1 CFID=OSDK_CLIENT 5 9/1 CFID=OSDK_NUCLEUS 6 9/1 CFID=OSDK_WEBOFFER 7 9/1 CFID=OSDK_ABUSE_REPORTING 8 9/1 CFID=OSDK_XMS_ABUSE_REPORTING 9 1/70 Authentication::logout <-- gave up here ``` Expected happy path (compiled from `grid-blaze/src/main.rs:32-44`, `pamplona-future/.../util.ts:41-74`, and the z7 msgNum ordering): ``` 9/7 Util::preAuth -> PreAuthResponse 9/1 Util::fetchClientConfig -> FetchConfigResponse (xN) 1/10 Authentication::login -> LoginResponse << NOTIFY 30722/8 UserAuthenticated (UserSessionLoginInfo) 9/8 Util::postAuth -> PostAuthResponse << NOTIFY 30722/1 UserSessionExtendedDataUpdate << NOTIFY 30722/2 (UserAdded — DATA + USER) 1/29 Authentication::listUserEntitlements2 -> Entitlements{NLST} 9/28 Util::setClientState -> empty reply (req: MODE=1, STAT=0) 1/36 Authentication::getAuthToken -> {AUTH: ""} 9/10 Util::userSettingsLoad -> UserSettingsResponse 25/6 AssociationLists::getLists -> GetListsResponse{LMAP} 30722/20 UserSessions::updateNetworkInfo -> empty reply 9/22 Util::setClientMetrics -> empty reply 9/2 Util::ping (every pingPeriod) ``` Ordering caveat: in `grid-blaze` the `UserAuthenticated` notification is pushed **from inside the login handler, before the login reply is written** (`routes/authentication.rs:128-132`); pamplona writes the reply first, then the notification (`authentication.ts:54-57`). Both apparently work. `postAuth` pushes its two notifications around its reply (`util.ts:60-64`: extendedDataAttribute → reply → extendedData). ### `Util::postAuth` (9/8) — **(A) binary** Request `Blaze::Util::PostAuthRequest` @ `0x1448757c0`: **`DSUI` dirtySockUserIndex(int32), `UDID` uniqueDeviceId(string)** — only 2 members. *(z7 shows a third `MAC` field; FIFA 17 does not have it. Another z7 mismatch.)* Response `Blaze::Util::PostAuthResponse` @ `0x144875810`: `TELE`, `TICK`, `UROP`. - `TELE` = `GetTelemetryServerResponse` @ `0x144875470`, 15 members: `ADRS`(str) `ANON`(bool) `DISA`(str) `EDCT`(bool) `FILT`(str) `LOC`(u32) `MINR`(bool) `NOOK`(str) `PORT`(u32) `SDLY`(u32) `SESS`(str) `SKEY`(str) `SPCT`(u32) `STIM`(str) `SVNM`(str). - `TICK` = `GetTickerServerResponse` @ `0x1448754c0`, 3 members: `ADRS`(str) `PORT`(u32) `SKEY`(str). - `UROP` = `UserOptions` @ `0x144875770`, 2 members: `TMOP`(enum `TelemetryOpt`) `UID`(int64). ### Bug in our current responder `Blaze::Util::PingResponse` @ `0x144875560` has **exactly one member: `STIM` (serverTime, uint32)**. `blaze_responder_v2.py` sends `STIM` **and** `TIME`. `TIME` is not a member of FIFA 17's `PingResponse` (it is MEC's). Harmless-ish, but drop it. --- ## 7. Association lists (25/6) — for completeness **(A) clean-room 3P** only (`grid-blaze/src/models/association_lists.rs`). Response `GetListsResponse{ LMAP: list }`; each entry is `INFO{ BOID(ObjectId) FLGS(u8) LID{LNM(str) TYPE(u8)} LMS(u32) PNAM(str) PRID(u8) PRMS(u32) }`, `OFRC`, `TOCT`. MEC ships `friendList`(type 1), `followList`(type 5), `communicationBlockList`(type 4). FIFA's list names are **not** verified — do not assume. --- ## 8. Recommended next actions 1. **Fix layer 1 first.** The client logs out *before* trying to log in, and the on-screen string is `TXT_NOT_LOGIN_TO_EBISU`. Make the in-process LSX server on `127.0.0.1:4216` answer `OriginIsOnline` / `GetInternetConnectedState` as **online**, and `GetAuthCode` with any non-empty code. Without that there is nothing to put in `LoginRequest.AUTH` and Blaze work is unreachable. 2. Implement `Util::fetchClientConfig` returning a non-empty `CONF` map (`{CONF: map}`) for every `CFID`, even if only a couple of keys — removes it as a variable. 3. Put `nucleusConnect` / `nucleusConnectTrusted` in the **preAuth** `CONF` map pointing at our own listener, and serve `POST /connect/token` returning `{"access_token" : ""}`. 4. Implement `Authentication::login` (1/10) → the 5-member `LoginResponse` above, then push `UserAuthenticated` (30722/8, 16 members) with the **same** session-key string, using PersonaId 33068179 / "CAGE". 5. Implement `Util::postAuth` (9/8), `Authentication::listUserEntitlements2` (1/29) returning one `ONLINE_ACCESS` entitlement (`PJID`/offer id `1027460`, `TYPE`=ONLINE_ACCESS, `STAT`=ACTIVE), `Util::setClientState` (9/28) empty, `Authentication::getAuthToken` (1/36) → `{AUTH}`. 6. Keep handling `Authentication::logout` (1/70) with an empty reply — but treat receiving it as a **failure signal** in the responder log, not a normal step. --- ## 9. File index **Cloned reference repos (scratchpad):** - `/tmp/.../scratchpad/grid-blaze/src/routes/authentication.rs` — login handler + entitlements - `/tmp/.../scratchpad/grid-blaze/src/models/authentication.rs` — AuthResponse/Entitlement serialisers - `/tmp/.../scratchpad/grid-blaze/src/routes/util.rs`, `src/models/util.rs` — preAuth/postAuth/fetchClientConfig - `/tmp/.../scratchpad/grid-blaze/src/models/user_sessions.rs` — the 4 session notifications - `/tmp/.../scratchpad/grid-blaze/src/main.rs:32-44` — full route table - `/tmp/.../scratchpad/grid-blaze/src/packet.rs` — Fire2 framing (matches our corrected layout) - `/tmp/.../scratchpad/pamplona-future/src/blaze/components/authentication.ts:10-46` — **the command-id enum** - `/tmp/.../scratchpad/pamplona-future/src/blaze/components/util.ts:17-39` — Util command enum - `/tmp/.../scratchpad/pamplona-future/src/blaze/components/user-sessions.ts:21-50` — UserSessions command enum - `/tmp/.../scratchpad/catalyst-mitm/blaze/interceptor.ts` — redirector request XML shape - `/tmp/.../scratchpad/tdf/src/{writer,reader,tag,types}.rs` — reference TDF codec **Our own artefacts:** - `/home/alex/Documents/OpenFUT/fifa17-recon/captures/blaze/session/session_full.log` — the live session - `/home/alex/Documents/OpenFUT/fifa17-recon/captures/blaze/session/auth_cmd0x46.bin` — the logout frame - `/home/alex/Documents/OpenFUT/fifa17-recon/tools/preauth_schema_reflection.md` — prior reflection write-up + Util command table **Tooling written/used this pass (scratchpad):** - `reflect2.py` — TDF type-descriptor walker (`raw ` / `index ` / `byname`) - `authscan.py`, `stubrange.py`, `allstubs.txt` — `lea/ret` command-name stub recovery - `findstr2.py`, `nameblk.py`, `allstr.py`, `xref.py`, `rvatab.py`, `notifid.py` — string/xref/constant hunting - `clusters.json` — all 124 name-stub clusters (includes the UserSessions notification-name cluster `ServerDraining, UserAdded, UserAuthenticated, UserRemoved, UserSessionExtendedDataUpdate, UserUnauthenticated` @ `0x146de19c0`) **Descriptor VAs (FIFA17.exe, base `0x140000000`):** `LoginRequest 0x14487ca10` · `LoginResponse 0x14487d170` · `UserLoginInfo 0x14487cb00` · `PersonaDetails 0x14487cab0` · `GetAuthTokenResponse 0x14487d080` · `ExpressLoginRequest 0x14487d0d0` · `Entitlement 0x14487d490` · `Entitlements 0x14487d4e0` · `AccountInfo 0x14487c810` · `PersonaInfo 0x14487c7c0` · `UserSessionLoginInfo 0x14486f920` · `UserSessionLogoutInfo 0x14486f970` · `UserSessionExtendedData 0x144870390` · `UserSessionExtendedDataUpdate 0x1448703e0` · `Util::PostAuthRequest 0x1448757c0` · `Util::PostAuthResponse 0x144875810` · `Util::FetchConfigResponse 0x1448752e0` · `Util::PingResponse 0x144875560` · `Util::PreAuthResponse 0x144875600`