@volr/react-ui 0.1.0 → 0.1.1
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/README.md +116 -194
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,38 +1,32 @@
|
|
|
1
|
-
# @volr/react-ui
|
|
1
|
+
# @volr/react-ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Pre-built UI components for Volr SDK with minimal, modern design.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install @volr/react-ui @volr/react
|
|
9
|
-
#
|
|
9
|
+
# or
|
|
10
10
|
yarn add @volr/react-ui @volr/react
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Quick Start
|
|
14
14
|
|
|
15
|
-
### 1.
|
|
16
|
-
|
|
17
|
-
앱의 최상위 레벨에서 `VolrUIProvider`로 앱을 래핑합니다.
|
|
15
|
+
### 1. Wrap your app with VolrUIProvider
|
|
18
16
|
|
|
19
17
|
```tsx
|
|
20
18
|
import { VolrUIProvider } from '@volr/react-ui';
|
|
21
|
-
import { VolrConfig } from '@volr/react';
|
|
22
|
-
|
|
23
|
-
const volrConfig: VolrConfig = {
|
|
24
|
-
apiBaseUrl: 'https://api.volr.dev',
|
|
25
|
-
defaultChainId: 1,
|
|
26
|
-
projectApiKey: 'your-project-api-key',
|
|
27
|
-
};
|
|
28
19
|
|
|
29
20
|
function App() {
|
|
30
21
|
return (
|
|
31
22
|
<VolrUIProvider
|
|
32
|
-
config={
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
config={{
|
|
24
|
+
defaultChainId: 8453,
|
|
25
|
+
projectApiKey: 'your-project-api-key',
|
|
26
|
+
}}
|
|
27
|
+
accentColor="#3b82f6"
|
|
28
|
+
enabledLoginMethods={['email', 'social', 'siwe']}
|
|
29
|
+
socialProviders={['google', 'twitter', 'apple']}
|
|
36
30
|
>
|
|
37
31
|
<YourApp />
|
|
38
32
|
</VolrUIProvider>
|
|
@@ -40,9 +34,7 @@ function App() {
|
|
|
40
34
|
}
|
|
41
35
|
```
|
|
42
36
|
|
|
43
|
-
### 2. LoginModal
|
|
44
|
-
|
|
45
|
-
로그인 모달을 표시하려면 `LoginModal` 컴포넌트를 사용합니다.
|
|
37
|
+
### 2. Use LoginModal
|
|
46
38
|
|
|
47
39
|
```tsx
|
|
48
40
|
import { useState } from 'react';
|
|
@@ -53,14 +45,13 @@ function LoginButton() {
|
|
|
53
45
|
|
|
54
46
|
return (
|
|
55
47
|
<>
|
|
56
|
-
<button onClick={() => setIsOpen(true)}
|
|
48
|
+
<button onClick={() => setIsOpen(true)}>Login</button>
|
|
57
49
|
|
|
58
50
|
<LoginModal
|
|
59
51
|
isOpen={isOpen}
|
|
60
52
|
onClose={() => setIsOpen(false)}
|
|
61
53
|
onError={(error) => {
|
|
62
|
-
console.error('
|
|
63
|
-
// 에러 처리 로직
|
|
54
|
+
console.error('Login error:', error);
|
|
64
55
|
}}
|
|
65
56
|
/>
|
|
66
57
|
</>
|
|
@@ -68,52 +59,78 @@ function LoginButton() {
|
|
|
68
59
|
}
|
|
69
60
|
```
|
|
70
61
|
|
|
71
|
-
##
|
|
62
|
+
## Configuration
|
|
72
63
|
|
|
73
64
|
### VolrUIProvider Props
|
|
74
65
|
|
|
75
66
|
| Prop | Type | Default | Description |
|
|
76
67
|
|------|------|---------|-------------|
|
|
77
|
-
| `config` | `VolrConfig` |
|
|
78
|
-
| `accentColor` | `string` | `'#303030'` |
|
|
79
|
-
| `appName` | `string` | `undefined` |
|
|
80
|
-
| `enabledLoginMethods` | `('email' \| 'social' \| 'siwe')[]` | `['email', 'social', 'siwe']` |
|
|
81
|
-
| `socialProviders` | `('google' \| 'twitter' \| 'apple')[]` | `['google', 'twitter', 'apple']` |
|
|
82
|
-
| `volrLogoUrl` | `string` | `undefined` | Volr
|
|
83
|
-
| `providerPolicy` | `object` | `{ enforceOnFirstLogin: true }` | Provider
|
|
68
|
+
| `config` | `VolrConfig` | **Required** | Volr configuration (defaultChainId, projectApiKey) |
|
|
69
|
+
| `accentColor` | `string` | `'#303030'` | Accent color for buttons and links |
|
|
70
|
+
| `appName` | `string` | `undefined` | Application name |
|
|
71
|
+
| `enabledLoginMethods` | `('email' \| 'social' \| 'siwe')[]` | `['email', 'social', 'siwe']` | Enabled login methods |
|
|
72
|
+
| `socialProviders` | `('google' \| 'twitter' \| 'apple')[]` | `['google', 'twitter', 'apple']` | Enabled social providers |
|
|
73
|
+
| `volrLogoUrl` | `string` | `undefined` | Custom Volr logo URL |
|
|
74
|
+
| `providerPolicy` | `object` | `{ enforceOnFirstLogin: true }` | Provider policy settings |
|
|
84
75
|
|
|
85
76
|
### LoginModal Props
|
|
86
77
|
|
|
87
78
|
| Prop | Type | Default | Description |
|
|
88
79
|
|------|------|---------|-------------|
|
|
89
|
-
| `isOpen` | `boolean` |
|
|
90
|
-
| `onClose` | `() => void` |
|
|
91
|
-
| `onError` | `(error: Error) => void` | `undefined` |
|
|
92
|
-
|
|
93
|
-
##
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
80
|
+
| `isOpen` | `boolean` | **Required** | Modal open/close state |
|
|
81
|
+
| `onClose` | `() => void` | **Required** | Close callback |
|
|
82
|
+
| `onError` | `(error: Error) => void` | `undefined` | Error callback |
|
|
83
|
+
|
|
84
|
+
## Features
|
|
85
|
+
|
|
86
|
+
- **Email Login**: Email verification with 6-digit code
|
|
87
|
+
- **Social Login**: Google, Twitter, Apple OAuth
|
|
88
|
+
- **SIWE**: Sign-In with Ethereum (wallet login)
|
|
89
|
+
- **Passkey Wallet**: Automatic passkey-based wallet creation for new users
|
|
90
|
+
- **Minimal Design**: Clean, modern UI without emojis
|
|
91
|
+
- **Customizable**: Accent color and enabled methods
|
|
92
|
+
- **Type-Safe**: Full TypeScript support
|
|
93
|
+
|
|
94
|
+
## Login Flow
|
|
95
|
+
|
|
96
|
+
### Email Login
|
|
97
|
+
1. User clicks "Email Login"
|
|
98
|
+
2. Enter email address
|
|
99
|
+
3. Receive 6-digit verification code
|
|
100
|
+
4. Enter code
|
|
101
|
+
5. **New users**: Passkey setup
|
|
102
|
+
6. **Existing users**: Success
|
|
103
|
+
|
|
104
|
+
### Social Login (Google, Twitter, Apple)
|
|
105
|
+
1. User clicks social login button
|
|
106
|
+
2. Redirect to OAuth provider
|
|
107
|
+
3. Authorize and return
|
|
108
|
+
4. **New users**: Passkey setup
|
|
109
|
+
5. **Existing users**: Success
|
|
110
|
+
|
|
111
|
+
### Wallet Login (SIWE)
|
|
112
|
+
1. User clicks "Wallet Login"
|
|
113
|
+
2. Connect MetaMask or other wallet
|
|
114
|
+
3. Sign SIWE message
|
|
115
|
+
4. **New users**: Passkey setup
|
|
116
|
+
5. **Existing users**: Success
|
|
117
|
+
|
|
118
|
+
## Passkey Setup
|
|
119
|
+
|
|
120
|
+
For new users, a passkey-based wallet is automatically created:
|
|
121
|
+
|
|
122
|
+
1. **Create Passkey**: WebAuthn API with platform authenticator
|
|
123
|
+
2. **Derive Wrap Key**: From Passkey PRF
|
|
124
|
+
3. **Generate Master Seed**: 32-byte random seed
|
|
125
|
+
4. **Encrypt**: AES-256-GCM with Wrap Key
|
|
126
|
+
5. **Upload**: Encrypted seed to S3
|
|
127
|
+
6. **Register Provider**: Backend registration
|
|
128
|
+
7. **Derive EVM Key**: BIP-32 derivation
|
|
129
|
+
8. **Complete**: Wallet address confirmed
|
|
130
|
+
|
|
131
|
+
## Examples
|
|
132
|
+
|
|
133
|
+
### Email Only
|
|
117
134
|
|
|
118
135
|
```tsx
|
|
119
136
|
<VolrUIProvider
|
|
@@ -125,7 +142,7 @@ function App() {
|
|
|
125
142
|
</VolrUIProvider>
|
|
126
143
|
```
|
|
127
144
|
|
|
128
|
-
### Google
|
|
145
|
+
### Google and Apple Only
|
|
129
146
|
|
|
130
147
|
```tsx
|
|
131
148
|
<VolrUIProvider
|
|
@@ -138,7 +155,7 @@ function App() {
|
|
|
138
155
|
</VolrUIProvider>
|
|
139
156
|
```
|
|
140
157
|
|
|
141
|
-
###
|
|
158
|
+
### Custom Error Handling
|
|
142
159
|
|
|
143
160
|
```tsx
|
|
144
161
|
function App() {
|
|
@@ -159,7 +176,7 @@ function App() {
|
|
|
159
176
|
</div>
|
|
160
177
|
)}
|
|
161
178
|
|
|
162
|
-
<button onClick={() => setShowLogin(true)}
|
|
179
|
+
<button onClick={() => setShowLogin(true)}>Login</button>
|
|
163
180
|
|
|
164
181
|
<LoginModal
|
|
165
182
|
isOpen={showLogin}
|
|
@@ -169,7 +186,6 @@ function App() {
|
|
|
169
186
|
}}
|
|
170
187
|
onError={(err) => {
|
|
171
188
|
setError(err.message);
|
|
172
|
-
// 3초 후 에러 메시지 자동 숨김
|
|
173
189
|
setTimeout(() => setError(null), 3000);
|
|
174
190
|
}}
|
|
175
191
|
/>
|
|
@@ -178,57 +194,9 @@ function App() {
|
|
|
178
194
|
}
|
|
179
195
|
```
|
|
180
196
|
|
|
181
|
-
##
|
|
182
|
-
|
|
183
|
-
###
|
|
184
|
-
1. 사용자가 "Email Login" 버튼 클릭
|
|
185
|
-
2. 이메일 주소 입력 화면 표시
|
|
186
|
-
3. 인증 코드 전송
|
|
187
|
-
4. 6자리 코드 입력 화면 표시
|
|
188
|
-
5. 코드 검증
|
|
189
|
-
6. **신규 사용자**: Passkey 설정 화면으로 이동
|
|
190
|
-
7. **기존 사용자**: 성공 화면으로 이동
|
|
191
|
-
|
|
192
|
-
### 소셜 로그인 (Google, Twitter, Apple)
|
|
193
|
-
1. 사용자가 소셜 로그인 버튼 클릭
|
|
194
|
-
2. OAuth 페이지로 리다이렉트
|
|
195
|
-
3. 소셜 계정으로 로그인 및 권한 승인
|
|
196
|
-
4. 백엔드 콜백 처리 후 프론트엔드로 리다이렉트
|
|
197
|
-
5. **신규 사용자**: Passkey 설정 화면으로 이동
|
|
198
|
-
6. **기존 사용자**: 성공 화면으로 이동
|
|
199
|
-
|
|
200
|
-
### 지갑 로그인 (SIWE)
|
|
201
|
-
1. 사용자가 "Wallet Login" 버튼 클릭
|
|
202
|
-
2. MetaMask 또는 다른 지갑 연결
|
|
203
|
-
3. SIWE 메시지에 서명
|
|
204
|
-
4. 백엔드에서 서명 검증
|
|
205
|
-
5. **신규 사용자**: Passkey 설정 화면으로 이동
|
|
206
|
-
6. **기존 사용자**: 성공 화면으로 이동
|
|
207
|
-
|
|
208
|
-
## Passkey 설정
|
|
209
|
-
|
|
210
|
-
신규 사용자의 경우 자동으로 Passkey 지갑이 생성됩니다:
|
|
211
|
-
|
|
212
|
-
1. **Passkey 생성**: WebAuthn API를 사용하여 플랫폼 인증자(생체 인증) 생성
|
|
213
|
-
2. **Wrap Key 파생**: Passkey PRF에서 Wrap Key 생성
|
|
214
|
-
3. **Master Seed 생성 및 암호화**:
|
|
215
|
-
- 32바이트 무작위 Master Seed 생성
|
|
216
|
-
- Wrap Key로 AES-256-GCM 암호화
|
|
217
|
-
4. **S3 업로드**: 암호화된 Master Seed를 S3에 업로드
|
|
218
|
-
5. **Provider 등록**: 백엔드에 Provider 정보 등록
|
|
219
|
-
6. **EVM 키 파생**: Master Seed에서 BIP-32로 EVM 키 쌍 파생
|
|
220
|
-
7. **완료**: 지갑 주소 확인 및 로그인 완료
|
|
221
|
-
|
|
222
|
-
## 디자인 원칙
|
|
223
|
-
|
|
224
|
-
- **미니멀리즘**: 깔끔하고 모던한 디자인, 불필요한 장식 없음
|
|
225
|
-
- **accentColor 사용**: 버튼, 링크 등 강조 요소에 config의 accentColor 활용
|
|
226
|
-
- **이모지 금지**: 모든 UI 요소에서 이모지 사용 안 함, SVG 아이콘만 사용
|
|
227
|
-
- **일관된 여백과 타이포그래피**: 명확한 시각적 계층
|
|
228
|
-
|
|
229
|
-
## 백엔드 설정
|
|
230
|
-
|
|
231
|
-
로그인 기능을 사용하려면 백엔드에 다음 환경 변수를 설정해야 합니다:
|
|
197
|
+
## Backend Requirements
|
|
198
|
+
|
|
199
|
+
### Environment Variables
|
|
232
200
|
|
|
233
201
|
```bash
|
|
234
202
|
# Google OAuth
|
|
@@ -245,97 +213,51 @@ TWITTER_REDIRECT_URI=http://localhost:3000/auth/twitter/callback
|
|
|
245
213
|
APPLE_CLIENT_ID=your_apple_client_id
|
|
246
214
|
APPLE_REDIRECT_URI=http://localhost:3000/auth/apple/callback
|
|
247
215
|
|
|
248
|
-
# Frontend URL (OAuth
|
|
216
|
+
# Frontend URL (OAuth callback redirect)
|
|
249
217
|
FRONTEND_URL=http://localhost:5173
|
|
250
218
|
|
|
251
219
|
# API Base URL
|
|
252
220
|
API_BASE_URL=http://localhost:3000
|
|
253
221
|
```
|
|
254
222
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
백엔드에서 제공해야 하는 엔드포인트:
|
|
223
|
+
### Required Endpoints
|
|
258
224
|
|
|
259
|
-
|
|
260
|
-
- `POST /auth/email/send` -
|
|
261
|
-
- `POST /auth/email/verify` -
|
|
225
|
+
**Email Login**
|
|
226
|
+
- `POST /auth/email/send` - Send verification code
|
|
227
|
+
- `POST /auth/email/verify` - Verify code and login
|
|
262
228
|
|
|
263
|
-
|
|
264
|
-
- `GET /auth/google` - Google OAuth
|
|
265
|
-
- `GET /auth/google/callback` - Google OAuth
|
|
266
|
-
- `GET /auth/twitter` - Twitter OAuth
|
|
267
|
-
- `GET /auth/twitter/callback` - Twitter OAuth
|
|
268
|
-
- `GET /auth/apple` - Apple OAuth
|
|
269
|
-
- `POST /auth/apple/callback` - Apple OAuth
|
|
229
|
+
**Social Login**
|
|
230
|
+
- `GET /auth/google` - Start Google OAuth
|
|
231
|
+
- `GET /auth/google/callback` - Google OAuth callback
|
|
232
|
+
- `GET /auth/twitter` - Start Twitter OAuth
|
|
233
|
+
- `GET /auth/twitter/callback` - Twitter OAuth callback
|
|
234
|
+
- `GET /auth/apple` - Start Apple OAuth
|
|
235
|
+
- `POST /auth/apple/callback` - Apple OAuth callback
|
|
270
236
|
|
|
271
|
-
|
|
272
|
-
- `POST /auth/siwe/nonce` -
|
|
273
|
-
- `POST /auth/siwe/verify` - SIWE
|
|
237
|
+
**SIWE**
|
|
238
|
+
- `POST /auth/siwe/nonce` - Generate nonce
|
|
239
|
+
- `POST /auth/siwe/verify` - Verify SIWE signature
|
|
274
240
|
|
|
275
|
-
|
|
276
|
-
- `POST /blob/presign` - S3
|
|
277
|
-
- `POST /wallet/provider/register` -
|
|
241
|
+
**Passkey**
|
|
242
|
+
- `POST /blob/presign` - Generate S3 upload URL
|
|
243
|
+
- `POST /wallet/provider/register` - Register provider
|
|
278
244
|
|
|
279
|
-
##
|
|
245
|
+
## Troubleshooting
|
|
280
246
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const volrConfig: VolrConfig = {
|
|
287
|
-
apiBaseUrl: process.env.VITE_API_BASE_URL || 'http://localhost:3000',
|
|
288
|
-
defaultChainId: 1,
|
|
289
|
-
projectApiKey: process.env.VITE_PROJECT_API_KEY || '',
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
function LoginPage() {
|
|
293
|
-
const [isLoginOpen, setIsLoginOpen] = useState(false);
|
|
294
|
-
|
|
295
|
-
return (
|
|
296
|
-
<>
|
|
297
|
-
<button onClick={() => setIsLoginOpen(true)}>로그인</button>
|
|
298
|
-
|
|
299
|
-
<LoginModal
|
|
300
|
-
isOpen={isLoginOpen}
|
|
301
|
-
onClose={() => setIsLoginOpen(false)}
|
|
302
|
-
onError={(error) => {
|
|
303
|
-
console.error('로그인 오류:', error);
|
|
304
|
-
alert(error.message);
|
|
305
|
-
}}
|
|
306
|
-
/>
|
|
307
|
-
</>
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function App() {
|
|
312
|
-
return (
|
|
313
|
-
<VolrUIProvider
|
|
314
|
-
config={volrConfig}
|
|
315
|
-
accentColor="#6366f1"
|
|
316
|
-
enabledLoginMethods={['email', 'social', 'siwe']}
|
|
317
|
-
socialProviders={['google', 'twitter', 'apple']}
|
|
318
|
-
>
|
|
319
|
-
<LoginPage />
|
|
320
|
-
</VolrUIProvider>
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
export default App;
|
|
325
|
-
```
|
|
247
|
+
### OAuth Redirect Failure
|
|
248
|
+
- Verify OAuth Client ID and Secret
|
|
249
|
+
- Check Redirect URI matches OAuth app settings
|
|
250
|
+
- Verify CORS configuration
|
|
326
251
|
|
|
327
|
-
|
|
252
|
+
### SIWE Signature Failure
|
|
253
|
+
- Ensure MetaMask or wallet is installed
|
|
254
|
+
- Check wallet is connected to correct network
|
|
328
255
|
|
|
329
|
-
###
|
|
330
|
-
-
|
|
331
|
-
-
|
|
332
|
-
-
|
|
256
|
+
### Passkey Creation Failure
|
|
257
|
+
- HTTPS or localhost required (HTTP not supported)
|
|
258
|
+
- Verify browser supports WebAuthn
|
|
259
|
+
- Check biometric authentication is enabled on device
|
|
333
260
|
|
|
334
|
-
|
|
335
|
-
- MetaMask 또는 다른 지갑이 설치되어 있는지 확인
|
|
336
|
-
- 지갑이 올바른 네트워크에 연결되어 있는지 확인
|
|
261
|
+
## License
|
|
337
262
|
|
|
338
|
-
|
|
339
|
-
- HTTPS 또는 localhost에서만 작동 (HTTP는 불가)
|
|
340
|
-
- 브라우저가 WebAuthn을 지원하는지 확인
|
|
341
|
-
- 디바이스에 생체 인증이 활성화되어 있는지 확인
|
|
263
|
+
MIT
|