Skip to content

refactor: migrate app passcode flows to SwiftUI - #4419

Open
marinofaggiana wants to merge 17 commits into
masterfrom
passcode
Open

marinofaggiana wants to merge 17 commits into
masterfrom
passcode

Conversation

@marinofaggiana

@marinofaggiana marinofaggiana commented Oct 23, 2026 •

Copy link
Copy Markdown
Member

Replace the legacy passcode implementation in the main app and Share Extension with shared SwiftUI components for app unlocking, passcode creation, change and removal, and E2EE authentication. Remove TOPasscodeViewController and its obsolete wrappers, and update third-party acknowledgements.

The implementation keeps the existing Keychain credentials and preferences, supports Face ID and Touch ID, and shares failed-attempt counters and lockout deadlines across passcode operations. Optional requests cancel when their scene enters the background; mandatory app unlocking remains non-dismissible. A dedicated window per scene presents the mandatory lock independently of existing sheets and alerts.

Fix the cumulative failed-attempt counter being cleared when the temporary lockout expires: the 60-second delay after three incorrect attempts now preserves the total used by the optional reset-after-ten-attempts policy. Successful authentication still clears both counters.

Local UI tests

The seven XCUITests in PasscodeUITests are intended to run locally. The CI test command explicitly excludes this suite with -skip-testing:NextcloudUITests/PasscodeUITests; it still compiles the tests and runs the remaining enabled tests. A green CI run therefore does not validate these seven scenarios.

Setup and execution

  1. Use Xcode 27.1 or newer and a dedicated iOS simulator. Set the simulator's system language to English; the shared Test action also uses English / United States. Use a disposable test environment: the last test deliberately resets the app and removes its local data and credentials.
  2. Start Docker Desktop and prepare a Nextcloud stable30 server at http://localhost:8080, with credentials admin / admin. The repository provides Tests/Server.sh. Keep the server running throughout the tests.
  3. Ensure the server's web login uses English:
    docker exec --user www-data xcode-test-server php /var/www/html/occ config:system:set force_language --value=en
    curl --fail --show-error http://localhost:8080/status.php
    The status response should show installed: true, maintenance: false and needsDbUpgrade: false.
  4. Open Nextcloud.xcodeproj, select the Nextcloud scheme and the dedicated simulator, then open the Test navigator (⌘6). Run NextcloudUITests → PasscodeUITests, or run individual methods while investigating a failure.
  5. Allow the full suite to finish. The reset test crosses three real 60-second lockouts, so it takes at least three minutes on its own. Inspect the test report and retained diagnostic attachments if a test fails.

The Debug-only setup guarded by UI_TESTING and UI_TESTING_PASSCODE resets the passcode, counters and related lock settings before each test. The login helper reuses an existing signed-in account or performs the real web login when needed, including dismissal of the password-saving prompt. Persistence checks relaunch without the setup flags so stored credentials, counters and deadlines are preserved.

Scenarios covered

Test Expected behavior
testCreateChangeAndDisablePasscode Create and change the code; reject the old code; disable the lock with the new code.
testDisableLockoutSurvivesReopeningBackgroundAndRelaunch Preserve failed attempts and the original lockout deadline across reopening, background/foreground transitions and process relaunch.
testChangePasscodeCannotResetFailedAttemptsByReopening Reopening the change-code sheet cannot clear failed attempts or bypass the lockout.
testBackgroundCannotBypassMandatoryAppLock Returning from background presents a non-dismissible lock; settings underneath remain inaccessible and dragging cannot dismiss it.
testAppLockAppearsAbovePresentedSheet The lock covers an existing Acknowledgements sheet, which remains open after successful authentication.
testAppLockAppearsAbovePresentedAlert The lock covers an existing Clear cache confirmation alert, which remains open after successful authentication.
testAppResetAfterTenFailuresAcrossLockouts With reset enabled, ten wrong codes across three lockouts reset and close the app; relaunch shows login without the old passcode.

Additional manual verification

  • Face ID / Touch ID success, failure and cancellation on a supported device.
  • Share Extension and E2EE authentication, including cancellation and reset behavior.
  • Independent locking of multiple scenes on iPad.
Screenshot 2026-10-23 alle 09 38 11

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

Support passcode authentication, creation, changes and removal with biometrics and shared lockout protection. Keep the existing passcode flow active until callers are migrated.

Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
Update lock settings after passcode completion and handle reset results. Add toolbar dismissal and require the current passcode when changing it.

Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
Require authentication in Debug builds and verify the account before executing E2EE actions. Present cancellable passcode flows as sheets and handle completion during dismissal.

Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
Use the shared passcode view for app unlock and enforced setup. Scope lifecycle handling to the presenting window scene and cancel optional operations when backgrounded.

Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
@marinofaggiana
marinofaggiana marked this pull request as draft October 23, 2026 07:42
@marinofaggiana marinofaggiana changed the title Passcode refactor: migrate app passcode flows to SwiftUI Oct 23, 2026
Replace TOPasscode in the main app and Share Extension, remove the legacy implementation, and add passcode UI tests.

Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
@marinofaggiana
marinofaggiana marked this pull request as ready for review October 23, 2026 08:56
Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
@marinofaggiana
marinofaggiana marked this pull request as draft October 23, 2026 09:08
Clear only the retry counter and lockout deadline when the delay expires. Add UI coverage verifying app reset after ten failures across lockouts.

Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
@marinofaggiana
marinofaggiana marked this pull request as ready for review October 23, 2026 09:26
Present the mandatory lock in a dedicated window per scene and preserve underlying UI after unlocking.

Add UI regression coverage, improve authentication test handling, and enforce CI test timeouts with retained diagnostics.

Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
Allow the initial HTTPS request to time out before expecting the localhost authentication page, and skip UI queries against authentication services that are not running.

Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
Assisted-by: Codex:GPT-6
Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant