Skip to content

fix: gate DFX auth at the source instead of HomeBloc only#304

Merged
TaprootFreak merged 1 commit into
RealUnitCH:developfrom
joshuakrueger-dfx:fix/bitbox-auth-skip-everywhere
May 8, 2026
Merged

fix: gate DFX auth at the source instead of HomeBloc only#304
TaprootFreak merged 1 commit into
RealUnitCH:developfrom
joshuakrueger-dfx:fix/bitbox-auth-skip-everywhere

Conversation

@joshuakrueger-dfx
Copy link
Copy Markdown
Collaborator

Summary

  • Move the BitBox short-circuit out of HomeBloc._setupFiatService and into DFXAuthService.getAuthToken() so every caller is covered
  • getAuthToken() returns null for BitboxCredentials wallets, which the existing callers already treat as "not authenticated"

Why

bitbox_flutter's ETHSignMessage panics on a NACK from the device and crashes the Flutter engine (panics in gomobile bindings cannot be caught from Dart). #301 added a guard inside HomeBloc._setupFiatService so the auto-auth on Home would not crash a BitBox-backed wallet, but the same getAuthToken() path is reachable from DfxKycService, DfxSupportService, DfxWidgetService, and every future DFXAuthService consumer — each one would still crash the app the moment the user touched that feature.

Putting the check at the source means the workaround can stay in one place until the SDK returns the error gracefully (tracked in DFXswiss/bitbox_flutter).

Changes

  • lib/packages/service/dfx/dfx_auth_service.dart: getAuthToken() short-circuits to null when the active credentials are BitboxCredentials
  • lib/screens/home/bloc/home_bloc.dart: drop the now-redundant guard and the bitbox_credentials import

Test plan

bitbox_flutter's ETHSignMessage panics on a NACK from the device and
crashes the Flutter engine. The HomeBloc check from RealUnitCH#301 only blocks
the auto-auth on Home, but the same call path is reachable from
DfxKycService, DfxSupportService, DfxWidgetService, and any future
DFXAuthService consumer — every one of them would crash the app for
a BitBox-backed wallet.

Move the BitBox short-circuit into DFXAuthService.getAuthToken()
itself: it returns null for BitBox wallets, which the existing
callers already handle as "not authenticated". Removed the now
redundant guard in HomeBloc._setupFiatService.
@TaprootFreak TaprootFreak merged commit f1b62b4 into RealUnitCH:develop May 8, 2026
1 check passed
TaprootFreak added a commit that referenced this pull request May 12, 2026
> [!IMPORTANT]
> **Merge-blocked on DFXswiss/bitbox_flutter#11.** Without that SDK PR's
per-message scoped dedup + 60 s BLE read timeout, the EIP-712 sign
aborts at step 1→2 (BLE-layer retransmit corrupts the U2F HID frame
stream) and / or after the 10 s timeout fires mid-confirmation.
Sequence: merge #11 → tag `v0.0.3` → bump this PR's `pubspec.yaml` from
`v0.0.2` to `v0.0.3` → merge here.

## Summary
- `KycCubit` hoists the disclaimer + form (name/address) + EIP-712
13-step BitBox sign in front of every state past the email step.
Returning users at `level >= requiredLevel` previously dropped straight
into `KycCompleted` without ever touching the BitBox; the
hardware-wallet ceremony is the security gate, not the backend KYC
level. `_bitboxConfirmed` is per-`KycCubit` instance and resets on every
KYC entry, so each entry forces a fresh confirmation.
- `KycRegistrationSubmitCubit` treats every `ApiException` raised after
a successful EIP-712 sign as `RegistrationStatus.completed`. The user
has proven hardware-wallet control on the device — the backend's logical
reply (already registered, wallet linked to another account, merge
required, …) is informational. `KycCubit.checkKyc()` then resolves the
next state from the refreshed status, including the existing
`KycAccountMergeRequested` page when the wallet is bound to another DFX
user. Network / parse / sign errors raise non-`ApiException` types and
still surface as a `KycRegistrationSubmitFailure` SnackBar.
- `Eip712Signer._signTypedData` now throws when the signature comes back
empty or `'0x'`. The bitbox_flutter iOS bridge returns empty bytes when
the user cancels mid-sign or the BLE link drops; before this PR the
empty signature was POSTed and silently accepted as a successful sign.
- TFA handling: the cubit recognises both `statusCode == 403` and `code
== 'TFA_REQUIRED'` as the trigger for routing to the 2FA step (paired
with #310's `httpStatusCode` fallback so the code path is reachable).
- `checkKyc` is wrapped in a 30 s top-level timeout. The
email-auto-registration recursion has a one-shot guard so a backend that
does not bump the level after `registerEmail` cannot keep the loading
spinner alive forever. An `isClosed` guard after the API fetch in
`_runCheckKyc` prevents a slow response from overwriting a timeout
failure.
- `DFXAuthService`: removed the BitBox auth skip from #304 (no longer
needed after the v0.0.2 SDK fixes — see inline comment for context).
Added a 3 min sign timeout for the BitBox 13-step ceremony, 20 s HTTP
timeouts, and an empty-signature guard for personal-message signing.
- Email verification shows a localized BitBox sign hint
(`registerEmailVerificationBitboxSignHint`) below the confirm button
while the signature ceremony is running.

## Test plan
- [x] `flutter analyze` — no issues
- [x] `flutter test` — 188 tests pass
- [ ] Fresh wallet without RealUnit registration: email → disclaimer →
form → BitBox 13 fields + sign → ident
- [ ] Wallet already attached to the same RealUnit user, KYC level < 30:
disclaimer → form → BitBox 13 fields + sign → ident (backend's
already-registered reply tolerated, KYC continues)
- [ ] **Wallet attached to a different DFX user**: disclaimer → form →
BitBox 13 fields + sign → `KycAccountMergePage` ("Identität in anderem
Konto gefunden, Merge per Email bestätigen") instead of the generic
failure screen
- [ ] Returning user already at `level >= 30`: disclaimer + form +
BitBox sign first, then `KycCompleted` (no silent grant)
- [ ] Cancel the BitBox sign mid-ceremony → `Signature was empty`
SnackBar, no silent success
- [ ] BitBox not connected on submit → existing modal sheet pops (light
theme), reconnect → retry
- [ ] Backend response with `code: TFA_REQUIRED` → cubit emits
`KycSuccess(twoFa)` instead of `KycFailure`

---------

Co-authored-by: TaprootFreak <142087526+TaprootFreak@users.noreply.github.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.

2 participants