|
| 1 | +# Wallet Authentication System |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The Wallet Authentication System provides automatic sign-in verification after wallet connection, ensuring users verify wallet ownership before accessing sensitive information. |
| 6 | + |
| 7 | +## Features |
| 8 | + |
| 9 | +- ✅ **Automatic Popup**: Appears automatically after wallet connection |
| 10 | +- ✅ **Wallet Signature Verification**: Uses typed data signing for secure verification |
| 11 | +- ✅ **Session Persistence**: Authentication persists for the current session |
| 12 | +- ✅ **Auto Reset on Disconnect**: Clears authentication when wallet disconnects |
| 13 | +- ✅ **Theme Support**: Fully integrated with dark/light mode |
| 14 | +- ✅ **Mobile Responsive**: Works seamlessly across all devices |
| 15 | + |
| 16 | +## How It Works |
| 17 | + |
| 18 | +### 1. User Flow |
| 19 | + |
| 20 | +1. User connects wallet via Starknetkit modal |
| 21 | +2. **Automatic popup appears** prompting user to sign in |
| 22 | +3. User clicks "Sign Message" to verify wallet ownership |
| 23 | +4. Wallet prompts user to sign a message (no gas fees) |
| 24 | +5. Upon successful signature, user is authenticated |
| 25 | +6. Authentication persists for the session |
| 26 | +7. When wallet disconnects, authentication resets |
| 27 | + |
| 28 | +### 2. Architecture |
| 29 | + |
| 30 | +#### Components |
| 31 | + |
| 32 | +- **`WalletAuthProvider`**: Context provider managing authentication state |
| 33 | +- **`WalletSignInModal`**: The popup modal for signature verification |
| 34 | +- **`ProtectedContent`**: Wrapper component for sensitive content |
| 35 | +- **`useWalletAuth`**: Hook to access authentication state and functions |
| 36 | +- **`useRequireAuth`**: Hook to require authentication in components |
| 37 | + |
| 38 | +#### File Structure |
| 39 | + |
| 40 | +``` |
| 41 | +frontend/ |
| 42 | +├── app/ |
| 43 | +│ ├── context/ |
| 44 | +│ │ └── wallet-auth-context.tsx # Authentication context |
| 45 | +│ ├── components/ |
| 46 | +│ │ └── modals/ |
| 47 | +│ │ └── WalletSignInModal.tsx # Sign-in modal component |
| 48 | +│ └── layout.tsx # Provider integration |
| 49 | +└── components/ |
| 50 | + └── shared/ |
| 51 | + ├── ProtectedContent.tsx # Protected content wrapper |
| 52 | + └── WalletConnected.tsx # Updated with auth clearing |
| 53 | +``` |
| 54 | + |
| 55 | +## Usage Examples |
| 56 | + |
| 57 | +### 1. Protecting Sensitive Content |
| 58 | + |
| 59 | +Use the `ProtectedContent` component to wrap any sensitive information: |
| 60 | + |
| 61 | +```tsx |
| 62 | +import ProtectedContent from '@/components/shared/ProtectedContent' |
| 63 | + |
| 64 | +function UserSettings() { |
| 65 | + return ( |
| 66 | + <ProtectedContent fallbackMessage="Sign in to view your settings"> |
| 67 | + <div> |
| 68 | + <h2>Private Settings</h2> |
| 69 | + <p>Your sensitive data here...</p> |
| 70 | + </div> |
| 71 | + </ProtectedContent> |
| 72 | + ) |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +### 2. Checking Authentication Status |
| 77 | + |
| 78 | +Use the `useWalletAuth` hook to check authentication status: |
| 79 | + |
| 80 | +```tsx |
| 81 | +import { useWalletAuth } from '@/app/context/wallet-auth-context' |
| 82 | + |
| 83 | +function MyComponent() { |
| 84 | + const { isAuthenticated } = useWalletAuth() |
| 85 | + |
| 86 | + return ( |
| 87 | + <div> |
| 88 | + {isAuthenticated ? ( |
| 89 | + <div>Welcome! You are authenticated.</div> |
| 90 | + ) : ( |
| 91 | + <div>Please authenticate to continue.</div> |
| 92 | + )} |
| 93 | + </div> |
| 94 | + ) |
| 95 | +} |
| 96 | +``` |
| 97 | + |
| 98 | +### 3. Requiring Authentication Before Action |
| 99 | + |
| 100 | +Use the `useRequireAuth` hook to require authentication before performing actions: |
| 101 | + |
| 102 | +```tsx |
| 103 | +import { useRequireAuth } from '@/components/shared/ProtectedContent' |
| 104 | + |
| 105 | +function TransactionComponent() { |
| 106 | + const { requireAuth } = useRequireAuth() |
| 107 | + |
| 108 | + const handleTransaction = () => { |
| 109 | + // Check if user is authenticated before proceeding |
| 110 | + if (!requireAuth()) { |
| 111 | + return // Will show auth modal |
| 112 | + } |
| 113 | + |
| 114 | + // Proceed with transaction |
| 115 | + executeSensitiveOperation() |
| 116 | + } |
| 117 | + |
| 118 | + return <button onClick={handleTransaction}>Execute Transaction</button> |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +### 4. Manually Triggering Auth Modal |
| 123 | + |
| 124 | +```tsx |
| 125 | +import { useWalletAuth } from '@/app/context/wallet-auth-context' |
| 126 | + |
| 127 | +function CustomComponent() { |
| 128 | + const { setIsAuthModalOpen } = useWalletAuth() |
| 129 | + |
| 130 | + return <button onClick={() => setIsAuthModalOpen(true)}>Verify Wallet</button> |
| 131 | +} |
| 132 | +``` |
| 133 | + |
| 134 | +## API Reference |
| 135 | + |
| 136 | +### `useWalletAuth()` Hook |
| 137 | + |
| 138 | +Returns an object with: |
| 139 | + |
| 140 | +- **`isAuthenticated`** (boolean): Whether the user is authenticated |
| 141 | +- **`isAuthModalOpen`** (boolean): Whether the auth modal is open |
| 142 | +- **`setIsAuthModalOpen`** (function): Function to open/close the modal |
| 143 | +- **`authenticate`** (function): Function to mark user as authenticated |
| 144 | +- **`logout`** (function): Function to disconnect wallet and clear auth |
| 145 | +- **`needsAuthentication`** (boolean): Whether user needs to authenticate |
| 146 | + |
| 147 | +### `useRequireAuth()` Hook |
| 148 | + |
| 149 | +Returns an object with: |
| 150 | + |
| 151 | +- **`isAuthenticated`** (boolean): Whether the user is authenticated |
| 152 | +- **`requireAuth`** (function): Function that returns true if authenticated, opens modal if not |
| 153 | + |
| 154 | +### `ProtectedContent` Component |
| 155 | + |
| 156 | +Props: |
| 157 | + |
| 158 | +- **`children`** (ReactNode): The sensitive content to protect |
| 159 | +- **`fallbackMessage`** (string, optional): Custom message when not authenticated |
| 160 | + |
| 161 | +## Technical Details |
| 162 | + |
| 163 | +### Signature Process |
| 164 | + |
| 165 | +The system uses Starknet typed data signing: |
| 166 | + |
| 167 | +1. Creates a typed data structure with: |
| 168 | + |
| 169 | + - Domain: Spherre app identification |
| 170 | + - Message: Sign-in text with wallet address and timestamp |
| 171 | + - Types: StarkNet domain and message types |
| 172 | + |
| 173 | +2. Requests signature from user's wallet |
| 174 | +3. Validates signature exists (signature verification is done client-side) |
| 175 | +4. Marks user as authenticated in session storage |
| 176 | + |
| 177 | +### Session Storage |
| 178 | + |
| 179 | +Authentication state is stored per wallet address: |
| 180 | + |
| 181 | +- Key format: `wallet_auth_{address}` |
| 182 | +- Storage: `sessionStorage` (cleared when browser tab closes) |
| 183 | +- Automatic cleanup on disconnect |
| 184 | + |
| 185 | +### Auto-Trigger Logic |
| 186 | + |
| 187 | +The modal automatically appears when: |
| 188 | + |
| 189 | +1. Wallet address is detected (connected) |
| 190 | +2. User is not already authenticated for that address |
| 191 | +3. Small 500ms delay after wallet connection for smooth UX |
| 192 | + |
| 193 | +## Security Considerations |
| 194 | + |
| 195 | +✅ **Secure**: Uses cryptographic signature verification |
| 196 | +✅ **Gas-free**: No blockchain transactions involved |
| 197 | +✅ **Session-only**: Auth clears when session ends |
| 198 | +✅ **Per-wallet**: Each wallet requires separate authentication |
| 199 | +✅ **No private keys**: Never exposes or stores private keys |
| 200 | + |
| 201 | +## Testing Checklist |
| 202 | + |
| 203 | +- [ ] Connect wallet → Modal appears automatically |
| 204 | +- [ ] Sign message → Authentication succeeds |
| 205 | +- [ ] Reject signature → Error message shown with retry option |
| 206 | +- [ ] Authenticated → ProtectedContent renders children |
| 207 | +- [ ] Not authenticated → ProtectedContent shows fallback |
| 208 | +- [ ] Disconnect wallet → Authentication resets |
| 209 | +- [ ] Reconnect same wallet → Modal appears again (new session) |
| 210 | +- [ ] Dark/light mode → Modal theme updates correctly |
| 211 | +- [ ] Mobile view → Modal is responsive and usable |
| 212 | + |
| 213 | +## Troubleshooting |
| 214 | + |
| 215 | +### Modal doesn't appear after connection |
| 216 | + |
| 217 | +- Check that `WalletAuthProvider` is in the provider tree |
| 218 | +- Check browser console for errors |
| 219 | +- Ensure `StarknetProvider` is above `WalletAuthProvider` |
| 220 | + |
| 221 | +### Authentication doesn't persist |
| 222 | + |
| 223 | +- Check that sessionStorage is enabled in browser |
| 224 | +- Verify wallet address is being captured correctly |
| 225 | +- Check for console errors in `wallet-auth-context.tsx` |
| 226 | + |
| 227 | +### Modal appears too quickly after connection |
| 228 | + |
| 229 | +- Adjust the 500ms delay in `wallet-auth-context.tsx` line 48 |
| 230 | +- Increase timeout for slower wallet connection modals |
| 231 | + |
| 232 | +## Future Enhancements |
| 233 | + |
| 234 | +Potential improvements: |
| 235 | + |
| 236 | +- [ ] Backend signature verification |
| 237 | +- [ ] JWT token generation for API authentication |
| 238 | +- [ ] Remember device option (localStorage with expiry) |
| 239 | +- [ ] Biometric authentication fallback |
| 240 | +- [ ] Multi-signature support for shared accounts |
| 241 | +- [ ] Rate limiting for signature requests |
| 242 | + |
| 243 | +## Support |
| 244 | + |
| 245 | +For issues or questions: |
| 246 | + |
| 247 | +- Check the troubleshooting section above |
| 248 | +- Review the code comments in context files |
| 249 | +- Test with a fresh browser session (incognito mode) |
0 commit comments