@phantom/react-native-sdk 0.1.6 → 0.1.7
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/dist/index.js +96 -50
- package/dist/index.mjs +96 -50
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -295,7 +295,8 @@ var import_sdk_types = require("@phantom/sdk-types");
|
|
|
295
295
|
var ReactNativeStamper = class {
|
|
296
296
|
// Optional for PKI, required for OIDC
|
|
297
297
|
constructor(config = {}) {
|
|
298
|
-
this.
|
|
298
|
+
this.activeKeyRecord = null;
|
|
299
|
+
this.pendingKeyRecord = null;
|
|
299
300
|
this.algorithm = import_sdk_types.Algorithm.ed25519;
|
|
300
301
|
this.type = "PKI";
|
|
301
302
|
this.keyPrefix = config.keyPrefix || "phantom-rn-stamper";
|
|
@@ -305,32 +306,27 @@ var ReactNativeStamper = class {
|
|
|
305
306
|
* Initialize the stamper and generate/load cryptographic keys
|
|
306
307
|
*/
|
|
307
308
|
async init() {
|
|
308
|
-
|
|
309
|
-
if (
|
|
310
|
-
|
|
311
|
-
if (keyInfo2) {
|
|
312
|
-
this.keyInfo = keyInfo2;
|
|
313
|
-
return keyInfo2;
|
|
314
|
-
}
|
|
309
|
+
this.activeKeyRecord = await this.loadActiveKeyRecord();
|
|
310
|
+
if (!this.activeKeyRecord) {
|
|
311
|
+
this.activeKeyRecord = await this.generateAndStoreNewKeyRecord("active");
|
|
315
312
|
}
|
|
316
|
-
|
|
317
|
-
this.keyInfo
|
|
318
|
-
return keyInfo;
|
|
313
|
+
this.pendingKeyRecord = await this.loadPendingKeyRecord();
|
|
314
|
+
return this.activeKeyRecord.keyInfo;
|
|
319
315
|
}
|
|
320
316
|
/**
|
|
321
317
|
* Get the current key information
|
|
322
318
|
*/
|
|
323
319
|
getKeyInfo() {
|
|
324
|
-
return this.keyInfo;
|
|
320
|
+
return this.activeKeyRecord?.keyInfo || null;
|
|
325
321
|
}
|
|
326
322
|
/**
|
|
327
323
|
* Generate and store a new key pair, replacing any existing keys
|
|
328
324
|
*/
|
|
329
325
|
async resetKeyPair() {
|
|
330
326
|
await this.clear();
|
|
331
|
-
|
|
332
|
-
this.
|
|
333
|
-
return keyInfo;
|
|
327
|
+
this.activeKeyRecord = await this.generateAndStoreNewKeyRecord("active");
|
|
328
|
+
this.pendingKeyRecord = null;
|
|
329
|
+
return this.activeKeyRecord.keyInfo;
|
|
334
330
|
}
|
|
335
331
|
/**
|
|
336
332
|
* Create X-Phantom-Stamp header value using stored secret key
|
|
@@ -338,79 +334,129 @@ var ReactNativeStamper = class {
|
|
|
338
334
|
* @returns Complete X-Phantom-Stamp header value
|
|
339
335
|
*/
|
|
340
336
|
async stamp(params) {
|
|
341
|
-
if (!this.
|
|
337
|
+
if (!this.activeKeyRecord) {
|
|
342
338
|
throw new Error("Stamper not initialized. Call init() first.");
|
|
343
339
|
}
|
|
344
|
-
const
|
|
345
|
-
if (!storedSecretKey) {
|
|
346
|
-
throw new Error("Secret key not found in secure storage");
|
|
347
|
-
}
|
|
348
|
-
const apiKeyStamper = new import_api_key_stamper.ApiKeyStamper({ apiSecretKey: storedSecretKey });
|
|
340
|
+
const apiKeyStamper = new import_api_key_stamper.ApiKeyStamper({ apiSecretKey: this.activeKeyRecord.secretKey });
|
|
349
341
|
return await apiKeyStamper.stamp(params);
|
|
350
342
|
}
|
|
351
343
|
/**
|
|
352
344
|
* Clear all stored keys from SecureStore
|
|
353
345
|
*/
|
|
354
346
|
async clear() {
|
|
355
|
-
const
|
|
356
|
-
const
|
|
347
|
+
const activeKey = this.getActiveKeyName();
|
|
348
|
+
const pendingKey = this.getPendingKeyName();
|
|
349
|
+
try {
|
|
350
|
+
await SecureStore2.deleteItemAsync(activeKey);
|
|
351
|
+
} catch (error) {
|
|
352
|
+
}
|
|
357
353
|
try {
|
|
358
|
-
await SecureStore2.deleteItemAsync(
|
|
354
|
+
await SecureStore2.deleteItemAsync(pendingKey);
|
|
359
355
|
} catch (error) {
|
|
360
356
|
}
|
|
357
|
+
this.activeKeyRecord = null;
|
|
358
|
+
this.pendingKeyRecord = null;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Generate a new keypair for rotation without making it active
|
|
362
|
+
*/
|
|
363
|
+
async rotateKeyPair() {
|
|
364
|
+
this.pendingKeyRecord = await this.generateAndStoreNewKeyRecord("pending");
|
|
365
|
+
return this.pendingKeyRecord.keyInfo;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Switch to the pending keypair, making it active and cleaning up the old one
|
|
369
|
+
*/
|
|
370
|
+
async commitRotation(authenticatorId) {
|
|
371
|
+
if (!this.pendingKeyRecord) {
|
|
372
|
+
throw new Error("No pending keypair to commit");
|
|
373
|
+
}
|
|
374
|
+
if (this.activeKeyRecord) {
|
|
375
|
+
try {
|
|
376
|
+
await SecureStore2.deleteItemAsync(this.getActiveKeyName());
|
|
377
|
+
} catch (error) {
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
this.pendingKeyRecord.status = "active";
|
|
381
|
+
this.pendingKeyRecord.authenticatorId = authenticatorId;
|
|
382
|
+
this.pendingKeyRecord.keyInfo.authenticatorId = authenticatorId;
|
|
383
|
+
this.activeKeyRecord = this.pendingKeyRecord;
|
|
384
|
+
this.pendingKeyRecord = null;
|
|
385
|
+
await this.storeKeyRecord(this.activeKeyRecord, "active");
|
|
361
386
|
try {
|
|
362
|
-
await SecureStore2.deleteItemAsync(
|
|
387
|
+
await SecureStore2.deleteItemAsync(this.getPendingKeyName());
|
|
363
388
|
} catch (error) {
|
|
364
389
|
}
|
|
365
|
-
this.keyInfo = null;
|
|
366
390
|
}
|
|
367
|
-
|
|
391
|
+
/**
|
|
392
|
+
* Discard the pending keypair on rotation failure
|
|
393
|
+
*/
|
|
394
|
+
async rollbackRotation() {
|
|
395
|
+
if (!this.pendingKeyRecord) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
try {
|
|
399
|
+
await SecureStore2.deleteItemAsync(this.getPendingKeyName());
|
|
400
|
+
} catch (error) {
|
|
401
|
+
}
|
|
402
|
+
this.pendingKeyRecord = null;
|
|
403
|
+
}
|
|
404
|
+
async generateAndStoreNewKeyRecord(type) {
|
|
368
405
|
const keypair = (0, import_crypto.generateKeyPair)();
|
|
369
406
|
const keyId = this.createKeyId(keypair.publicKey);
|
|
407
|
+
const now = Date.now();
|
|
370
408
|
const keyInfo = {
|
|
371
409
|
keyId,
|
|
372
|
-
publicKey: keypair.publicKey
|
|
410
|
+
publicKey: keypair.publicKey,
|
|
411
|
+
createdAt: now
|
|
412
|
+
};
|
|
413
|
+
const record = {
|
|
414
|
+
keyInfo,
|
|
415
|
+
secretKey: keypair.secretKey,
|
|
416
|
+
createdAt: now,
|
|
417
|
+
expiresAt: 0,
|
|
418
|
+
// Not used anymore, kept for backward compatibility
|
|
419
|
+
status: type
|
|
373
420
|
};
|
|
374
|
-
await this.
|
|
375
|
-
return
|
|
421
|
+
await this.storeKeyRecord(record, type);
|
|
422
|
+
return record;
|
|
376
423
|
}
|
|
377
424
|
createKeyId(publicKey) {
|
|
378
425
|
return (0, import_base64url.base64urlEncode)(new TextEncoder().encode(publicKey)).substring(0, 16);
|
|
379
426
|
}
|
|
380
|
-
async
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
await SecureStore2.setItemAsync(infoKey, JSON.stringify(keyInfo), {
|
|
384
|
-
requireAuthentication: false
|
|
385
|
-
});
|
|
386
|
-
await SecureStore2.setItemAsync(secretKeyName, secretKey, {
|
|
427
|
+
async storeKeyRecord(record, type) {
|
|
428
|
+
const keyName = type === "active" ? this.getActiveKeyName() : this.getPendingKeyName();
|
|
429
|
+
await SecureStore2.setItemAsync(keyName, JSON.stringify(record), {
|
|
387
430
|
requireAuthentication: false
|
|
388
431
|
});
|
|
389
432
|
}
|
|
390
|
-
async
|
|
433
|
+
async loadActiveKeyRecord() {
|
|
391
434
|
try {
|
|
392
|
-
const
|
|
393
|
-
const
|
|
394
|
-
if (
|
|
395
|
-
return JSON.parse(
|
|
435
|
+
const activeKey = this.getActiveKeyName();
|
|
436
|
+
const storedRecord = await SecureStore2.getItemAsync(activeKey);
|
|
437
|
+
if (storedRecord) {
|
|
438
|
+
return JSON.parse(storedRecord);
|
|
396
439
|
}
|
|
397
440
|
} catch (error) {
|
|
398
441
|
}
|
|
399
442
|
return null;
|
|
400
443
|
}
|
|
401
|
-
async
|
|
444
|
+
async loadPendingKeyRecord() {
|
|
402
445
|
try {
|
|
403
|
-
const
|
|
404
|
-
|
|
446
|
+
const pendingKey = this.getPendingKeyName();
|
|
447
|
+
const storedRecord = await SecureStore2.getItemAsync(pendingKey);
|
|
448
|
+
if (storedRecord) {
|
|
449
|
+
return JSON.parse(storedRecord);
|
|
450
|
+
}
|
|
405
451
|
} catch (error) {
|
|
406
|
-
return null;
|
|
407
452
|
}
|
|
453
|
+
return null;
|
|
408
454
|
}
|
|
409
|
-
|
|
410
|
-
return `${this.keyPrefix}-${this.organizationId}-
|
|
455
|
+
getActiveKeyName() {
|
|
456
|
+
return `${this.keyPrefix}-${this.organizationId}-active`;
|
|
411
457
|
}
|
|
412
|
-
|
|
413
|
-
return `${this.keyPrefix}-${this.organizationId}-
|
|
458
|
+
getPendingKeyName() {
|
|
459
|
+
return `${this.keyPrefix}-${this.organizationId}-pending`;
|
|
414
460
|
}
|
|
415
461
|
};
|
|
416
462
|
|
package/dist/index.mjs
CHANGED
|
@@ -251,7 +251,8 @@ import { Algorithm } from "@phantom/sdk-types";
|
|
|
251
251
|
var ReactNativeStamper = class {
|
|
252
252
|
// Optional for PKI, required for OIDC
|
|
253
253
|
constructor(config = {}) {
|
|
254
|
-
this.
|
|
254
|
+
this.activeKeyRecord = null;
|
|
255
|
+
this.pendingKeyRecord = null;
|
|
255
256
|
this.algorithm = Algorithm.ed25519;
|
|
256
257
|
this.type = "PKI";
|
|
257
258
|
this.keyPrefix = config.keyPrefix || "phantom-rn-stamper";
|
|
@@ -261,32 +262,27 @@ var ReactNativeStamper = class {
|
|
|
261
262
|
* Initialize the stamper and generate/load cryptographic keys
|
|
262
263
|
*/
|
|
263
264
|
async init() {
|
|
264
|
-
|
|
265
|
-
if (
|
|
266
|
-
|
|
267
|
-
if (keyInfo2) {
|
|
268
|
-
this.keyInfo = keyInfo2;
|
|
269
|
-
return keyInfo2;
|
|
270
|
-
}
|
|
265
|
+
this.activeKeyRecord = await this.loadActiveKeyRecord();
|
|
266
|
+
if (!this.activeKeyRecord) {
|
|
267
|
+
this.activeKeyRecord = await this.generateAndStoreNewKeyRecord("active");
|
|
271
268
|
}
|
|
272
|
-
|
|
273
|
-
this.keyInfo
|
|
274
|
-
return keyInfo;
|
|
269
|
+
this.pendingKeyRecord = await this.loadPendingKeyRecord();
|
|
270
|
+
return this.activeKeyRecord.keyInfo;
|
|
275
271
|
}
|
|
276
272
|
/**
|
|
277
273
|
* Get the current key information
|
|
278
274
|
*/
|
|
279
275
|
getKeyInfo() {
|
|
280
|
-
return this.keyInfo;
|
|
276
|
+
return this.activeKeyRecord?.keyInfo || null;
|
|
281
277
|
}
|
|
282
278
|
/**
|
|
283
279
|
* Generate and store a new key pair, replacing any existing keys
|
|
284
280
|
*/
|
|
285
281
|
async resetKeyPair() {
|
|
286
282
|
await this.clear();
|
|
287
|
-
|
|
288
|
-
this.
|
|
289
|
-
return keyInfo;
|
|
283
|
+
this.activeKeyRecord = await this.generateAndStoreNewKeyRecord("active");
|
|
284
|
+
this.pendingKeyRecord = null;
|
|
285
|
+
return this.activeKeyRecord.keyInfo;
|
|
290
286
|
}
|
|
291
287
|
/**
|
|
292
288
|
* Create X-Phantom-Stamp header value using stored secret key
|
|
@@ -294,79 +290,129 @@ var ReactNativeStamper = class {
|
|
|
294
290
|
* @returns Complete X-Phantom-Stamp header value
|
|
295
291
|
*/
|
|
296
292
|
async stamp(params) {
|
|
297
|
-
if (!this.
|
|
293
|
+
if (!this.activeKeyRecord) {
|
|
298
294
|
throw new Error("Stamper not initialized. Call init() first.");
|
|
299
295
|
}
|
|
300
|
-
const
|
|
301
|
-
if (!storedSecretKey) {
|
|
302
|
-
throw new Error("Secret key not found in secure storage");
|
|
303
|
-
}
|
|
304
|
-
const apiKeyStamper = new ApiKeyStamper({ apiSecretKey: storedSecretKey });
|
|
296
|
+
const apiKeyStamper = new ApiKeyStamper({ apiSecretKey: this.activeKeyRecord.secretKey });
|
|
305
297
|
return await apiKeyStamper.stamp(params);
|
|
306
298
|
}
|
|
307
299
|
/**
|
|
308
300
|
* Clear all stored keys from SecureStore
|
|
309
301
|
*/
|
|
310
302
|
async clear() {
|
|
311
|
-
const
|
|
312
|
-
const
|
|
303
|
+
const activeKey = this.getActiveKeyName();
|
|
304
|
+
const pendingKey = this.getPendingKeyName();
|
|
305
|
+
try {
|
|
306
|
+
await SecureStore2.deleteItemAsync(activeKey);
|
|
307
|
+
} catch (error) {
|
|
308
|
+
}
|
|
313
309
|
try {
|
|
314
|
-
await SecureStore2.deleteItemAsync(
|
|
310
|
+
await SecureStore2.deleteItemAsync(pendingKey);
|
|
315
311
|
} catch (error) {
|
|
316
312
|
}
|
|
313
|
+
this.activeKeyRecord = null;
|
|
314
|
+
this.pendingKeyRecord = null;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Generate a new keypair for rotation without making it active
|
|
318
|
+
*/
|
|
319
|
+
async rotateKeyPair() {
|
|
320
|
+
this.pendingKeyRecord = await this.generateAndStoreNewKeyRecord("pending");
|
|
321
|
+
return this.pendingKeyRecord.keyInfo;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Switch to the pending keypair, making it active and cleaning up the old one
|
|
325
|
+
*/
|
|
326
|
+
async commitRotation(authenticatorId) {
|
|
327
|
+
if (!this.pendingKeyRecord) {
|
|
328
|
+
throw new Error("No pending keypair to commit");
|
|
329
|
+
}
|
|
330
|
+
if (this.activeKeyRecord) {
|
|
331
|
+
try {
|
|
332
|
+
await SecureStore2.deleteItemAsync(this.getActiveKeyName());
|
|
333
|
+
} catch (error) {
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
this.pendingKeyRecord.status = "active";
|
|
337
|
+
this.pendingKeyRecord.authenticatorId = authenticatorId;
|
|
338
|
+
this.pendingKeyRecord.keyInfo.authenticatorId = authenticatorId;
|
|
339
|
+
this.activeKeyRecord = this.pendingKeyRecord;
|
|
340
|
+
this.pendingKeyRecord = null;
|
|
341
|
+
await this.storeKeyRecord(this.activeKeyRecord, "active");
|
|
317
342
|
try {
|
|
318
|
-
await SecureStore2.deleteItemAsync(
|
|
343
|
+
await SecureStore2.deleteItemAsync(this.getPendingKeyName());
|
|
319
344
|
} catch (error) {
|
|
320
345
|
}
|
|
321
|
-
this.keyInfo = null;
|
|
322
346
|
}
|
|
323
|
-
|
|
347
|
+
/**
|
|
348
|
+
* Discard the pending keypair on rotation failure
|
|
349
|
+
*/
|
|
350
|
+
async rollbackRotation() {
|
|
351
|
+
if (!this.pendingKeyRecord) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
try {
|
|
355
|
+
await SecureStore2.deleteItemAsync(this.getPendingKeyName());
|
|
356
|
+
} catch (error) {
|
|
357
|
+
}
|
|
358
|
+
this.pendingKeyRecord = null;
|
|
359
|
+
}
|
|
360
|
+
async generateAndStoreNewKeyRecord(type) {
|
|
324
361
|
const keypair = generateKeyPair();
|
|
325
362
|
const keyId = this.createKeyId(keypair.publicKey);
|
|
363
|
+
const now = Date.now();
|
|
326
364
|
const keyInfo = {
|
|
327
365
|
keyId,
|
|
328
|
-
publicKey: keypair.publicKey
|
|
366
|
+
publicKey: keypair.publicKey,
|
|
367
|
+
createdAt: now
|
|
368
|
+
};
|
|
369
|
+
const record = {
|
|
370
|
+
keyInfo,
|
|
371
|
+
secretKey: keypair.secretKey,
|
|
372
|
+
createdAt: now,
|
|
373
|
+
expiresAt: 0,
|
|
374
|
+
// Not used anymore, kept for backward compatibility
|
|
375
|
+
status: type
|
|
329
376
|
};
|
|
330
|
-
await this.
|
|
331
|
-
return
|
|
377
|
+
await this.storeKeyRecord(record, type);
|
|
378
|
+
return record;
|
|
332
379
|
}
|
|
333
380
|
createKeyId(publicKey) {
|
|
334
381
|
return base64urlEncode(new TextEncoder().encode(publicKey)).substring(0, 16);
|
|
335
382
|
}
|
|
336
|
-
async
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
await SecureStore2.setItemAsync(infoKey, JSON.stringify(keyInfo), {
|
|
340
|
-
requireAuthentication: false
|
|
341
|
-
});
|
|
342
|
-
await SecureStore2.setItemAsync(secretKeyName, secretKey, {
|
|
383
|
+
async storeKeyRecord(record, type) {
|
|
384
|
+
const keyName = type === "active" ? this.getActiveKeyName() : this.getPendingKeyName();
|
|
385
|
+
await SecureStore2.setItemAsync(keyName, JSON.stringify(record), {
|
|
343
386
|
requireAuthentication: false
|
|
344
387
|
});
|
|
345
388
|
}
|
|
346
|
-
async
|
|
389
|
+
async loadActiveKeyRecord() {
|
|
347
390
|
try {
|
|
348
|
-
const
|
|
349
|
-
const
|
|
350
|
-
if (
|
|
351
|
-
return JSON.parse(
|
|
391
|
+
const activeKey = this.getActiveKeyName();
|
|
392
|
+
const storedRecord = await SecureStore2.getItemAsync(activeKey);
|
|
393
|
+
if (storedRecord) {
|
|
394
|
+
return JSON.parse(storedRecord);
|
|
352
395
|
}
|
|
353
396
|
} catch (error) {
|
|
354
397
|
}
|
|
355
398
|
return null;
|
|
356
399
|
}
|
|
357
|
-
async
|
|
400
|
+
async loadPendingKeyRecord() {
|
|
358
401
|
try {
|
|
359
|
-
const
|
|
360
|
-
|
|
402
|
+
const pendingKey = this.getPendingKeyName();
|
|
403
|
+
const storedRecord = await SecureStore2.getItemAsync(pendingKey);
|
|
404
|
+
if (storedRecord) {
|
|
405
|
+
return JSON.parse(storedRecord);
|
|
406
|
+
}
|
|
361
407
|
} catch (error) {
|
|
362
|
-
return null;
|
|
363
408
|
}
|
|
409
|
+
return null;
|
|
364
410
|
}
|
|
365
|
-
|
|
366
|
-
return `${this.keyPrefix}-${this.organizationId}-
|
|
411
|
+
getActiveKeyName() {
|
|
412
|
+
return `${this.keyPrefix}-${this.organizationId}-active`;
|
|
367
413
|
}
|
|
368
|
-
|
|
369
|
-
return `${this.keyPrefix}-${this.organizationId}-
|
|
414
|
+
getPendingKeyName() {
|
|
415
|
+
return `${this.keyPrefix}-${this.organizationId}-pending`;
|
|
370
416
|
}
|
|
371
417
|
};
|
|
372
418
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phantom/react-native-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Phantom Wallet SDK for React Native and Expo applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -47,11 +47,11 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@phantom/api-key-stamper": "^0.1.5",
|
|
49
49
|
"@phantom/base64url": "^0.1.0",
|
|
50
|
-
"@phantom/client": "^0.1.
|
|
50
|
+
"@phantom/client": "^0.1.10",
|
|
51
51
|
"@phantom/constants": "^0.0.3",
|
|
52
52
|
"@phantom/crypto": "^0.1.2",
|
|
53
|
-
"@phantom/embedded-provider-core": "^0.1.
|
|
54
|
-
"@phantom/sdk-types": "^0.1.
|
|
53
|
+
"@phantom/embedded-provider-core": "^0.1.9",
|
|
54
|
+
"@phantom/sdk-types": "^0.1.5",
|
|
55
55
|
"@types/bs58": "^5.0.0",
|
|
56
56
|
"bs58": "^6.0.0",
|
|
57
57
|
"buffer": "^6.0.3"
|