Repository navigation
fix(kyc): resolve authenticated user ID for Etherfuse KYC gates - #1087
Conversation
Plan expiredYour subscription has expired. Please renew your subscription to continue using CI/CD integration and other features. |
|
@mubkid1 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
|
@mubkid1 is attempting to deploy a commit to the kindfi Team on Vercel. A member of the Team first needs to authorize it. |
|
Warning Review limit reachedNext included review available in 59 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (6)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Overview
The Etherfuse on/off-ramp cards constructed their KYC gate with
useKycRequiredGate(userId ?? '')at render time, while the real authenticated user id was fetched later inside the request handler with an uncheckedfetch('/api/auth/user'). When the optionaluserIdprop was absent, the gate was built with''(whosepreflightshort-circuits totrue, silently disabling the gate) and a second, independent resolution of the same id was used for the request body. This change resolves the authenticated user id once, stores it in component state, and uses that single value for both the KYC gate and the Etherfuse request bodies.Related Issue
Closes #1046
Changes
New resolver
apps/web/lib/kyc/resolved-user-id.tsnormalizeUserIdtrims and collapsesnull/undefined/''/whitespace tonull, so an empty string can never look like a resolved principal.resolveAuthenticatedUserIdreturns aprovidedid with no network call, otherwise queries/api/auth/userexactly once and returnsresolvedonly for{ user: { id: <non-empty string> } }(top-levelidaccepted defensively).unavailablewith a machine-readable reason (network_error,http_<status>,invalid_payload,missing_id); it never throws and never returns an empty id.New hook
apps/web/hooks/use-resolved-user-id.tsuseResolvedUserId(userIdProp?)stores the resolved id in state and exposes{ userId, isLoading, error, refresh }.refresh()overlaps an in-flight resolution.null, never''.Modified KYC gate hook
apps/web/hooks/use-kyc-required-gate.tsuserId: string | null(existingstringcallers keep compiling).isPrincipalResolved(falseonly whenuserId === null).nullid means "principal not resolved yet" and must not be treated as "gate disabled"; existingpreflightbehaviour for a non-empty id is unchanged.Modified Etherfuse cards
apps/web/components/sections/projects/manage/escrow/components/etherfuse-off-ramp-card.tsxapps/web/components/sections/projects/manage/escrow/components/etherfuse-on-ramp-card.tsxfetch('/api/auth/user')anduseKycRequiredGate(userId ?? '')withuseResolvedUserId(userId)+useKycRequiredGate(resolvedUserId).handleOffRamp/handleOnRampbail out withtoast.error('Unable to resolve your account. Please sign in again.')when the id isnull, and otherwise send the sameresolvedUserIdin the request body.<KycRequiredGate userId={resolvedUserId} />now receives the exact id used by the request body.isResolvingUserIdis true; denial handling is unchanged.Tests
apps/web/test/kyc-resolved-user-id.test.ts(bun:test)normalizeUserId:null/undefined/empty/whitespace ->null, trimming of real ids.resolveAuthenticatedUserId: provided prop wins with zero fetches (asserted via a spy),user.idresolved, defensive top-levelid, 500 ->http_500, 401 ->http_401, rejected fetch ->network_error, non-JSON body ->invalid_payload, missinguser->missing_id, and empty/whitespace/non-stringuser.id->missing_id(the regression that previously let the gate receive'').unavailableinstead of throwing.Verification Results
The end-to-end suite runs with
bun test(theapps/webtest script isbun test/**.test.{mjs,ts,js}); this PR was authored via the GitHub API without alocal clone, so no
bun testoutput is claimed here. What the added test file covers:user.idand via a defensive top-levelid.''regression specifically — an emptyuser.idresolves tounavailable/missing_id, not a usable id.The type-level part of this change WAS executed, with the real toolchain
(
tsc --strict --noEmit --skipLibCheck, TypeScript from the repository's owndevDependency range). Because
useKycRequiredGategained astring | nullparameter while five other call sites still pass a plain
string, the hook keeps atype parameter so each caller keeps its own
userIdtype:That is why both cards pass
userId={resolvedUserId ?? ''}to<KycRequiredGate>while passing the raw
resolvedUserIdtouseKycRequiredGateand the request body:the component's prop is a required
string, the gate hook's is not.useKycRequiredGatewith an empty user IDuseKycRequiredGate(resolvedUserId), which isnull(not'') until resolveduseResolvedUserIdkeeps it in state and exposes it asuserIdresolvedUserIdis passed touseKycRequiredGate, the request body, and<KycRequiredGate userId={...} />userIdprop is providednullwhile unresolved (no gated request), andrefresh()re-resolvesCloses #1046