Problem
The JoyID connect flow in @joyid/ckb uses authWithPopup() from @joyid/common, which relies on a WebSocket relay to pass the passkey result from phone to desktop.
This relay only works reliably in Chrome/Chromium. On Brave and Firefox:
- The WebSocket relay is blocked or throttled by the browser
- The desktop never receives the signed passkey result
- Users must scan the QR 2–4 times before it sometimes succeeds
- Often times out entirely, leaving users stuck
This affects any dapp using CCC's JoyID connector on desktop Brave/Firefox — which is a significant portion of the CKB developer/user community.
Root Cause
Chrome has Google's own FIDO2 cross-device transport (CTAP2) infrastructure built in. Brave and Firefox route through JoyID's relay server, which these browsers throttle or block.
authWithRedirect() and authCallback() already exist in @joyid/common and work in every browser (full-page redirect to app.joy.id, no WebSocket relay). However, they are not accessible through the CCC JoyID connector — CCC uses authWithPopup() directly.
Workaround
We implemented a custom connector that wraps @joyid/common directly with browser detection:
// Brave/Firefox: authWithRedirect() — full page nav to app.joy.id, no relay needed
// Chrome/mobile: authWithPopup() — existing behaviour unchanged
if (typeof navigator.brave !== 'undefined' || /Firefox/.test(navigator.userAgent)) {
common.authWithRedirect(request); // redirects to app.joy.id
} else {
return common.authWithPopup(request);
}
Result: Brave users go from 2–4 QR scan attempts + frequent timeouts → 1 Face ID tap, done.
Note: navigator.brave.isBrave is a Promise (async) — must use typeof navigator.brave !== 'undefined' for synchronous detection.
Suggested Fix
The CCC JoyID connector could detect the browser and fall back to authWithRedirect() on Brave/Firefox. Alternatively, expose a preferRedirect option.
We also filed nervina-labs/joyid-sdk-js#58 requesting that authWithRedirect, authCallback, and isRedirectFromJoyID be re-exported from @joyid/ckb directly — a one-line fix on their end.
Browser Scorecard (tested on wyltekindustries.com)
- Chrome ✅ popup, 1 shot
- Brave ✅ redirect flow (custom connector), 1 shot
- Chromium ✅ redirect flow, 1 shot
- Safari iOS ✅ 1 tap Face ID
- Firefox ⚠ not supported (CTAP2 hybrid not available in Firefox — we show a clear message)
Impact
Any dapp using CCC + JoyID on desktop Brave/Firefox is affected. Brave has significant adoption in the Web3/crypto community.
Problem
The JoyID connect flow in
@joyid/ckbusesauthWithPopup()from@joyid/common, which relies on a WebSocket relay to pass the passkey result from phone to desktop.This relay only works reliably in Chrome/Chromium. On Brave and Firefox:
This affects any dapp using CCC's JoyID connector on desktop Brave/Firefox — which is a significant portion of the CKB developer/user community.
Root Cause
Chrome has Google's own FIDO2 cross-device transport (CTAP2) infrastructure built in. Brave and Firefox route through JoyID's relay server, which these browsers throttle or block.
authWithRedirect()andauthCallback()already exist in@joyid/commonand work in every browser (full-page redirect toapp.joy.id, no WebSocket relay). However, they are not accessible through the CCC JoyID connector — CCC usesauthWithPopup()directly.Workaround
We implemented a custom connector that wraps
@joyid/commondirectly with browser detection:Result: Brave users go from 2–4 QR scan attempts + frequent timeouts → 1 Face ID tap, done.
Note:
navigator.brave.isBraveis a Promise (async) — must usetypeof navigator.brave !== 'undefined'for synchronous detection.Suggested Fix
The CCC JoyID connector could detect the browser and fall back to
authWithRedirect()on Brave/Firefox. Alternatively, expose apreferRedirectoption.We also filed nervina-labs/joyid-sdk-js#58 requesting that
authWithRedirect,authCallback, andisRedirectFromJoyIDbe re-exported from@joyid/ckbdirectly — a one-line fix on their end.Browser Scorecard (tested on wyltekindustries.com)
Impact
Any dapp using CCC + JoyID on desktop Brave/Firefox is affected. Brave has significant adoption in the Web3/crypto community.