@mentaproject/signer-react-native 0.0.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 +343 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/signer.d.ts +161 -0
- package/dist/signer.d.ts.map +1 -0
- package/dist/signer.js +378 -0
- package/dist/signer.js.map +1 -0
- package/dist/types/index.d.ts +117 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/base64url.d.ts +75 -0
- package/dist/utils/base64url.d.ts.map +1 -0
- package/dist/utils/base64url.js +142 -0
- package/dist/utils/base64url.js.map +1 -0
- package/dist/utils/cose.d.ts +98 -0
- package/dist/utils/cose.d.ts.map +1 -0
- package/dist/utils/cose.js +259 -0
- package/dist/utils/cose.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/passkey.d.ts +108 -0
- package/dist/utils/passkey.d.ts.map +1 -0
- package/dist/utils/passkey.js +296 -0
- package/dist/utils/passkey.js.map +1 -0
- package/package.json +71 -0
- package/src/index.ts +59 -0
- package/src/signer.ts +499 -0
- package/src/types/index.ts +140 -0
- package/src/utils/base64url.ts +161 -0
- package/src/utils/cose.ts +356 -0
- package/src/utils/index.ts +40 -0
- package/src/utils/passkey.ts +392 -0
package/README.md
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# @mentaproject/signer-react-native
|
|
2
|
+
|
|
3
|
+
React Native Passkey signer for Menta wallet. Implements `SmartAccountSigner` using WebAuthn/Passkeys with P-256 (secp256r1) keys stored in the device's secure enclave.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @mentaproject/passkey-signer-react-native
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Peer Dependencies
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install react-native-passkey viem @mentaproject/core
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### iOS Setup
|
|
18
|
+
|
|
19
|
+
Add the Associated Domains capability to your app:
|
|
20
|
+
|
|
21
|
+
1. In Xcode, go to your target's "Signing & Capabilities"
|
|
22
|
+
2. Add "Associated Domains"
|
|
23
|
+
3. Add `webcredentials:yourdomain.com`
|
|
24
|
+
|
|
25
|
+
Create an `apple-app-site-association` file on your server at `https://yourdomain.com/.well-known/apple-app-site-association`:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"webcredentials": {
|
|
30
|
+
"apps": ["TEAMID.com.yourcompany.yourapp"]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Android Setup
|
|
36
|
+
|
|
37
|
+
Add to your `android/app/build.gradle`:
|
|
38
|
+
|
|
39
|
+
```gradle
|
|
40
|
+
android {
|
|
41
|
+
defaultConfig {
|
|
42
|
+
// ...
|
|
43
|
+
resValue "string", "host", "yourdomain.com"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Create a `assetlinks.json` file at `https://yourdomain.com/.well-known/assetlinks.json`:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
[{
|
|
52
|
+
"relation": ["delegate_permission/common.get_login_creds", "delegate_permission/common.handle_all_urls"],
|
|
53
|
+
"target": {
|
|
54
|
+
"namespace": "android_app",
|
|
55
|
+
"package_name": "com.yourcompany.yourapp",
|
|
56
|
+
"sha256_cert_fingerprints": ["YOUR_APP_FINGERPRINT"]
|
|
57
|
+
}
|
|
58
|
+
}]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Quick Start
|
|
62
|
+
|
|
63
|
+
### Register a New Passkey
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { ReactNativePasskeySigner } from '@mentaproject/signer-react-native';
|
|
67
|
+
|
|
68
|
+
// Create a new passkey (triggers biometric prompt)
|
|
69
|
+
const signer = await ReactNativePasskeySigner.register('user@example.com', {
|
|
70
|
+
rpId: 'yourdomain.com',
|
|
71
|
+
rpName: 'Your App Name',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Store the credential for later use
|
|
75
|
+
const credential = signer.getCredential();
|
|
76
|
+
await AsyncStorage.setItem('passkey', JSON.stringify(credential));
|
|
77
|
+
|
|
78
|
+
// Get the public key for smart account creation
|
|
79
|
+
const publicKeyX = signer.getPublicKeyX(); // 32 bytes hex
|
|
80
|
+
const publicKeyY = signer.getPublicKeyY(); // 32 bytes hex
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Restore from Stored Credential
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import {
|
|
87
|
+
ReactNativePasskeySigner,
|
|
88
|
+
deserializeCredential
|
|
89
|
+
} from '@mentaproject/signer-react-native';
|
|
90
|
+
|
|
91
|
+
// Load stored credential
|
|
92
|
+
const stored = await AsyncStorage.getItem('passkey');
|
|
93
|
+
const credential = deserializeCredential(JSON.parse(stored));
|
|
94
|
+
|
|
95
|
+
// Recreate the signer
|
|
96
|
+
const signer = ReactNativePasskeySigner.fromCredential(credential, {
|
|
97
|
+
rpId: 'yourdomain.com',
|
|
98
|
+
rpName: 'Your App Name',
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Sign a UserOperation
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// Sign a UserOpHash (triggers biometric prompt)
|
|
106
|
+
const signature = await signer.signMessage({
|
|
107
|
+
message: { raw: userOpHash }, // Hex string
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Use the signature with your smart account
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Sign Typed Data (EIP-712)
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const signature = await signer.signTypedData({
|
|
117
|
+
domain: {
|
|
118
|
+
name: 'Your App',
|
|
119
|
+
version: '1',
|
|
120
|
+
chainId: 1,
|
|
121
|
+
verifyingContract: '0x...',
|
|
122
|
+
},
|
|
123
|
+
types: {
|
|
124
|
+
Message: [
|
|
125
|
+
{ name: 'content', type: 'string' },
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
primaryType: 'Message',
|
|
129
|
+
message: {
|
|
130
|
+
content: 'Hello World',
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Signature Formats
|
|
136
|
+
|
|
137
|
+
The signer supports multiple signature encoding formats for different smart contract implementations:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// Default: Kernel/ZeroDev format
|
|
141
|
+
const signer = await ReactNativePasskeySigner.register(username, config);
|
|
142
|
+
|
|
143
|
+
// Change format for Rhinestone
|
|
144
|
+
const rhineSigner = signer.withSignatureFormat('rhinestone');
|
|
145
|
+
|
|
146
|
+
// Raw format (r || s only)
|
|
147
|
+
const rawSigner = signer.withSignatureFormat('raw');
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Kernel Format (Default)
|
|
151
|
+
|
|
152
|
+
```solidity
|
|
153
|
+
abi.encode(
|
|
154
|
+
bytes authenticatorData,
|
|
155
|
+
string clientDataJSON,
|
|
156
|
+
uint256 challengeIndex,
|
|
157
|
+
uint256 responseTypeIndex,
|
|
158
|
+
uint256 r,
|
|
159
|
+
uint256 s
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Rhinestone Format
|
|
164
|
+
|
|
165
|
+
```solidity
|
|
166
|
+
abi.encode(
|
|
167
|
+
bytes authenticatorData,
|
|
168
|
+
bytes clientDataJSON,
|
|
169
|
+
uint256[2] signature // [r, s]
|
|
170
|
+
)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## API Reference
|
|
174
|
+
|
|
175
|
+
### ReactNativePasskeySigner
|
|
176
|
+
|
|
177
|
+
#### Static Methods
|
|
178
|
+
|
|
179
|
+
| Method | Description |
|
|
180
|
+
|--------|-------------|
|
|
181
|
+
| `register(username, config)` | Creates a new passkey and returns a signer |
|
|
182
|
+
| `fromCredential(credential, config)` | Restores a signer from stored credential data |
|
|
183
|
+
|
|
184
|
+
#### Instance Properties
|
|
185
|
+
|
|
186
|
+
| Property | Type | Description |
|
|
187
|
+
|----------|------|-------------|
|
|
188
|
+
| `source` | `"passkey"` | Signer type identifier |
|
|
189
|
+
| `publicKey` | `Hex` | Uncompressed public key (65 bytes) |
|
|
190
|
+
| `credentialId` | `string` | Base64URL credential ID |
|
|
191
|
+
| `credentialIdHex` | `Hex` | Hex credential ID |
|
|
192
|
+
|
|
193
|
+
#### Instance Methods
|
|
194
|
+
|
|
195
|
+
| Method | Description |
|
|
196
|
+
|--------|-------------|
|
|
197
|
+
| `signMessage({ message })` | Signs a message (triggers biometric) |
|
|
198
|
+
| `signTypedData(typedData)` | Signs EIP-712 typed data |
|
|
199
|
+
| `getAddress()` | Returns derived address |
|
|
200
|
+
| `getPublicKeyX()` | Returns X coordinate (32 bytes) |
|
|
201
|
+
| `getPublicKeyY()` | Returns Y coordinate (32 bytes) |
|
|
202
|
+
| `getCredential()` | Returns credential for storage |
|
|
203
|
+
| `withSignatureFormat(format)` | Returns new signer with different format |
|
|
204
|
+
|
|
205
|
+
### Configuration
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
interface ReactNativePasskeySignerConfig {
|
|
209
|
+
// Required
|
|
210
|
+
rpId: string; // Relying Party ID (your domain)
|
|
211
|
+
rpName: string; // Display name for system UI
|
|
212
|
+
|
|
213
|
+
// Optional
|
|
214
|
+
timeout?: number; // Prompt timeout in ms (default: 60000)
|
|
215
|
+
userVerification?: 'required' | 'preferred' | 'discouraged';
|
|
216
|
+
signatureFormat?: 'kernel' | 'rhinestone' | 'raw';
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Credential Storage
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import {
|
|
224
|
+
serializeCredential,
|
|
225
|
+
deserializeCredential,
|
|
226
|
+
isPasskeyCredential
|
|
227
|
+
} from '@mentaproject/signer-react-native';
|
|
228
|
+
|
|
229
|
+
// Serialize for storage
|
|
230
|
+
const data = serializeCredential(signer.getCredential());
|
|
231
|
+
await SecureStorage.set('passkey', JSON.stringify(data));
|
|
232
|
+
|
|
233
|
+
// Deserialize on restore
|
|
234
|
+
const stored = JSON.parse(await SecureStorage.get('passkey'));
|
|
235
|
+
const credential = deserializeCredential(stored);
|
|
236
|
+
|
|
237
|
+
// Validate before use
|
|
238
|
+
if (isPasskeyCredential(credential)) {
|
|
239
|
+
const signer = ReactNativePasskeySigner.fromCredential(credential, config);
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Integration with Smart Accounts
|
|
244
|
+
|
|
245
|
+
### With Kernel (ZeroDev)
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { createKernelAccount } from '@zerodev/sdk';
|
|
249
|
+
import { ReactNativePasskeySigner } from '@mentaproject/signer-react-native';
|
|
250
|
+
|
|
251
|
+
const signer = await ReactNativePasskeySigner.register(username, {
|
|
252
|
+
rpId: 'yourdomain.com',
|
|
253
|
+
rpName: 'Your App',
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// The public key coordinates are needed for the WebAuthn validator
|
|
257
|
+
const webAuthnKey = {
|
|
258
|
+
pubKeyX: BigInt(signer.getPublicKeyX()),
|
|
259
|
+
pubKeyY: BigInt(signer.getPublicKeyY()),
|
|
260
|
+
authenticatorIdHash: keccak256(signer.credentialIdHex),
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// Create the kernel account with WebAuthn validator
|
|
264
|
+
const account = await createKernelAccount(publicClient, {
|
|
265
|
+
plugins: {
|
|
266
|
+
sudo: await signerToWebAuthnValidator(publicClient, {
|
|
267
|
+
webAuthnKey,
|
|
268
|
+
signer, // Pass the signer directly
|
|
269
|
+
}),
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### With permissionless.js
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
import { createSmartAccountClient } from 'permissionless';
|
|
278
|
+
|
|
279
|
+
const smartAccountClient = createSmartAccountClient({
|
|
280
|
+
account,
|
|
281
|
+
chain,
|
|
282
|
+
bundlerTransport: http(bundlerUrl),
|
|
283
|
+
// The signer's signMessage will be called for UserOp signing
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Utilities
|
|
288
|
+
|
|
289
|
+
For advanced use cases, low-level utilities are exported:
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import {
|
|
293
|
+
// COSE parsing
|
|
294
|
+
extractP256PublicKey,
|
|
295
|
+
extractPublicKeyFromAttestation,
|
|
296
|
+
|
|
297
|
+
// Signature handling
|
|
298
|
+
parseDERSignature,
|
|
299
|
+
normalizeSignatureS,
|
|
300
|
+
encodeWebAuthnSignature,
|
|
301
|
+
|
|
302
|
+
// Encoding
|
|
303
|
+
base64UrlToHex,
|
|
304
|
+
hexToBase64Url,
|
|
305
|
+
} from '@mentaproject/signer-react-native';
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Error Handling
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import {
|
|
312
|
+
PasskeySignerError,
|
|
313
|
+
COSEParseError,
|
|
314
|
+
SignatureError
|
|
315
|
+
} from '@mentaproject/signer-react-native';
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
const signer = await ReactNativePasskeySigner.register(username, config);
|
|
319
|
+
} catch (error) {
|
|
320
|
+
if (error instanceof PasskeySignerError) {
|
|
321
|
+
// User cancelled, biometric failed, or device not supported
|
|
322
|
+
console.error('Passkey error:', error.message);
|
|
323
|
+
console.error('Cause:', error.cause);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Security Considerations
|
|
329
|
+
|
|
330
|
+
1. **Secure Enclave**: Private keys never leave the device's secure enclave
|
|
331
|
+
2. **Biometric Required**: Each signature requires user authentication
|
|
332
|
+
3. **Low-S Normalization**: Signatures are normalized to prevent malleability
|
|
333
|
+
4. **Domain Binding**: Passkeys are bound to your domain (rpId)
|
|
334
|
+
|
|
335
|
+
## Requirements
|
|
336
|
+
|
|
337
|
+
- iOS 16+ or Android 9+
|
|
338
|
+
- React Native 0.70+
|
|
339
|
+
- Device with biometric authentication (Face ID, Touch ID, fingerprint)
|
|
340
|
+
|
|
341
|
+
## License
|
|
342
|
+
|
|
343
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mentaproject/signer-react-native
|
|
3
|
+
*
|
|
4
|
+
* React Native Passkey signer for Menta wallet.
|
|
5
|
+
* Implements SmartAccountSigner using WebAuthn/Passkeys with P-256 keys.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
export { ReactNativePasskeySigner, PasskeySignerError, isPasskeyCredential, serializeCredential, deserializeCredential, } from "./signer.js";
|
|
10
|
+
export type { SmartAccountSigner, SmartAccountSignerSource, SignableMessage, PasskeyConfig, P256PublicKey, PasskeyCredential, AuthenticatorData, WebAuthnSignature, SignatureEncodingFormat, ReactNativePasskeySignerConfig, } from "./types/index.js";
|
|
11
|
+
export { base64UrlToBytes, bytesToBase64Url, base64UrlToHex, hexToBase64Url, bytesToHex, hexToBytes, padHex, concatHex, extractP256PublicKey, extractPublicKeyFromAttestation, encodeUncompressedPublicKey, isValidP256PublicKey, COSEParseError, parseWebAuthnAssertion, encodeWebAuthnSignature, parseDERSignature, normalizeSignatureS, createChallenge, SignatureError, } from "./utils/index.js";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,EACf,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,uBAAuB,EACvB,8BAA8B,GAC/B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAEL,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,UAAU,EACV,UAAU,EACV,MAAM,EACN,SAAS,EAGT,oBAAoB,EACpB,+BAA+B,EAC/B,2BAA2B,EAC3B,oBAAoB,EACpB,cAAc,EAGd,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,cAAc,GACf,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mentaproject/signer-react-native
|
|
3
|
+
*
|
|
4
|
+
* React Native Passkey signer for Menta wallet.
|
|
5
|
+
* Implements SmartAccountSigner using WebAuthn/Passkeys with P-256 keys.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
// Main signer class
|
|
10
|
+
export { ReactNativePasskeySigner, PasskeySignerError, isPasskeyCredential, serializeCredential, deserializeCredential, } from "./signer.js";
|
|
11
|
+
// Utilities (for advanced use cases)
|
|
12
|
+
export {
|
|
13
|
+
// Base64URL utilities
|
|
14
|
+
base64UrlToBytes, bytesToBase64Url, base64UrlToHex, hexToBase64Url, bytesToHex, hexToBytes, padHex, concatHex,
|
|
15
|
+
// COSE parsing
|
|
16
|
+
extractP256PublicKey, extractPublicKeyFromAttestation, encodeUncompressedPublicKey, isValidP256PublicKey, COSEParseError,
|
|
17
|
+
// Signature utilities
|
|
18
|
+
parseWebAuthnAssertion, encodeWebAuthnSignature, parseDERSignature, normalizeSignatureS, createChallenge, SignatureError, } from "./utils/index.js";
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,oBAAoB;AACpB,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,aAAa,CAAC;AAgBrB,qCAAqC;AACrC,OAAO;AACL,sBAAsB;AACtB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,UAAU,EACV,UAAU,EACV,MAAM,EACN,SAAS;AAET,eAAe;AACf,oBAAoB,EACpB,+BAA+B,EAC/B,2BAA2B,EAC3B,oBAAoB,EACpB,cAAc;AAEd,sBAAsB;AACtB,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,EACf,cAAc,GACf,MAAM,kBAAkB,CAAC"}
|
package/dist/signer.d.ts
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { type Address, type Hex, type TypedData, type TypedDataDefinition } from "viem";
|
|
2
|
+
import type { SmartAccountSigner, PasskeyCredential, P256PublicKey, ReactNativePasskeySignerConfig, SignableMessage, SignatureEncodingFormat } from "./types/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Error thrown when passkey operations fail.
|
|
5
|
+
*/
|
|
6
|
+
export declare class PasskeySignerError extends Error {
|
|
7
|
+
readonly cause?: unknown | undefined;
|
|
8
|
+
constructor(message: string, cause?: unknown | undefined);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* ReactNativePasskeySigner implements SmartAccountSigner using WebAuthn/Passkeys.
|
|
12
|
+
*
|
|
13
|
+
* This signer uses the device's biometric authentication (Face ID, Touch ID, fingerprint)
|
|
14
|
+
* to sign messages and transactions for ERC-4337 smart accounts.
|
|
15
|
+
*
|
|
16
|
+
* The public key is a P-256 (secp256r1) key stored in the device's secure enclave.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // Register a new passkey
|
|
21
|
+
* const signer = await ReactNativePasskeySigner.register("user@example.com", {
|
|
22
|
+
* rpId: "example.com",
|
|
23
|
+
* rpName: "Example App",
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* // Sign a message (triggers biometric prompt)
|
|
27
|
+
* const signature = await signer.signMessage({
|
|
28
|
+
* message: { raw: userOpHash },
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare class ReactNativePasskeySigner implements SmartAccountSigner<"passkey"> {
|
|
33
|
+
readonly source: "passkey";
|
|
34
|
+
/**
|
|
35
|
+
* The credential ID in Base64URL format.
|
|
36
|
+
*/
|
|
37
|
+
readonly credentialId: string;
|
|
38
|
+
/**
|
|
39
|
+
* The credential ID as hex.
|
|
40
|
+
*/
|
|
41
|
+
readonly credentialIdHex: Hex;
|
|
42
|
+
/**
|
|
43
|
+
* The P-256 public key coordinates (x, y).
|
|
44
|
+
*/
|
|
45
|
+
readonly publicKeyCoordinates: P256PublicKey;
|
|
46
|
+
/**
|
|
47
|
+
* The encoded public key (uncompressed SEC1 format: 0x04 || x || y).
|
|
48
|
+
*/
|
|
49
|
+
readonly publicKey: Hex;
|
|
50
|
+
/**
|
|
51
|
+
* Configuration for passkey operations.
|
|
52
|
+
*/
|
|
53
|
+
private readonly config;
|
|
54
|
+
/**
|
|
55
|
+
* Creates a new ReactNativePasskeySigner instance.
|
|
56
|
+
* Use the static `register` or `fromCredential` methods instead of calling this directly.
|
|
57
|
+
*
|
|
58
|
+
* @param credential - The passkey credential data
|
|
59
|
+
* @param config - Configuration for passkey operations
|
|
60
|
+
*/
|
|
61
|
+
private constructor();
|
|
62
|
+
/**
|
|
63
|
+
* Registers a new passkey and returns a signer instance.
|
|
64
|
+
*
|
|
65
|
+
* This triggers the device's WebAuthn registration flow:
|
|
66
|
+
* 1. Shows a system prompt to create a new passkey
|
|
67
|
+
* 2. User authenticates with biometrics
|
|
68
|
+
* 3. Device generates a new P-256 key pair in the secure enclave
|
|
69
|
+
* 4. Returns the public key and credential ID
|
|
70
|
+
*
|
|
71
|
+
* @param username - The username for the passkey (displayed in system UI)
|
|
72
|
+
* @param config - Configuration for the passkey
|
|
73
|
+
* @returns A new ReactNativePasskeySigner instance
|
|
74
|
+
* @throws PasskeySignerError if registration fails
|
|
75
|
+
*/
|
|
76
|
+
static register(username: string, config: ReactNativePasskeySignerConfig): Promise<ReactNativePasskeySigner>;
|
|
77
|
+
/**
|
|
78
|
+
* Creates a signer from an existing credential.
|
|
79
|
+
*
|
|
80
|
+
* Use this when you have previously registered a passkey and stored the credential data.
|
|
81
|
+
*
|
|
82
|
+
* @param credential - The stored credential data
|
|
83
|
+
* @param config - Configuration for passkey operations
|
|
84
|
+
* @returns A new ReactNativePasskeySigner instance
|
|
85
|
+
*/
|
|
86
|
+
static fromCredential(credential: PasskeyCredential, config: ReactNativePasskeySignerConfig): ReactNativePasskeySigner;
|
|
87
|
+
/**
|
|
88
|
+
* Signs a message using the passkey.
|
|
89
|
+
*
|
|
90
|
+
* This triggers the device's biometric authentication prompt.
|
|
91
|
+
* The message can be a string or raw bytes (typically a UserOpHash).
|
|
92
|
+
*
|
|
93
|
+
* @param params - The message to sign
|
|
94
|
+
* @returns The encoded signature
|
|
95
|
+
* @throws PasskeySignerError if signing fails
|
|
96
|
+
*/
|
|
97
|
+
signMessage({ message, }: {
|
|
98
|
+
message: SignableMessage;
|
|
99
|
+
}): Promise<Hex>;
|
|
100
|
+
/**
|
|
101
|
+
* Signs typed data according to EIP-712.
|
|
102
|
+
*
|
|
103
|
+
* @param typedData - The typed data to sign
|
|
104
|
+
* @returns The encoded signature
|
|
105
|
+
* @throws PasskeySignerError if signing fails
|
|
106
|
+
*/
|
|
107
|
+
signTypedData<const TTypedData extends TypedData | Record<string, unknown>, TPrimaryType extends keyof TTypedData | "EIP712Domain" = keyof TTypedData>(typedData: TypedDataDefinition<TTypedData, TPrimaryType>): Promise<Hex>;
|
|
108
|
+
/**
|
|
109
|
+
* Returns the address derived from the public key.
|
|
110
|
+
*
|
|
111
|
+
* Note: P-256 keys don't directly map to Ethereum addresses.
|
|
112
|
+
* This returns a pseudo-address derived from the public key hash.
|
|
113
|
+
* The actual smart account address is determined by the account implementation.
|
|
114
|
+
*
|
|
115
|
+
* @returns A derived address (not a real EOA address)
|
|
116
|
+
*/
|
|
117
|
+
getAddress(): Promise<Address>;
|
|
118
|
+
/**
|
|
119
|
+
* Returns the X coordinate of the public key.
|
|
120
|
+
*/
|
|
121
|
+
getPublicKeyX(): Hex;
|
|
122
|
+
/**
|
|
123
|
+
* Returns the Y coordinate of the public key.
|
|
124
|
+
*/
|
|
125
|
+
getPublicKeyY(): Hex;
|
|
126
|
+
/**
|
|
127
|
+
* Returns the full credential data for storage/restoration.
|
|
128
|
+
*/
|
|
129
|
+
getCredential(): PasskeyCredential;
|
|
130
|
+
/**
|
|
131
|
+
* Sets the signature encoding format.
|
|
132
|
+
*
|
|
133
|
+
* @param format - The format to use for encoding signatures
|
|
134
|
+
* @returns A new signer instance with the updated format
|
|
135
|
+
*/
|
|
136
|
+
withSignatureFormat(format: SignatureEncodingFormat): ReactNativePasskeySigner;
|
|
137
|
+
/**
|
|
138
|
+
* Internal method to sign a challenge with the passkey.
|
|
139
|
+
*
|
|
140
|
+
* @param challenge - The Base64URL-encoded challenge
|
|
141
|
+
* @returns The encoded signature
|
|
142
|
+
*/
|
|
143
|
+
private signWithPasskey;
|
|
144
|
+
/**
|
|
145
|
+
* Converts a hex string to Uint8Array.
|
|
146
|
+
*/
|
|
147
|
+
private hexToBytes;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Type guard to check if a value is a valid PasskeyCredential.
|
|
151
|
+
*/
|
|
152
|
+
export declare function isPasskeyCredential(value: unknown): value is PasskeyCredential;
|
|
153
|
+
/**
|
|
154
|
+
* Serializes a PasskeyCredential to a JSON-safe object.
|
|
155
|
+
*/
|
|
156
|
+
export declare function serializeCredential(credential: PasskeyCredential): Record<string, string>;
|
|
157
|
+
/**
|
|
158
|
+
* Deserializes a PasskeyCredential from a JSON object.
|
|
159
|
+
*/
|
|
160
|
+
export declare function deserializeCredential(data: Record<string, string>): PasskeyCredential;
|
|
161
|
+
//# sourceMappingURL=signer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../src/signer.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,GAAG,EACR,KAAK,SAAS,EACd,KAAK,mBAAmB,EAGzB,MAAM,MAAM,CAAC;AAEd,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,8BAA8B,EAC9B,eAAe,EACf,uBAAuB,EACxB,MAAM,kBAAkB,CAAC;AAe1B;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;aAGzB,KAAK,CAAC,EAAE,OAAO;gBAD/B,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,OAAO,YAAA;CAKlC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,wBAAyB,YAAW,kBAAkB,CAAC,SAAS,CAAC;IAC5E,SAAgB,MAAM,EAAG,SAAS,CAAU;IAE5C;;OAEG;IACH,SAAgB,YAAY,EAAE,MAAM,CAAC;IAErC;;OAEG;IACH,SAAgB,eAAe,EAAE,GAAG,CAAC;IAErC;;OAEG;IACH,SAAgB,oBAAoB,EAAE,aAAa,CAAC;IAEpD;;OAEG;IACH,SAAgB,SAAS,EAAE,GAAG,CAAC;IAE/B;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;IAExD;;;;;;OAMG;IACH,OAAO;IAgBP;;;;;;;;;;;;;OAaG;WACiB,QAAQ,CAC1B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,8BAA8B,GACrC,OAAO,CAAC,wBAAwB,CAAC;IA2FpC;;;;;;;;OAQG;WACW,cAAc,CAC1B,UAAU,EAAE,iBAAiB,EAC7B,MAAM,EAAE,8BAA8B,GACrC,wBAAwB;IAQ3B;;;;;;;;;OASG;IACU,WAAW,CAAC,EACvB,OAAO,GACR,EAAE;QACD,OAAO,EAAE,eAAe,CAAC;KAC1B,GAAG,OAAO,CAAC,GAAG,CAAC;IAmChB;;;;;;OAMG;IACU,aAAa,CACxB,KAAK,CAAC,UAAU,SAAS,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5D,YAAY,SAAS,MAAM,UAAU,GAAG,cAAc,GAAG,MAAM,UAAU,EACzE,SAAS,EAAE,mBAAmB,CAAC,UAAU,EAAE,YAAY,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IAWzE;;;;;;;;OAQG;IACU,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAQ3C;;OAEG;IACI,aAAa,IAAI,GAAG;IAI3B;;OAEG;IACI,aAAa,IAAI,GAAG;IAI3B;;OAEG;IACI,aAAa,IAAI,iBAAiB;IASzC;;;;;OAKG;IACI,mBAAmB,CACxB,MAAM,EAAE,uBAAuB,GAC9B,wBAAwB;IAO3B;;;;;OAKG;YACW,eAAe;IAwC7B;;OAEG;IACH,OAAO,CAAC,UAAU;CAQnB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,iBAAiB,CAkB5B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,iBAAiB,GAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC3B,iBAAiB,CAoBnB"}
|