Skip to content

Commit ec1a976

Browse files
committed
Enhance wallet session handling and authorization checks
- Updated the RootLayout component to include a delay before refetching the wallet session after authorization, ensuring cookies are set properly. - Improved the wallet queries in the PageWallets and useUserWallets hooks to only execute when the user is authorized, preventing 403 errors. - Added additional error handling for wallet address mismatches in the wallet router, enhancing security and user experience. - Implemented loading states and delays for smoother transitions during data fetching.
1 parent fb11e7d commit ec1a976

File tree

4 files changed

+69
-10
lines changed

4 files changed

+69
-10
lines changed

src/components/common/overall-layout/layout.tsx

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -371,19 +371,35 @@ export default function RootLayout({
371371
// Don't refetch here - let the natural query refetch handle it if needed
372372
}, []);
373373

374-
const handleAuthModalAuthorized = useCallback(() => {
374+
const handleAuthModalAuthorized = useCallback(async () => {
375375
setShowAuthModal(false);
376376
setCheckingSession(false);
377377
setHasCheckedSession(true); // Mark as checked so we don't check again
378378
// Show loading skeleton for smooth transition
379379
setShowPostAuthLoading(true);
380-
// Refetch session after authorization to update state (but don't show modal again)
381-
void refetchWalletSession();
380+
381+
// Wait a moment for the cookie to be set by the browser, then refetch session
382+
await new Promise(resolve => setTimeout(resolve, 200));
383+
384+
// Refetch session to update state
385+
await refetchWalletSession();
386+
387+
// Invalidate wallet queries so they refetch with the new session
388+
// Use a small delay to ensure cookie is available on subsequent requests
389+
setTimeout(() => {
390+
const userAddressForInvalidation = userAddress || address;
391+
if (userAddressForInvalidation) {
392+
void ctx.wallet.getUserWallets.invalidate({ address: userAddressForInvalidation });
393+
void ctx.wallet.getUserNewWallets.invalidate({ address: userAddressForInvalidation });
394+
void ctx.wallet.getUserNewWalletsNotOwner.invalidate({ address: userAddressForInvalidation });
395+
}
396+
}, 300);
397+
382398
// Hide loading after a brief delay to allow data to load
383399
setTimeout(() => {
384400
setShowPostAuthLoading(false);
385-
}, 1000);
386-
}, [refetchWalletSession]);
401+
}, 1500);
402+
}, [refetchWalletSession, ctx.wallet, userAddress, address]);
387403

388404
// Memoize computed route values
389405
const isWalletPath = useMemo(() => router.pathname.includes("/wallets/[wallet]"), [router.pathname]);

