From a37cb0b372009c31af5f0f2d647ee50b1657869c Mon Sep 17 00:00:00 2001 From: Redth Date: Tue, 21 Apr 2026 12:40:22 -0400 Subject: [PATCH] Fix Apple SMS 2FA verification failing with 'Invalid security code' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Show verification method dropdown when any phone numbers exist (was > 1) so single-phone users can switch from Trusted Device to SMS - Call RequestSmsCodeAsync to trigger Apple to send the SMS when phone is selected (auto-selected or user-switched) — was never called from UI - Add session headers (X-Apple-ID-Session-Id, scnt) to RequestSmsCodeAsync and use cached service key — requests were failing without session context Fixes #157 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Services/AppleDownloadAuthService.cs | 7 +++++- .../Pages/Modals/XcodeDownloadAuthModal.razor | 24 +++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/MauiSherpa.Core/Services/AppleDownloadAuthService.cs b/src/MauiSherpa.Core/Services/AppleDownloadAuthService.cs index 1e3b67f..cbe197b 100644 --- a/src/MauiSherpa.Core/Services/AppleDownloadAuthService.cs +++ b/src/MauiSherpa.Core/Services/AppleDownloadAuthService.cs @@ -284,11 +284,15 @@ public async Task RequestSmsCodeAsync(TwoFactorMethod phone) try { - var serviceKey = await GetServiceKeyAsync(); + var serviceKey = _pendingServiceKey ?? await GetServiceKeyAsync(); if (serviceKey == null) return false; var request = new HttpRequestMessage(HttpMethod.Put, $"{AuthUrl}/verify/phone"); request.Headers.Add("X-Apple-Widget-Key", serviceKey); + if (_pendingSessionId != null) + request.Headers.Add("X-Apple-ID-Session-Id", _pendingSessionId); + if (_pendingScnt != null) + request.Headers.Add("scnt", _pendingScnt); request.Content = new StringContent( JsonSerializer.Serialize(new { @@ -298,6 +302,7 @@ public async Task RequestSmsCodeAsync(TwoFactorMethod phone) Encoding.UTF8, "application/json"); var response = await _httpClient.SendAsync(request); + _logger.LogInformation($"SMS code request response: {(int)response.StatusCode} {response.StatusCode}"); return response.IsSuccessStatusCode; } catch (Exception ex) diff --git a/src/MauiSherpa/Pages/Modals/XcodeDownloadAuthModal.razor b/src/MauiSherpa/Pages/Modals/XcodeDownloadAuthModal.razor index 5fd954f..0ffcea8 100644 --- a/src/MauiSherpa/Pages/Modals/XcodeDownloadAuthModal.razor +++ b/src/MauiSherpa/Pages/Modals/XcodeDownloadAuthModal.razor @@ -4,6 +4,7 @@ @using MauiSherpa.Pages.Forms @inject HybridFormBridgeHolder BridgeHolder @inject XcodeManagementViewModel ViewModel +@inject IAppleDownloadAuthService AuthService @implements IDisposable @{ @@ -68,11 +69,11 @@ }

- @if (twoFactorOptions?.TrustedPhoneNumbers.Count > 1) + @if (twoFactorOptions?.TrustedPhoneNumbers.Count > 0) {
- @if (twoFactorOptions.CanUseTrustedDevice) { @@ -149,6 +150,21 @@ private void OnFieldChanged() => UpdateWizardState(); + private async Task OnPhoneSelectionChanged() + { + UpdateWizardState(); + await RequestSmsForSelectedPhone(); + } + + private async Task RequestSmsForSelectedPhone() + { + if (selectedPhoneIndex >= 0 && twoFactorOptions?.TrustedPhoneNumbers.Count > selectedPhoneIndex) + { + var phone = twoFactorOptions.TrustedPhoneNumbers[selectedPhoneIndex]; + await AuthService.RequestSmsCodeAsync(phone); + } + } + private void UpdateWizardState() { var bridge = BridgeHolder.Current; @@ -211,6 +227,10 @@ && twoFactorOptions.TrustedPhoneNumbers.Count > 0 ? 0 : -1; + + // If SMS was auto-selected, request the code be sent + if (selectedPhoneIndex >= 0) + await RequestSmsForSelectedPhone(); } else {