Skip to content

Commit f422aa4

Browse files
committed
Add params to quasar.connect()
1 parent 3237b3a commit f422aa4

File tree

7 files changed

+775
-15
lines changed

7 files changed

+775
-15
lines changed

docs/PRIVATE_KEY_ACCESS.md

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Quasar Private Key Access Feature
2+
3+
## Overview
4+
5+
The Quasar wallet now supports requesting private key access during connection. This is a **high-security feature** that should only be used when absolutely necessary and with trusted applications.
6+
7+
## API Changes
8+
9+
### Updated `connect()` Method
10+
11+
The `connect()` method now accepts a `QuasarConnectionParams` object:
12+
13+
```typescript
14+
interface QuasarConnectionParams {
15+
address?: string; // Connect to specific wallet address
16+
return_private_key?: boolean; // Request private key access
17+
}
18+
19+
// New signature
20+
async connect(params?: QuasarConnectionParams | string): Promise<WalletAccount[]>
21+
```
22+
23+
### Backward Compatibility
24+
25+
The method maintains backward compatibility. You can still pass a string as the address:
26+
27+
```javascript
28+
// Old way (still works)
29+
await quasar.connect("0x1234567890abcdef...");
30+
31+
// New way
32+
await quasar.connect({
33+
address: "0x1234567890abcdef...",
34+
return_private_key: false
35+
});
36+
```
37+
38+
## Usage Examples
39+
40+
### Basic Connection
41+
```javascript
42+
// Connect normally (no changes needed)
43+
const accounts = await quasar.connect();
44+
```
45+
46+
### Connect with Private Key Access
47+
```javascript
48+
// Request private key access
49+
const result = await quasar.connect({
50+
return_private_key: true
51+
});
52+
53+
// Private key will be included in the response
54+
console.log('Private key:', result.privateKey);
55+
```
56+
57+
### Connect to Specific Address with Private Key
58+
```javascript
59+
// Connect to specific wallet and request private key
60+
const result = await quasar.connect({
61+
address: "0x1234567890abcdef...",
62+
return_private_key: true
63+
});
64+
```
65+
66+
## Security Features
67+
68+
When `return_private_key: true` is requested:
69+
70+
### 1. Security Warning Dialog
71+
- A prominent warning is displayed to the user
72+
- Clear indication of the security implications
73+
- Visual emphasis on the risks
74+
75+
### 2. Confirmation Phrase Requirement
76+
- User must type the exact phrase: **"I understand the security risks"**
77+
- Prevents accidental approval
78+
- Ensures user awareness of the implications
79+
80+
### 3. Visual Indicators
81+
- Red color scheme for private key requests
82+
- Warning icons and messages
83+
- Disabled approve button until phrase is correctly entered
84+
85+
## Security Best Practices
86+
87+
### For Developers
88+
1. **Only request private key access when absolutely necessary**
89+
2. **Never store private keys in logs or persistent storage**
90+
3. **Use private key access only for legitimate cryptographic operations**
91+
4. **Implement proper key handling and memory cleanup**
92+
5. **Test only with development/test wallets**
93+
94+
### For Users
95+
1. **Only approve private key requests from trusted applications**
96+
2. **Verify the website URL before entering the confirmation phrase**
97+
3. **Never approve private key requests from unknown websites**
98+
4. **Use separate test wallets for development**
99+
100+
## Response Format
101+
102+
When private key access is granted, the response includes additional fields:
103+
104+
```javascript
105+
{
106+
}
107+
```
108+
109+
## Error Handling
110+
111+
```javascript
112+
113+
```
114+
115+
## Testing
116+
117+
Use the provided test page: `tests/private-key-test.html`
118+
119+
This page includes:
120+
- Normal connection test
121+
- Private key access test
122+
- Multi-parameter connection test
123+
- Security warning demonstrations
124+
125+
## Implementation Details
126+
127+
### Files Modified
128+
- `src/lib/browser/wallet-injection.ts` - Updated connect method signature
129+
- `src/pages/Background/index.js` - Enhanced connection parameter handling
130+
- `src/pages/Popup/RequestDialog.tsx` - Added security warning UI and validation
131+
- `src/pages/Popup/RequestDialog.css` - Styled security warning components
132+
- `src/pages/Panel/Panel.tsx` - Updated dev tools to support new parameters
133+
134+
### Security Measures Implemented
135+
1. **Phrase verification** - Exact match required for confirmation
136+
2. **Visual warnings** - Red color scheme and warning icons
137+
3. **Disabled controls** - Approval disabled until phrase is entered
138+
4. **Clear messaging** - Explicit security warnings and implications
139+
5. **Parameter validation** - Proper handling of connection parameters
140+
141+
## Migration Guide
142+
143+
### From v5.4.30 and earlier
144+
```javascript
145+
// Old code
146+
const accounts = await quasar.connect();
147+
const accountsWithAddress = await quasar.connect("0x123...");
148+
149+
// New code (backward compatible)
150+
const accounts = await quasar.connect();
151+
const accountsWithAddress = await quasar.connect("0x123...");
152+
153+
// Or use new parameter object
154+
const accounts = await quasar.connect({});
155+
const accountsWithAddress = await quasar.connect({
156+
address: "0x123..."
157+
});
158+
159+
// New private key access
160+
const result = await quasar.connect({
161+
return_private_key: true
162+
});
163+
```
164+
165+
No breaking changes - all existing code continues to work unchanged.

