Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/panna-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"lru-cache": "^11.1.0",
"lucide-react": "^0.514.0",
"postcss": "^8.5.6",
"react-cookie": "^8.0.1",
"react-hook-form": "^7.60.0",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.11",
Expand Down
7 changes: 2 additions & 5 deletions packages/panna-sdk/src/core/auth/siwe-auth.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Account } from 'thirdweb/wallets';
import Cookies from 'universal-cookie';
import { authCookieOptions } from '../consts/cookies';
import { PannaApiService } from '../util/api-service';
import type {
AuthChallengeReply,
Expand Down Expand Up @@ -68,11 +69,7 @@ export class SiweAuth {
this.pannaApiService = pannaApiService;
// Cookie configuration for auth token storage
// Using sameSite: 'strict' for CSRF protection, and secure: true to require HTTPS.
this.cookies = new Cookies(null, {
path: '/',
secure: true,
sameSite: 'strict'
});
this.cookies = new Cookies(null, authCookieOptions);

// Load existing auth data from cookies on initialization
if (typeof window !== 'undefined') {
Expand Down
14 changes: 14 additions & 0 deletions packages/panna-sdk/src/core/consts/cookies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// For user preferences (long-lived)
export const preferenceCookieOptions = {
path: '/',
secure: true,
Comment thread
matjazv marked this conversation as resolved.
sameSite: 'strict',
maxAge: 60 * 60 * 24 * 365 // 1 year
} as const;

// For auth tokens (session or short-lived)
export const authCookieOptions = {
path: '/',
secure: true,
sameSite: 'strict'
} as const;
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { CookiesProvider } from 'react-cookie';
import { useActiveAccount } from '@/hooks';
import { preferenceCookieOptions } from '../../../core/consts/cookies';
import { AccountDialog } from '../account/account-dialog';
import { AccountViewProvider } from '../account/account-view-provider';
import { LoginButton } from './login-button';
Expand Down Expand Up @@ -64,7 +66,9 @@ export function ConnectButton({
<>
{account?.address ? (
<AccountViewProvider>
<AccountDialog address={account.address} />
<CookiesProvider defaultSetOptions={preferenceCookieOptions}>
<AccountDialog address={account.address} />
</CookiesProvider>
</AccountViewProvider>
) : (
<LoginButton
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CheckIcon, ChevronDownIcon } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react';
import { useCookies } from 'react-cookie';
import type { UseFormReturn } from 'react-hook-form';
import { DEFAULT_COUNTRY_CODE } from 'src/core';
import type { Country } from '../../types/country.types';
Expand Down Expand Up @@ -34,10 +35,17 @@ type SelectBuyRegionStepProps = {
form: UseFormReturn<BuyFormData>;
};

type CookieMap = {
panna_user_country: Country;
};

export function SelectBuyRegionStep({ form }: SelectBuyRegionStepProps) {
const { next } = useDialogStepper();
const [open, setOpen] = useState(false);
const [query, setQuery] = useState('');
const [cookie, setCookie] = useCookies<'panna_user_country', CookieMap>([
'panna_user_country'
]);

const countries = useMemo(() => {
if (!query) return COUNTRIES_SORTED;
Expand All @@ -55,16 +63,35 @@ export function SelectBuyRegionStep({ form }: SelectBuyRegionStepProps) {
? getCountryByCode(detectedCountryCode)
: null;

// Set default country: detected > US > first available
// Set default country: cookie > (detected > US > first available)
const defaultCountry =
detectedCountry ||
getCountryByCode(DEFAULT_COUNTRY_CODE) ||
COUNTRIES_SORTED[0];
if (defaultCountry) {
if (cookie.panna_user_country?.code) {
const cookieCountry = getCountryByCode(cookie.panna_user_country.code);
if (cookieCountry) {
form.setValue('country', cookieCountry);
} else if (defaultCountry) {
form.setValue('country', defaultCountry);
}
} else if (defaultCountry) {
form.setValue('country', defaultCountry);
}
Comment thread
ikem-legend marked this conversation as resolved.
}
}, [form]);
}, [form, cookie.panna_user_country?.code]);

const handleCountrySubmit = () => {
const selectedCountry = form.getValues('country');
if (!selectedCountry) return;
if (
!cookie.panna_user_country?.code ||
cookie.panna_user_country.code !== selectedCountry.code
) {
setCookie('panna_user_country', selectedCountry);
}
next();
};

return (
<div className="flex flex-col gap-6">
Expand Down Expand Up @@ -142,7 +169,7 @@ export function SelectBuyRegionStep({ form }: SelectBuyRegionStepProps) {
/>
<Button
type="button"
onClick={() => next()}
onClick={handleCountrySubmit}
disabled={!form.watch('country')}
>
Next
Expand Down
33 changes: 33 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.