auth-flow
This SPA has no Web3 providers (no wagmi, magic-sdk, core-web3, thirdweb). Authentication is entirely localStorage-based, which is what lets lightweight routes ship without ~580-780KB of crypto deps.
Wallet & identity hooks
useWalletAddress()(src/hooks/useWalletAddress.ts) — readssingle-sign-on-*keys fromlocalStorage. Subscribes to MetaMaskaccountsChangedand cross-tabstorageevents. Sync store pattern (useSyncExternalStore), safe on lightweight routes (no Redux needed).useAuthIdentity()(src/hooks/useAuthIdentity.ts) — wrapsuseWalletAddress, returns{ identity, hasValidIdentity, address }. Identity comes fromlocalStorageGetIdentity()(@dcl/single-sign-on-client).
Sign-in / Sign-out
- Sign-in: navbar button → redirects to the external
/authSSO dapp. On return, identity is inlocalStorageand the hooks pick it up automatically via thestorageevent. - Sign-out: navbar button →
useWalletAddress.disconnect()clears theselocalStoragekeys:single-sign-on-*wagmi*wc@2*dcl_magic_user_emaildcl_thirdweb_user_email
Signed mutations
Routes that mutate data (whats-on create event, social communities, storage uploads, etc.) call signedFetch(url, identity) from src/utils/signedFetch.ts. The helper signs the request payload with the identity's ephemeral key — backend validates and authorizes.
Blog public endpoints don't need identity.
Boundary rule (lightweight tier)
useAuthIdentity and useWalletAddress are safe on lightweight routes. They don't import Redux or Web3 providers. The navbar (src/components/LandingNavbar/) consumes useWalletAddress directly and is part of the lightweight <Layout />.
Do NOT introduce wagmi, magic-sdk, core-web3, or thirdweb into this repo. The whole reason for the dual-shell architecture is that lightweight routes stay lean — see CLAUDE.md > Architecture > Dual Shell.
Pitfalls
- Forgetting that
useWalletAddressis reactive — components re-render when the user signs in/out from another tab. If you cache identity inuseState, you'll show stale data. - Reaching for wagmi or magic-sdk because they're familiar — those would re-add the ~580KB of deps we deleted.
- Calling
signedFetchon a route that runs beforeuseAuthIdentitysettles →identityisundefinedand the request 401s. Gate onhasValidIdentity. - OTP/Magic email sign-in maps to a stable on-chain wallet address. Sign-in pending snapshot logic must fingerprint the ephemeral payload, not just addresses.
/blog/sign-inuses a parallel redirect helper (blogAuthRedirect) that does NOT callmarkSignInPending— any wallet-switcher flow there will land on a stale wallet.