src/lib/browser/wallet-injection.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface BrowserInfo {
2929
export interface WalletAccount {
3030
address: string;
3131
publicKey: string;
32+
privateKey?: string;
3233
curve: string;
3334
}
3435

@@ -54,6 +55,11 @@ export interface TransactionResponse {
5455
error?: string;
5556
}
5657

58+
export interface QuasarConnectionParams {
59+
address?: string;
60+
return_private_key?: boolean;
61+
}
62+
5763
export interface WalletEvents {
5864
accountsChanged: (accounts: WalletAccount[]) => void;
5965
chainChanged: (chainId: string) => void;
@@ -205,9 +211,17 @@ class QuasarWallet {
205211
}
206212

207213
// Public API methods - now using relayMap for type safety
208-
async connect(address?: string): Promise<WalletAccount[]> {
214+
async connect(params?: QuasarConnectionParams | string): Promise<WalletAccount[]> {
209215
try {
210-
const payload = address ? { address } : undefined;
216+
let payload: QuasarConnectionParams | undefined;
217+
218+
// Handle backward compatibility - if params is a string, treat it as address
219+
if (typeof params === 'string') {
220+
payload = { address: params };
221+
} else {
222+
payload = params;
223+
}
224+
211225
const result = await this.sendMessage('QUASAR_CONNECT', payload);
212226
if (result && result.accounts) {
213227
this.isConnected = true;

src/pages/Background/index.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ async function getWalletData(origin = null, hostname = null) {
103103
}
104104

105105
// Handle wallet connection request
106-
async function handleConnectWallet(origin, hostname, requestedAddress = null) {
107-
console.log(`Connection request from: ${hostname}${requestedAddress ? ` for address: ${requestedAddress}` : ''}`);
106+
async function handleConnectWallet(origin, hostname, connectionParams = null) {
107+
console.log(`Connection request from: ${hostname}${connectionParams?.address ? ` for address: ${connectionParams.address}` : ''}${connectionParams?.return_private_key ? ' [PRIVATE KEY REQUESTED]' : ''}`);
108108

109109
// Check if already connected
110110
if (connectedSites.has(origin)) {
@@ -116,7 +116,8 @@ async function handleConnectWallet(origin, hostname, requestedAddress = null) {
116116
hostname,
117117
title: 'Get Wallet Data',
118118
message: `Getting wallet data for ${hostname}`,
119-
requestedAddress
119+
requestedAddress: connectionParams?.address,
120+
connectionParams
120121
});
121122

122123
return new Promise((resolve, reject) => {
@@ -166,10 +167,11 @@ async function handleConnectWallet(origin, hostname, requestedAddress = null) {
166167
origin,
167168
hostname,
168169
title: 'Connect Wallet',
169-
message: requestedAddress
170-
? `${hostname} wants to connect to wallet with address: ${requestedAddress}`
170+
message: connectionParams?.address
171+
? `${hostname} wants to connect to wallet with address: ${connectionParams.address}`
171172
: `${hostname} wants to connect to your wallet`,
172-
requestedAddress
173+
requestedAddress: connectionParams?.address,
174+
connectionParams
173175
});
174176

175177
// Wait for user response
@@ -345,7 +347,7 @@ if (browserAPI.runtime.onMessage) {
345347
break;
346348

347349
case 'CONNECT_WALLET':
348-
handleConnectWallet(message.origin, message.hostname, message.payload?.address).then(result => {
350+
handleConnectWallet(message.origin, message.hostname, message.payload).then(result => {
349351
sendResponse(result);
350352
}).catch(error => {
351353
sendResponse({ success: false, error: error.message });

src/pages/Panel/Panel.tsx

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ interface SignMessageParams {
2222
}
2323

2424
interface ConnectParams {
25-
chainId?: string;
26-
requestedChains?: string[];
25+
address?: string;
26+
return_private_key?: boolean;
2727
}
2828

2929
interface DialogState {
@@ -54,6 +54,8 @@ const Panel: React.FC = () => {
5454
});
5555

5656
const [connectForm, setConnectForm] = useState<ConnectParams>({
57+
address: undefined,
58+
return_private_key: false,
5759
chainId: undefined,
5860
requestedChains: []
5961
});
@@ -622,26 +624,59 @@ const Panel: React.FC = () => {
622624
<button
623625
type="button"
624626
className="preset-btn"
625-
onClick={() => setConnectForm({ chainId: undefined, requestedChains: [] })}
627+
onClick={() => setConnectForm({ address: undefined, return_private_key: false, chainId: undefined, requestedChains: [] })}
626628
>
627629
Default
628630
</button>
629631
<button
630632
type="button"
631633
className="preset-btn"
632-
onClick={() => setConnectForm({ chainId: 'stellaris', requestedChains: ['stellaris'] })}
634+
onClick={() => setConnectForm({ address: undefined, return_private_key: false, chainId: 'stellaris', requestedChains: ['stellaris'] })}
633635
>
634636
Stellaris
635637
</button>
636638
<button
637639
type="button"
638640
className="preset-btn"
639-
onClick={() => setConnectForm({ chainId: 'ethereum', requestedChains: ['ethereum', 'polygon'] })}
641+
onClick={() => setConnectForm({ address: undefined, return_private_key: false, chainId: 'ethereum', requestedChains: ['ethereum', 'polygon'] })}
640642
>
641643
Multi-chain
642644
</button>
645+
<button
646+
type="button"
647+
className="preset-btn"
648+
onClick={() => setConnectForm({ address: undefined, return_private_key: true, chainId: undefined, requestedChains: [] })}
649+
style={{ background: '#ff6b6b', color: 'white' }}
650+
>
651+
Test Private Key
652+
</button>
643653
</div>
644654
</div>
655+
<div className="form-group">
656+
<label>Specific Address (optional)</label>
657+
<input
658+
type="text"
659+
value={connectForm.address || ''}
660+
onChange={(e) => setConnectForm({ ...connectForm, address: e.target.value || undefined })}
661+
placeholder="e.g., 0x1234567890abcdef..."
662+
/>
663+
<small style={{ color: '#666', fontSize: '11px' }}>
664+
Connect to a specific wallet address
665+
</small>
666+
</div>
667+
<div className="form-group">
668+
<label style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
669+
<input
670+
type="checkbox"
671+
checked={connectForm.return_private_key || false}
672+
onChange={(e) => setConnectForm({ ...connectForm, return_private_key: e.target.checked })}
673+
/>
674+
Request Private Key Access
675+
</label>
676+
<small style={{ color: '#ff6b6b', fontSize: '11px' }}>
677+
⚠️ WARNING: This grants full access to the wallet. Only use for testing!
678+
</small>
679+
</div>
645680
<div className="form-group">
646681
<label>Chain ID (optional)</label>
647682
<input

0 commit comments

Comments
 (0)