src/components/pages/homepage/wallets/index.tsx

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,21 @@ export default function PageWallets() {
2929
const [showArchived, setShowArchived] = useState(false);
3030
const userAddress = useUserStore((state) => state.userAddress);
3131

32+
// Check wallet session authorization before enabling queries
33+
const { data: walletSession } = api.auth.getWalletSession.useQuery(
34+
{ address: userAddress ?? "" },
35+
{
36+
enabled: !!userAddress && userAddress.length > 0,
37+
refetchOnWindowFocus: false,
38+
},
39+
);
40+
const isAuthorized = walletSession?.authorized ?? false;
41+
3242
const { data: newPendingWallets, isLoading: isLoadingNewWallets } = api.wallet.getUserNewWallets.useQuery(
3343
{ address: userAddress! },
3444
{
35-
enabled: userAddress !== undefined,
45+
// Only enable query when user is authorized (prevents 403 errors)
46+
enabled: userAddress !== undefined && isAuthorized,
3647
retry: (failureCount, error) => {
3748
// Don't retry on authorization errors (403)
3849
if (error && typeof error === "object") {
@@ -60,15 +71,24 @@ export default function PageWallets() {
6071
api.wallet.getUserNewWalletsNotOwner.useQuery(
6172
{ address: userAddress! },
6273
{
63-
enabled: userAddress !== undefined,
74+
// Only enable query when user is authorized (prevents 403 errors)
75+
enabled: userAddress !== undefined && isAuthorized,
6476
retry: (failureCount, error) => {
6577
// Don't retry on authorization errors (403)
6678
if (error && typeof error === "object") {
67-
const err = error as { code?: string; message?: string; data?: { code?: string } };
79+
const err = error as {
80+
code?: string;
81+
message?: string;
82+
data?: { code?: string; httpStatus?: number };
83+
shape?: { code?: string; message?: string };
84+
};
85+
const errorMessage = err.message || err.shape?.message || "";
6886
const isAuthError =
6987
err.code === "FORBIDDEN" ||
7088
err.data?.code === "FORBIDDEN" ||
71-
err.message?.includes("Address mismatch");
89+
err.data?.httpStatus === 403 ||
90+
err.shape?.code === "FORBIDDEN" ||
91+
errorMessage.includes("Address mismatch");
7292
if (isAuthError) return false;
7393
}
7494
return failureCount < 1;

src/hooks/useUserWallets.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,22 @@ import { DbWalletWithLegacy } from "@/types/wallet";
77
export default function useUserWallets() {
88
const network = useSiteStore((state) => state.network);
99
const userAddress = useUserStore((state) => state.userAddress);
10+
11+
// Check wallet session authorization before enabling queries
12+
const { data: walletSession } = api.auth.getWalletSession.useQuery(
13+
{ address: userAddress ?? "" },
14+
{
15+
enabled: !!userAddress && userAddress.length > 0,
16+
refetchOnWindowFocus: false,
17+
},
18+
);
19+
const isAuthorized = walletSession?.authorized ?? false;
20+
1021
const { data: wallets, isLoading } = api.wallet.getUserWallets.useQuery(
1122
{ address: userAddress! },
1223
{
13-
enabled: userAddress !== undefined,
24+
// Only enable query when user is authorized (prevents 403 errors)
25+
enabled: userAddress !== undefined && isAuthorized,
1426
staleTime: 1 * 60 * 1000, // 1 minute (user/wallet data)
1527
gcTime: 5 * 60 * 1000, // 5 minutes
1628
retry: (failureCount, error) => {

src/server/api/routers/wallets.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,12 @@ export const walletRouter = createTRPCRouter({
9898
: ctx.sessionAddress
9999
? [ctx.sessionAddress]
100100
: [];
101+
// If user has an active session, validate that the requested address is authorized
102+
// Throw error if address doesn't match (security: prevent unauthorized access)
101103
if (addresses.length > 0 && !addresses.includes(input.address)) {
102104
throw new TRPCError({ code: "FORBIDDEN", message: "Address mismatch" });
103105
}
106+
// Query wallets where the user is a signer
104107
return ctx.db.wallet.findMany({
105108
where: {
106109
signersAddresses: {
@@ -119,6 +122,8 @@ export const walletRouter = createTRPCRouter({
119122
: ctx.sessionAddress
120123
? [ctx.sessionAddress]
121124
: [];
125+
// If user has an active session, validate that the requested address is authorized
126+
// Throw error if address doesn't match (security: prevent unauthorized access)
122127
if (addresses.length > 0 && !addresses.includes(input.address)) {
123128
throw new TRPCError({ code: "FORBIDDEN", message: "Address mismatch" });
124129
}
@@ -268,9 +273,12 @@ export const walletRouter = createTRPCRouter({
268273
: ctx.sessionAddress
269274
? [ctx.sessionAddress]
270275
: [];
276+
// If user has an active session, validate that the requested address is authorized
277+
// Throw error if address doesn't match (security: prevent unauthorized access)
271278
if (addresses.length > 0 && !addresses.includes(input.address)) {
272279
throw new TRPCError({ code: "FORBIDDEN", message: "Address mismatch" });
273280
}
281+
// Query new wallets owned by the user
274282
return ctx.db.newWallet.findMany({
275283
where: {
276284
ownerAddress: input.address,
@@ -287,9 +295,12 @@ export const walletRouter = createTRPCRouter({
287295
: ctx.sessionAddress
288296
? [ctx.sessionAddress]
289297
: [];
298+
// If user has an active session, validate that the requested address is authorized
299+
// Throw error if address doesn't match (security: prevent unauthorized access)
290300
if (addresses.length > 0 && !addresses.includes(input.address)) {
291301
throw new TRPCError({ code: "FORBIDDEN", message: "Address mismatch" });
292302
}
303+
// Query new wallets where user is a signer but not the owner
293304
return ctx.db.newWallet.findMany({
294305
where: {
295306
signersAddresses: {

0 commit comments

Comments
 (0)