shogun-button-react 3.1.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -1
- package/README.md +132 -22
- package/dist/components/ShogunButton.d.ts +3 -2
- package/dist/components/ShogunButton.js +173 -34
- package/dist/connector.js +2 -2
- package/dist/interfaces/connector-options.d.ts +5 -8
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,76 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## Version
|
|
3
|
+
## Version 4.0.0 (Latest)
|
|
4
|
+
|
|
5
|
+
### 💥 Breaking Changes
|
|
6
|
+
- **OAuth Removed**: Removed OAuth authentication support following removal from shogun-core
|
|
7
|
+
- Removed `showOauth` option from `ShogunConnectorOptions`
|
|
8
|
+
- Removed `oauth` configuration from connector options
|
|
9
|
+
- Removed OAuth login/signup methods from button component
|
|
10
|
+
- Removed Google OAuth icon and UI elements
|
|
11
|
+
- Updated `authMethod` type to exclude "oauth"
|
|
12
|
+
|
|
13
|
+
### ✨ New Features
|
|
14
|
+
- **ZK-Proof Authentication**: Added support for anonymous authentication using Zero-Knowledge Proofs
|
|
15
|
+
- New `showZkProof` option to enable/disable ZK-Proof in UI
|
|
16
|
+
- `zkproof` configuration for customizing ZK-Proof settings
|
|
17
|
+
- Login with trapdoor (recovery phrase)
|
|
18
|
+
- Signup generates new anonymous identity with trapdoor
|
|
19
|
+
- Complete anonymity using Semaphore protocol
|
|
20
|
+
- Multi-device support with same trapdoor
|
|
21
|
+
- Added ZK-Proof icon and UI components
|
|
22
|
+
- Added `authMethod: "zkproof"` support
|
|
23
|
+
- Added `seedPhrase` field to `AuthData` interface for trapdoor backup
|
|
24
|
+
|
|
25
|
+
### ⬆️ Dependencies Update
|
|
26
|
+
- **shogun-core**: Updated from `^3.3.8` to `^4.0.0`
|
|
27
|
+
- **rxjs**: Updated from `^7.8.1` to `^7.8.2` (aligned with shogun-core 4.0.0)
|
|
28
|
+
|
|
29
|
+
### 🔄 Migration Guide
|
|
30
|
+
If you were using OAuth authentication:
|
|
31
|
+
- Remove `showOauth` property from your `shogunConnector` configuration
|
|
32
|
+
- Remove `oauth` provider configuration
|
|
33
|
+
- Use alternative authentication methods: Password, MetaMask, WebAuthn, or Nostr
|
|
34
|
+
|
|
35
|
+
For updating from version 3.x:
|
|
36
|
+
- Update dependencies: `yarn upgrade shogun-button-react shogun-core`
|
|
37
|
+
- No API changes required - all existing functionality is maintained
|
|
38
|
+
- Verify compatibility with shogun-core 4.0.0 features
|
|
39
|
+
- New ZK-Proof authentication available (opt-in via `showZkProof: true`)
|
|
40
|
+
|
|
41
|
+
### 📝 New Configuration Options
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
const { core, options } = shogunConnector({
|
|
45
|
+
appName: "My App",
|
|
46
|
+
showZkProof: true, // Enable ZK-Proof authentication
|
|
47
|
+
zkproof: {
|
|
48
|
+
enabled: true,
|
|
49
|
+
defaultGroupId: "my-app-users",
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 🔐 ZK-Proof Authentication Flow
|
|
55
|
+
|
|
56
|
+
**Signup:**
|
|
57
|
+
```typescript
|
|
58
|
+
const result = await signUp("zkproof");
|
|
59
|
+
if (result.success && result.seedPhrase) {
|
|
60
|
+
// User MUST save the seedPhrase (trapdoor)
|
|
61
|
+
console.log("Save this trapdoor:", result.seedPhrase);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Login:**
|
|
66
|
+
```typescript
|
|
67
|
+
const result = await login("zkproof", savedTrapdoor);
|
|
68
|
+
if (result.success) {
|
|
69
|
+
console.log("Logged in anonymously!");
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Version 1.3.4
|
|
4
74
|
|
|
5
75
|
### 🐛 Bug Fixes
|
|
6
76
|
- **Export Gun Pair Fix**: Fixed issue where "Export Pair" option was not accessible from user dropdown - now works correctly without requiring disconnect
|
package/README.md
CHANGED
|
@@ -2,25 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
A comprehensive React component library for seamless integration of Shogun authentication into your applications. This library provides a simple yet powerful way to add multi-method authentication, account management, and real-time data synchronization to your React applications.
|
|
4
4
|
|
|
5
|
+
> **Version 4.0.0** - Compatible with shogun-core ^4.0.0
|
|
6
|
+
|
|
5
7
|
## ✨ Features
|
|
6
8
|
|
|
7
9
|
- 🚀 **Easy Integration** - Simple setup with minimal configuration
|
|
8
10
|
- 🎨 **Customizable UI** - Modern, responsive design with dark mode support
|
|
9
|
-
- 🔒 **Multi-Authentication** - Support for Password, MetaMask, WebAuthn, Nostr, and
|
|
11
|
+
- 🔒 **Multi-Authentication** - Support for Password, MetaMask, WebAuthn, Nostr, and ZK-Proof
|
|
10
12
|
- 🔑 **Account Management** - Export/import Gun pairs for account backup and recovery
|
|
11
13
|
- 📱 **Responsive Design** - Works seamlessly across all device sizes
|
|
12
14
|
- 🌍 **TypeScript Support** - Full type safety and IntelliSense support
|
|
13
15
|
- 🔌 **Plugin System** - Advanced Gun operations with custom hooks
|
|
14
16
|
- 📊 **Real-time Data** - Reactive data synchronization with RxJS observables
|
|
15
17
|
|
|
18
|
+
## 📦 Requirements
|
|
19
|
+
|
|
20
|
+
- **React**: ^18.0.0
|
|
21
|
+
- **shogun-core**: ^4.0.0
|
|
22
|
+
- **Node.js**: ≥18
|
|
23
|
+
|
|
16
24
|
## 🚀 Quick Start
|
|
17
25
|
|
|
18
26
|
### Installation
|
|
19
27
|
|
|
20
28
|
```bash
|
|
21
|
-
npm install shogun-button-react
|
|
29
|
+
npm install shogun-button-react shogun-core
|
|
22
30
|
# or
|
|
23
|
-
yarn add shogun-button-react
|
|
31
|
+
yarn add shogun-button-react shogun-core
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Updating from 3.x
|
|
35
|
+
|
|
36
|
+
If you're upgrading from version 3.x:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
yarn upgrade shogun-button-react shogun-core
|
|
40
|
+
# or
|
|
41
|
+
npm update shogun-button-react shogun-core
|
|
24
42
|
```
|
|
25
43
|
|
|
26
44
|
### Basic Usage
|
|
@@ -37,7 +55,7 @@ function App() {
|
|
|
37
55
|
showMetamask: true,
|
|
38
56
|
showWebauthn: true,
|
|
39
57
|
showNostr: true,
|
|
40
|
-
|
|
58
|
+
showZkProof: true,
|
|
41
59
|
// Optional peers
|
|
42
60
|
peers: [
|
|
43
61
|
"https://gun-manhattan.herokuapp.com/gun"
|
|
@@ -84,21 +102,17 @@ const { core, options } = shogunConnector({
|
|
|
84
102
|
showMetamask: true,
|
|
85
103
|
showWebauthn: true,
|
|
86
104
|
showNostr: true,
|
|
87
|
-
|
|
105
|
+
showZkProof: true,
|
|
88
106
|
|
|
89
107
|
// Network configuration
|
|
90
108
|
peers: [
|
|
91
109
|
"https://gun-manhattan.herokuapp.com/gun"
|
|
92
110
|
],
|
|
93
111
|
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
clientId: "your-google-client-id",
|
|
99
|
-
redirectUri: "https://myapp.com/auth/callback"
|
|
100
|
-
}
|
|
101
|
-
}
|
|
112
|
+
// ZK-Proof configuration
|
|
113
|
+
zkproof: {
|
|
114
|
+
enabled: true,
|
|
115
|
+
defaultGroupId: "my-app-users",
|
|
102
116
|
},
|
|
103
117
|
|
|
104
118
|
// Gun Advanced Plugin configuration
|
|
@@ -132,7 +146,8 @@ interface AuthData {
|
|
|
132
146
|
userPub: string; // User's public key
|
|
133
147
|
username: string; // Display name
|
|
134
148
|
password?: string; // Password (if applicable)
|
|
135
|
-
|
|
149
|
+
seedPhrase?: string; // Seed phrase/trapdoor (for ZK-Proof)
|
|
150
|
+
authMethod?: "password" | "web3" | "webauthn" | "nostr" | "zkproof" | "pair";
|
|
136
151
|
}
|
|
137
152
|
```
|
|
138
153
|
|
|
@@ -223,6 +238,31 @@ function UserProfile() {
|
|
|
223
238
|
}
|
|
224
239
|
};
|
|
225
240
|
|
|
241
|
+
const handleZkProofSignup = async () => {
|
|
242
|
+
try {
|
|
243
|
+
const result = await signUp("zkproof");
|
|
244
|
+
if (result.success && result.seedPhrase) {
|
|
245
|
+
console.log("ZK-Proof signup successful!");
|
|
246
|
+
console.log("SAVE THIS TRAPDOOR:", result.seedPhrase);
|
|
247
|
+
// CRITICAL: User must save the trapdoor for account recovery
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error("ZK-Proof signup failed:", error);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const handleZkProofLogin = async () => {
|
|
255
|
+
try {
|
|
256
|
+
const trapdoor = "user-saved-trapdoor-here";
|
|
257
|
+
const result = await login("zkproof", trapdoor);
|
|
258
|
+
if (result.success) {
|
|
259
|
+
console.log("ZK-Proof anonymous login successful!");
|
|
260
|
+
}
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.error("ZK-Proof login failed:", error);
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
226
266
|
// Example: Account backup and recovery
|
|
227
267
|
const handleExportAccount = async () => {
|
|
228
268
|
try {
|
|
@@ -367,6 +407,84 @@ function UserSettings() {
|
|
|
367
407
|
}
|
|
368
408
|
```
|
|
369
409
|
|
|
410
|
+
### ZK-Proof Anonymous Authentication
|
|
411
|
+
|
|
412
|
+
ZK-Proof (Zero-Knowledge Proof) authentication provides complete anonymity using Semaphore protocol. Users can authenticate without revealing their identity.
|
|
413
|
+
|
|
414
|
+
```tsx
|
|
415
|
+
function ZkProofExample() {
|
|
416
|
+
const { login, signUp } = useShogun();
|
|
417
|
+
const [trapdoor, setTrapdoor] = useState("");
|
|
418
|
+
|
|
419
|
+
// Signup: Creates anonymous identity and returns trapdoor
|
|
420
|
+
const handleSignup = async () => {
|
|
421
|
+
try {
|
|
422
|
+
const result = await signUp("zkproof");
|
|
423
|
+
|
|
424
|
+
if (result.success && result.seedPhrase) {
|
|
425
|
+
// CRITICAL: User MUST save this trapdoor!
|
|
426
|
+
// It's the ONLY way to recover the anonymous account
|
|
427
|
+
console.log("Your trapdoor (save securely):", result.seedPhrase);
|
|
428
|
+
|
|
429
|
+
// Recommend user to:
|
|
430
|
+
// 1. Write it down on paper
|
|
431
|
+
// 2. Store in password manager
|
|
432
|
+
// 3. Keep multiple secure copies
|
|
433
|
+
alert(`Save this trapdoor securely:\n\n${result.seedPhrase}\n\nYou'll need it to login on other devices!`);
|
|
434
|
+
}
|
|
435
|
+
} catch (error) {
|
|
436
|
+
console.error("ZK-Proof signup failed:", error);
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
// Login: Use saved trapdoor to restore anonymous identity
|
|
441
|
+
const handleLogin = async () => {
|
|
442
|
+
try {
|
|
443
|
+
const result = await login("zkproof", trapdoor);
|
|
444
|
+
|
|
445
|
+
if (result.success) {
|
|
446
|
+
console.log("Logged in anonymously!");
|
|
447
|
+
console.log("Identity commitment:", result.userPub);
|
|
448
|
+
}
|
|
449
|
+
} catch (error) {
|
|
450
|
+
console.error("ZK-Proof login failed:", error);
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
return (
|
|
455
|
+
<div>
|
|
456
|
+
<h3>Anonymous Authentication with ZK-Proof</h3>
|
|
457
|
+
|
|
458
|
+
<div>
|
|
459
|
+
<button onClick={handleSignup}>
|
|
460
|
+
Create Anonymous Identity
|
|
461
|
+
</button>
|
|
462
|
+
</div>
|
|
463
|
+
|
|
464
|
+
<div>
|
|
465
|
+
<input
|
|
466
|
+
type="text"
|
|
467
|
+
value={trapdoor}
|
|
468
|
+
onChange={(e) => setTrapdoor(e.target.value)}
|
|
469
|
+
placeholder="Enter your trapdoor to login"
|
|
470
|
+
/>
|
|
471
|
+
<button onClick={handleLogin}>
|
|
472
|
+
Login Anonymously
|
|
473
|
+
</button>
|
|
474
|
+
</div>
|
|
475
|
+
</div>
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**Important Notes about ZK-Proof:**
|
|
481
|
+
|
|
482
|
+
- **Trapdoor is Critical**: The trapdoor is like a master password - without it, the account is permanently lost
|
|
483
|
+
- **No Recovery**: Unlike traditional auth, there's no "forgot password" - trapdoor loss means permanent account loss
|
|
484
|
+
- **Complete Anonymity**: Your identity remains private even from the application
|
|
485
|
+
- **Multi-Device Support**: Use the same trapdoor on different devices
|
|
486
|
+
- **Privacy-Preserving**: Uses Semaphore protocol for zero-knowledge proofs
|
|
487
|
+
|
|
370
488
|
### Connection Monitoring
|
|
371
489
|
|
|
372
490
|
```tsx
|
|
@@ -482,7 +600,6 @@ interface ShogunConnectorOptions {
|
|
|
482
600
|
showMetamask?: boolean;
|
|
483
601
|
showWebauthn?: boolean;
|
|
484
602
|
showNostr?: boolean;
|
|
485
|
-
showOauth?: boolean;
|
|
486
603
|
darkMode?: boolean;
|
|
487
604
|
|
|
488
605
|
// Network configuration
|
|
@@ -496,13 +613,6 @@ interface ShogunConnectorOptions {
|
|
|
496
613
|
signup?: number;
|
|
497
614
|
operation?: number;
|
|
498
615
|
};
|
|
499
|
-
oauth?: {
|
|
500
|
-
providers: Record<string, {
|
|
501
|
-
clientId: string;
|
|
502
|
-
redirectUri?: string;
|
|
503
|
-
clientSecret?: string;
|
|
504
|
-
}>;
|
|
505
|
-
};
|
|
506
616
|
|
|
507
617
|
// Gun Advanced Plugin configuration
|
|
508
618
|
enableGunDebug?: boolean;
|
|
@@ -44,13 +44,14 @@ type ShogunButtonProviderProps = {
|
|
|
44
44
|
userPub: string;
|
|
45
45
|
username: string;
|
|
46
46
|
password?: string;
|
|
47
|
-
authMethod?: "password" | "web3" | "webauthn" | "nostr" | "
|
|
47
|
+
authMethod?: "password" | "web3" | "webauthn" | "nostr" | "zkproof";
|
|
48
48
|
}) => void;
|
|
49
49
|
onSignupSuccess?: (data: {
|
|
50
50
|
userPub: string;
|
|
51
51
|
username: string;
|
|
52
52
|
password?: string;
|
|
53
|
-
|
|
53
|
+
seedPhrase?: string;
|
|
54
|
+
authMethod?: "password" | "web3" | "webauthn" | "nostr" | "zkproof";
|
|
54
55
|
}) => void;
|
|
55
56
|
onError?: (error: string) => void;
|
|
56
57
|
};
|
|
@@ -140,16 +140,18 @@ export function ShogunButtonProvider({ children, core, options, onLoginSuccess,
|
|
|
140
140
|
username = pubkey;
|
|
141
141
|
result = await nostr.login(pubkey);
|
|
142
142
|
break;
|
|
143
|
-
case "
|
|
144
|
-
const
|
|
145
|
-
if (!
|
|
146
|
-
throw new Error("
|
|
147
|
-
const provider = args[0] || "google";
|
|
148
|
-
result = await oauth.login(provider);
|
|
149
|
-
authMethod = "oauth";
|
|
150
|
-
if (result.redirectUrl) {
|
|
151
|
-
return result;
|
|
143
|
+
case "zkproof":
|
|
144
|
+
const trapdoor = args[0];
|
|
145
|
+
if (!trapdoor || typeof trapdoor !== "string") {
|
|
146
|
+
throw new Error("Invalid trapdoor provided");
|
|
152
147
|
}
|
|
148
|
+
const zkproof = core.getPlugin("zkproof");
|
|
149
|
+
if (!zkproof)
|
|
150
|
+
throw new Error("ZK-Proof plugin not available");
|
|
151
|
+
const zkLoginResult = await zkproof.login(trapdoor);
|
|
152
|
+
result = zkLoginResult;
|
|
153
|
+
username = zkLoginResult.username || zkLoginResult.alias || `zk_${(zkLoginResult.userPub || "").slice(0, 16)}`;
|
|
154
|
+
authMethod = "zkproof";
|
|
153
155
|
break;
|
|
154
156
|
default:
|
|
155
157
|
throw new Error("Unsupported login method");
|
|
@@ -226,16 +228,15 @@ export function ShogunButtonProvider({ children, core, options, onLoginSuccess,
|
|
|
226
228
|
username = pubkey;
|
|
227
229
|
result = await nostr.signUp(pubkey);
|
|
228
230
|
break;
|
|
229
|
-
case "
|
|
230
|
-
const
|
|
231
|
-
if (!
|
|
232
|
-
throw new Error("
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
231
|
+
case "zkproof":
|
|
232
|
+
const zkproofPlugin = core.getPlugin("zkproof");
|
|
233
|
+
if (!zkproofPlugin)
|
|
234
|
+
throw new Error("ZK-Proof plugin not available");
|
|
235
|
+
const seed = args[0]; // Optional seed
|
|
236
|
+
const zkSignupResult = await zkproofPlugin.signUp(seed);
|
|
237
|
+
result = zkSignupResult;
|
|
238
|
+
username = zkSignupResult.username || zkSignupResult.alias || `zk_${(zkSignupResult.userPub || "").slice(0, 16)}`;
|
|
239
|
+
authMethod = "zkproof";
|
|
239
240
|
break;
|
|
240
241
|
default:
|
|
241
242
|
throw new Error("Unsupported signup method");
|
|
@@ -249,6 +250,7 @@ export function ShogunButtonProvider({ children, core, options, onLoginSuccess,
|
|
|
249
250
|
onSignupSuccess === null || onSignupSuccess === void 0 ? void 0 : onSignupSuccess({
|
|
250
251
|
userPub: userPub,
|
|
251
252
|
username: displayName,
|
|
253
|
+
seedPhrase: result.seedPhrase, // Include seedPhrase/trapdoor for ZK-Proof
|
|
252
254
|
authMethod: authMethod,
|
|
253
255
|
});
|
|
254
256
|
}
|
|
@@ -489,11 +491,6 @@ const KeyIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/20
|
|
|
489
491
|
React.createElement("circle", { cx: "7.5", cy: "15.5", r: "5.5" }),
|
|
490
492
|
React.createElement("path", { d: "m21 2-9.6 9.6" }),
|
|
491
493
|
React.createElement("path", { d: "m15.5 7.5 3 3L22 7l-3-3" })));
|
|
492
|
-
const GoogleIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor" },
|
|
493
|
-
React.createElement("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z", fill: "#4285F4" }),
|
|
494
|
-
React.createElement("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: "#34A853" }),
|
|
495
|
-
React.createElement("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z", fill: "#FBBC05" }),
|
|
496
|
-
React.createElement("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: "#EA4335" })));
|
|
497
494
|
const NostrIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
|
|
498
495
|
React.createElement("path", { d: "M19.5 4.5 15 9l-3-3-4.5 4.5L9 12l-1.5 1.5L12 18l4.5-4.5L15 12l1.5-1.5L21 6l-1.5-1.5Z" }),
|
|
499
496
|
React.createElement("path", { d: "M12 12 6 6l-1.5 1.5L9 12l-4.5 4.5L6 18l6-6Z" })));
|
|
@@ -518,6 +515,10 @@ const ImportIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org
|
|
|
518
515
|
React.createElement("polyline", { points: "14,2 14,8 20,8" }),
|
|
519
516
|
React.createElement("line", { x1: "16", y1: "13", x2: "8", y2: "13" }),
|
|
520
517
|
React.createElement("line", { x1: "12", y1: "17", x2: "12", y2: "9" })));
|
|
518
|
+
const ZkProofIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
|
|
519
|
+
React.createElement("path", { d: "M12 2L2 7l10 5 10-5-10-5z" }),
|
|
520
|
+
React.createElement("path", { d: "M2 17l10 5 10-5" }),
|
|
521
|
+
React.createElement("path", { d: "M2 12l10 5 10-5" })));
|
|
521
522
|
const ExportIcon = () => (React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
|
|
522
523
|
React.createElement("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
|
|
523
524
|
React.createElement("polyline", { points: "14,2 14,8 20,8" }),
|
|
@@ -547,6 +548,8 @@ export const ShogunButton = (() => {
|
|
|
547
548
|
const [exportedPair, setExportedPair] = useState("");
|
|
548
549
|
const [showCopySuccess, setShowCopySuccess] = useState(false);
|
|
549
550
|
const [showImportSuccess, setShowImportSuccess] = useState(false);
|
|
551
|
+
const [zkTrapdoor, setZkTrapdoor] = useState("");
|
|
552
|
+
const [zkGeneratedTrapdoor, setZkGeneratedTrapdoor] = useState("");
|
|
550
553
|
const dropdownRef = useRef(null);
|
|
551
554
|
// Handle click outside to close dropdown
|
|
552
555
|
useEffect(() => {
|
|
@@ -662,7 +665,57 @@ export const ShogunButton = (() => {
|
|
|
662
665
|
setAuthView("webauthn-username");
|
|
663
666
|
};
|
|
664
667
|
const handleNostrAuth = () => handleAuth("nostr");
|
|
665
|
-
const
|
|
668
|
+
const handleZkProofAuth = () => {
|
|
669
|
+
if (!(core === null || core === void 0 ? void 0 : core.hasPlugin("zkproof"))) {
|
|
670
|
+
setError("ZK-Proof plugin not available");
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
if (formMode === "login") {
|
|
674
|
+
setAuthView("zkproof-login");
|
|
675
|
+
}
|
|
676
|
+
else {
|
|
677
|
+
// For signup, directly call signUp and show trapdoor
|
|
678
|
+
handleZkProofSignup();
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
const handleZkProofLogin = async () => {
|
|
682
|
+
setError("");
|
|
683
|
+
setLoading(true);
|
|
684
|
+
try {
|
|
685
|
+
if (!zkTrapdoor.trim()) {
|
|
686
|
+
throw new Error("Please enter your trapdoor");
|
|
687
|
+
}
|
|
688
|
+
await handleAuth("zkproof", zkTrapdoor);
|
|
689
|
+
setModalIsOpen(false);
|
|
690
|
+
}
|
|
691
|
+
catch (e) {
|
|
692
|
+
setError(e.message || "ZK-Proof login failed");
|
|
693
|
+
}
|
|
694
|
+
finally {
|
|
695
|
+
setLoading(false);
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
const handleZkProofSignup = async () => {
|
|
699
|
+
setError("");
|
|
700
|
+
setLoading(true);
|
|
701
|
+
try {
|
|
702
|
+
const result = await signUp("zkproof");
|
|
703
|
+
if (result && result.success && result.seedPhrase) {
|
|
704
|
+
// Show the trapdoor to the user - CRITICAL for account recovery!
|
|
705
|
+
setZkGeneratedTrapdoor(result.seedPhrase);
|
|
706
|
+
setAuthView("zkproof-signup-result");
|
|
707
|
+
}
|
|
708
|
+
else if (result && !result.success) {
|
|
709
|
+
throw new Error(result.error || "ZK-Proof signup failed");
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
catch (e) {
|
|
713
|
+
setError(e.message || "ZK-Proof signup failed");
|
|
714
|
+
}
|
|
715
|
+
finally {
|
|
716
|
+
setLoading(false);
|
|
717
|
+
}
|
|
718
|
+
};
|
|
666
719
|
const handleRecover = async () => {
|
|
667
720
|
setError("");
|
|
668
721
|
setLoading(true);
|
|
@@ -751,6 +804,8 @@ export const ShogunButton = (() => {
|
|
|
751
804
|
setShowCopySuccess(false);
|
|
752
805
|
setShowImportSuccess(false);
|
|
753
806
|
setRecoveredHint("");
|
|
807
|
+
setZkTrapdoor("");
|
|
808
|
+
setZkGeneratedTrapdoor("");
|
|
754
809
|
};
|
|
755
810
|
const openModal = () => {
|
|
756
811
|
resetForm();
|
|
@@ -783,12 +838,12 @@ export const ShogunButton = (() => {
|
|
|
783
838
|
React.createElement("button", { type: "button", className: "shogun-auth-option-button", onClick: () => handleAuth("nostr"), disabled: loading },
|
|
784
839
|
React.createElement(NostrIcon, null),
|
|
785
840
|
formMode === "login" ? "Login with Nostr" : "Signup with Nostr"))),
|
|
786
|
-
options.
|
|
787
|
-
React.createElement("button", { type: "button", className: "shogun-auth-option-button
|
|
788
|
-
React.createElement(
|
|
841
|
+
options.showZkProof !== false && (core === null || core === void 0 ? void 0 : core.hasPlugin("zkproof")) && (React.createElement("div", { className: "shogun-auth-option-group" },
|
|
842
|
+
React.createElement("button", { type: "button", className: "shogun-auth-option-button", onClick: handleZkProofAuth, disabled: loading },
|
|
843
|
+
React.createElement(ZkProofIcon, null),
|
|
789
844
|
formMode === "login"
|
|
790
|
-
? "Login with
|
|
791
|
-
: "Signup with
|
|
845
|
+
? "Login with ZK-Proof"
|
|
846
|
+
: "Signup with ZK-Proof"))),
|
|
792
847
|
React.createElement("div", { className: "shogun-divider" },
|
|
793
848
|
React.createElement("span", null, "or")),
|
|
794
849
|
React.createElement("button", { type: "button", className: "shogun-auth-option-button", onClick: () => setAuthView("password"), disabled: loading },
|
|
@@ -952,6 +1007,83 @@ export const ShogunButton = (() => {
|
|
|
952
1007
|
setExportedPair("");
|
|
953
1008
|
}
|
|
954
1009
|
}, disabled: loading }, "Back"))));
|
|
1010
|
+
const renderZkProofLoginForm = () => (React.createElement("div", { className: "shogun-auth-form" },
|
|
1011
|
+
React.createElement("h3", null, "Login with ZK-Proof"),
|
|
1012
|
+
React.createElement("div", { style: {
|
|
1013
|
+
backgroundColor: "#f0f9ff",
|
|
1014
|
+
padding: "12px",
|
|
1015
|
+
borderRadius: "8px",
|
|
1016
|
+
marginBottom: "16px",
|
|
1017
|
+
border: "1px solid #0ea5e9",
|
|
1018
|
+
} },
|
|
1019
|
+
React.createElement("p", { style: {
|
|
1020
|
+
fontSize: "14px",
|
|
1021
|
+
color: "#0c4a6e",
|
|
1022
|
+
margin: "0",
|
|
1023
|
+
fontWeight: "500",
|
|
1024
|
+
} }, "\uD83D\uDD12 Anonymous Authentication"),
|
|
1025
|
+
React.createElement("p", { style: { fontSize: "13px", color: "#075985", margin: "4px 0 0 0" } }, "Enter your trapdoor (recovery phrase) to login anonymously using Zero-Knowledge Proofs. Your identity remains private.")),
|
|
1026
|
+
React.createElement("div", { className: "shogun-form-group" },
|
|
1027
|
+
React.createElement("label", { htmlFor: "zkTrapdoor" },
|
|
1028
|
+
React.createElement(KeyIcon, null),
|
|
1029
|
+
React.createElement("span", null, "Trapdoor / Recovery Phrase")),
|
|
1030
|
+
React.createElement("textarea", { id: "zkTrapdoor", value: zkTrapdoor, onChange: (e) => setZkTrapdoor(e.target.value), disabled: loading, placeholder: "Enter your trapdoor...", rows: 4, style: {
|
|
1031
|
+
fontFamily: "monospace",
|
|
1032
|
+
fontSize: "12px",
|
|
1033
|
+
width: "100%",
|
|
1034
|
+
padding: "8px",
|
|
1035
|
+
border: "1px solid #ccc",
|
|
1036
|
+
borderRadius: "4px",
|
|
1037
|
+
} })),
|
|
1038
|
+
React.createElement("button", { type: "button", className: "shogun-submit-button", onClick: handleZkProofLogin, disabled: loading || !zkTrapdoor.trim() }, loading ? "Processing..." : "Login Anonymously"),
|
|
1039
|
+
React.createElement("div", { className: "shogun-form-footer" },
|
|
1040
|
+
React.createElement("button", { className: "shogun-toggle-mode", onClick: () => setAuthView("options"), disabled: loading }, "Back to Login Options"))));
|
|
1041
|
+
const renderZkProofSignupResult = () => (React.createElement("div", { className: "shogun-auth-form" },
|
|
1042
|
+
React.createElement("h3", null, "ZK-Proof Account Created!"),
|
|
1043
|
+
React.createElement("div", { style: {
|
|
1044
|
+
backgroundColor: "#fef3c7",
|
|
1045
|
+
padding: "12px",
|
|
1046
|
+
borderRadius: "8px",
|
|
1047
|
+
marginBottom: "16px",
|
|
1048
|
+
border: "1px solid #f59e0b",
|
|
1049
|
+
} },
|
|
1050
|
+
React.createElement("p", { style: {
|
|
1051
|
+
fontSize: "14px",
|
|
1052
|
+
color: "#92400e",
|
|
1053
|
+
margin: "0",
|
|
1054
|
+
fontWeight: "500",
|
|
1055
|
+
} }, "\u26A0\uFE0F CRITICAL: Save Your Trapdoor!"),
|
|
1056
|
+
React.createElement("p", { style: { fontSize: "13px", color: "#a16207", margin: "4px 0 0 0" } }, "This is your ONLY way to recover your anonymous account. Save it in a secure location. If you lose it, you will lose access to your account permanently.")),
|
|
1057
|
+
React.createElement("div", { className: "shogun-form-group" },
|
|
1058
|
+
React.createElement("label", null, "Your Trapdoor (Recovery Phrase):"),
|
|
1059
|
+
React.createElement("textarea", { value: zkGeneratedTrapdoor, readOnly: true, rows: 4, style: {
|
|
1060
|
+
fontFamily: "monospace",
|
|
1061
|
+
fontSize: "12px",
|
|
1062
|
+
width: "100%",
|
|
1063
|
+
padding: "8px",
|
|
1064
|
+
border: "2px solid #f59e0b",
|
|
1065
|
+
borderRadius: "4px",
|
|
1066
|
+
backgroundColor: "#fffbeb",
|
|
1067
|
+
} }),
|
|
1068
|
+
React.createElement("button", { type: "button", className: "shogun-submit-button", style: { marginTop: "8px" }, onClick: async () => {
|
|
1069
|
+
if (navigator.clipboard) {
|
|
1070
|
+
await navigator.clipboard.writeText(zkGeneratedTrapdoor);
|
|
1071
|
+
setShowCopySuccess(true);
|
|
1072
|
+
setTimeout(() => setShowCopySuccess(false), 3000);
|
|
1073
|
+
}
|
|
1074
|
+
} }, showCopySuccess ? "✅ Copied!" : "📋 Copy Trapdoor"),
|
|
1075
|
+
!navigator.clipboard && (React.createElement("p", { style: { fontSize: "12px", color: "#666", marginTop: "8px" } }, "\u26A0\uFE0F Please manually copy the trapdoor above."))),
|
|
1076
|
+
React.createElement("div", { style: {
|
|
1077
|
+
backgroundColor: "#dcfce7",
|
|
1078
|
+
color: "#166534",
|
|
1079
|
+
padding: "12px",
|
|
1080
|
+
borderRadius: "8px",
|
|
1081
|
+
marginTop: "16px",
|
|
1082
|
+
fontSize: "14px",
|
|
1083
|
+
border: "1px solid #22c55e",
|
|
1084
|
+
textAlign: "center",
|
|
1085
|
+
} }, "\u2705 You're now logged in anonymously!"),
|
|
1086
|
+
React.createElement("button", { type: "button", className: "shogun-submit-button", style: { marginTop: "16px" }, onClick: () => setModalIsOpen(false) }, "Close and Start Using App")));
|
|
955
1087
|
const renderImportForm = () => (React.createElement("div", { className: "shogun-auth-form" },
|
|
956
1088
|
React.createElement("h3", null, "Import Gun Pair"),
|
|
957
1089
|
React.createElement("div", { style: {
|
|
@@ -1024,9 +1156,13 @@ export const ShogunButton = (() => {
|
|
|
1024
1156
|
? "Import Gun Pair"
|
|
1025
1157
|
: authView === "webauthn-username"
|
|
1026
1158
|
? "WebAuthn"
|
|
1027
|
-
:
|
|
1028
|
-
? "Login"
|
|
1029
|
-
:
|
|
1159
|
+
: authView === "zkproof-login"
|
|
1160
|
+
? "ZK-Proof Login"
|
|
1161
|
+
: authView === "zkproof-signup-result"
|
|
1162
|
+
? "ZK-Proof Account"
|
|
1163
|
+
: formMode === "login"
|
|
1164
|
+
? "Login"
|
|
1165
|
+
: "Sign Up"),
|
|
1030
1166
|
React.createElement("button", { className: "shogun-close-button", onClick: closeModal, "aria-label": "Close" },
|
|
1031
1167
|
React.createElement(CloseIcon, null))),
|
|
1032
1168
|
React.createElement("div", { className: "shogun-modal-content" },
|
|
@@ -1045,7 +1181,10 @@ export const ShogunButton = (() => {
|
|
|
1045
1181
|
authView === "export" && renderExportForm(),
|
|
1046
1182
|
authView === "import" && renderImportForm(),
|
|
1047
1183
|
authView === "webauthn-username" &&
|
|
1048
|
-
renderWebAuthnUsernameForm()
|
|
1184
|
+
renderWebAuthnUsernameForm(),
|
|
1185
|
+
authView === "zkproof-login" && renderZkProofLoginForm(),
|
|
1186
|
+
authView === "zkproof-signup-result" &&
|
|
1187
|
+
renderZkProofSignupResult()))))));
|
|
1049
1188
|
};
|
|
1050
1189
|
Button.displayName = "ShogunButton";
|
|
1051
1190
|
return Object.assign(Button, {
|
package/dist/connector.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { ShogunCore } from "shogun-core";
|
|
2
2
|
import { GunAdvancedPlugin } from "./plugins/GunAdvancedPlugin";
|
|
3
3
|
export function shogunConnector(options) {
|
|
4
|
-
const { gunInstance, gunOptions, appName, timeouts,
|
|
4
|
+
const { gunInstance, gunOptions, appName, timeouts, webauthn, nostr, web3, zkproof, showWebauthn, showNostr, showMetamask, showZkProof, darkMode, enableGunDebug = true, enableConnectionMonitoring = true, defaultPageSize = 20, connectionTimeout = 10000, debounceInterval = 100, ...restOptions } = options;
|
|
5
5
|
const core = new ShogunCore({
|
|
6
6
|
gunOptions: gunOptions || undefined,
|
|
7
7
|
gunInstance: gunInstance || undefined,
|
|
8
|
-
oauth,
|
|
9
8
|
webauthn,
|
|
10
9
|
nostr,
|
|
11
10
|
web3,
|
|
11
|
+
zkproof,
|
|
12
12
|
timeouts,
|
|
13
13
|
});
|
|
14
14
|
const setProvider = (provider) => {
|
|
@@ -8,7 +8,7 @@ export interface ShogunConnectorOptions {
|
|
|
8
8
|
showMetamask?: boolean;
|
|
9
9
|
showWebauthn?: boolean;
|
|
10
10
|
showNostr?: boolean;
|
|
11
|
-
|
|
11
|
+
showZkProof?: boolean;
|
|
12
12
|
darkMode?: boolean;
|
|
13
13
|
gunInstance?: IGunInstance;
|
|
14
14
|
gunOptions?: any;
|
|
@@ -17,13 +17,6 @@ export interface ShogunConnectorOptions {
|
|
|
17
17
|
signup?: number;
|
|
18
18
|
operation?: number;
|
|
19
19
|
};
|
|
20
|
-
oauth?: {
|
|
21
|
-
providers: Record<string, {
|
|
22
|
-
clientId: string;
|
|
23
|
-
clientSecret?: string;
|
|
24
|
-
redirectUri?: string;
|
|
25
|
-
}>;
|
|
26
|
-
};
|
|
27
20
|
webauthn?: {
|
|
28
21
|
enabled?: boolean;
|
|
29
22
|
};
|
|
@@ -33,6 +26,10 @@ export interface ShogunConnectorOptions {
|
|
|
33
26
|
web3?: {
|
|
34
27
|
enabled?: boolean;
|
|
35
28
|
};
|
|
29
|
+
zkproof?: {
|
|
30
|
+
enabled?: boolean;
|
|
31
|
+
defaultGroupId?: string;
|
|
32
|
+
};
|
|
36
33
|
enableGunDebug?: boolean;
|
|
37
34
|
enableConnectionMonitoring?: boolean;
|
|
38
35
|
defaultPageSize?: number;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shogun-button-react",
|
|
3
3
|
"description": "Shogun connector button",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "4.0.0",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
7
7
|
"src/styles/index.css"
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"ethers": "^6.13.5",
|
|
35
35
|
"prettier": "^3.5.3",
|
|
36
|
-
"rxjs": "^7.8.
|
|
37
|
-
"shogun-core": "^
|
|
36
|
+
"rxjs": "^7.8.2",
|
|
37
|
+
"shogun-core": "^4.0.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"react": "^18.0.0",
|