@phantom/react-native-sdk 1.0.6 → 2.0.0-beta.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/README.md +37 -0
- package/dist/index.d.ts +4 -6
- package/dist/index.js +109 -546
- package/dist/index.mjs +100 -538
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -31,8 +31,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
AddressType: () => import_client.AddressType,
|
|
34
|
-
NetworkId: () =>
|
|
34
|
+
NetworkId: () => import_constants2.NetworkId,
|
|
35
35
|
PhantomProvider: () => PhantomProvider,
|
|
36
|
+
base64urlDecode: () => import_base64url.base64urlDecode,
|
|
37
|
+
base64urlEncode: () => import_base64url.base64urlEncode,
|
|
36
38
|
darkTheme: () => import_wallet_sdk_ui6.darkTheme,
|
|
37
39
|
lightTheme: () => import_wallet_sdk_ui6.lightTheme,
|
|
38
40
|
useAccounts: () => useAccounts,
|
|
@@ -48,7 +50,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
48
50
|
// src/PhantomProvider.tsx
|
|
49
51
|
var import_react8 = require("react");
|
|
50
52
|
var import_embedded_provider_core = require("@phantom/embedded-provider-core");
|
|
51
|
-
var
|
|
53
|
+
var import_constants = require("@phantom/constants");
|
|
52
54
|
var import_wallet_sdk_ui5 = require("@phantom/wallet-sdk-ui");
|
|
53
55
|
|
|
54
56
|
// src/ModalProvider.tsx
|
|
@@ -530,110 +532,8 @@ var ExpoSecureStorage = class {
|
|
|
530
532
|
}
|
|
531
533
|
};
|
|
532
534
|
|
|
533
|
-
// src/providers/embedded/auth.ts
|
|
534
|
-
var WebBrowser = __toESM(require("expo-web-browser"));
|
|
535
|
-
var import_react_native5 = require("react-native");
|
|
536
|
-
var import_constants = require("@phantom/constants");
|
|
537
|
-
var ExpoAuthProvider = class {
|
|
538
|
-
async authenticate(options) {
|
|
539
|
-
if ("jwtToken" in options) {
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
const phantomOptions = options;
|
|
543
|
-
const { authUrl, redirectUrl, publicKey, sessionId, provider, appId } = phantomOptions;
|
|
544
|
-
if (!redirectUrl) {
|
|
545
|
-
throw new Error("redirectUrl is required for web browser authentication");
|
|
546
|
-
}
|
|
547
|
-
if (!publicKey || !sessionId || !appId) {
|
|
548
|
-
throw new Error("publicKey, sessionId and appId are required for authentication");
|
|
549
|
-
}
|
|
550
|
-
try {
|
|
551
|
-
const baseUrl = authUrl || import_constants.DEFAULT_AUTH_URL;
|
|
552
|
-
const params = new URLSearchParams({
|
|
553
|
-
public_key: publicKey,
|
|
554
|
-
app_id: appId,
|
|
555
|
-
redirect_uri: redirectUrl,
|
|
556
|
-
session_id: sessionId,
|
|
557
|
-
// OAuth session management - defaults to allow refresh unless explicitly clearing after logout
|
|
558
|
-
clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
|
|
559
|
-
allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
|
|
560
|
-
sdk_version: "1.0.6",
|
|
561
|
-
sdk_type: "react-native",
|
|
562
|
-
platform: import_react_native5.Platform.OS
|
|
563
|
-
});
|
|
564
|
-
if (provider) {
|
|
565
|
-
console.log("[ExpoAuthProvider] Provider specified, will skip selection", { provider });
|
|
566
|
-
params.append("provider", provider);
|
|
567
|
-
} else {
|
|
568
|
-
console.log("[ExpoAuthProvider] No provider specified, defaulting to Google");
|
|
569
|
-
params.append("provider", "google");
|
|
570
|
-
}
|
|
571
|
-
const fullAuthUrl = `${baseUrl}?${params.toString()}`;
|
|
572
|
-
console.log("[ExpoAuthProvider] Starting authentication", {
|
|
573
|
-
baseUrl,
|
|
574
|
-
redirectUrl,
|
|
575
|
-
publicKey,
|
|
576
|
-
sessionId,
|
|
577
|
-
provider
|
|
578
|
-
});
|
|
579
|
-
await WebBrowser.warmUpAsync();
|
|
580
|
-
const result = await WebBrowser.openAuthSessionAsync(fullAuthUrl, redirectUrl, {
|
|
581
|
-
// Use system browser on iOS for ASWebAuthenticationSession
|
|
582
|
-
preferEphemeralSession: false
|
|
583
|
-
});
|
|
584
|
-
console.log("[ExpoAuthProvider] Authentication result", {
|
|
585
|
-
type: result.type,
|
|
586
|
-
url: result.type === "success" && result.url ? result.url.substring(0, 100) + "..." : void 0
|
|
587
|
-
});
|
|
588
|
-
if (result.type === "success" && result.url) {
|
|
589
|
-
const url = new URL(result.url);
|
|
590
|
-
const walletId = url.searchParams.get("wallet_id");
|
|
591
|
-
const organizationId = url.searchParams.get("organization_id");
|
|
592
|
-
const accountDerivationIndex = url.searchParams.get("selected_account_index");
|
|
593
|
-
const expiresInMs = url.searchParams.get("expires_in_ms");
|
|
594
|
-
const authUserId = url.searchParams.get("auth_user_id");
|
|
595
|
-
if (!walletId) {
|
|
596
|
-
throw new Error("Authentication failed: no walletId in redirect URL");
|
|
597
|
-
}
|
|
598
|
-
if (!organizationId) {
|
|
599
|
-
console.error("[ExpoAuthProvider] Missing organizationId in redirect URL", { url: result.url });
|
|
600
|
-
throw new Error("Authentication failed: no organizationId in redirect URL");
|
|
601
|
-
}
|
|
602
|
-
console.log("[ExpoAuthProvider] Auth redirect parameters", {
|
|
603
|
-
walletId,
|
|
604
|
-
organizationId,
|
|
605
|
-
provider,
|
|
606
|
-
accountDerivationIndex,
|
|
607
|
-
expiresInMs,
|
|
608
|
-
authUserId
|
|
609
|
-
});
|
|
610
|
-
return {
|
|
611
|
-
walletId,
|
|
612
|
-
organizationId,
|
|
613
|
-
provider: provider || void 0,
|
|
614
|
-
accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
|
|
615
|
-
expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
|
|
616
|
-
authUserId: authUserId || void 0
|
|
617
|
-
};
|
|
618
|
-
} else if (result.type === "cancel") {
|
|
619
|
-
throw new Error("User cancelled authentication");
|
|
620
|
-
} else {
|
|
621
|
-
throw new Error("Authentication failed");
|
|
622
|
-
}
|
|
623
|
-
} catch (error) {
|
|
624
|
-
console.error("[ExpoAuthProvider] Authentication error", error);
|
|
625
|
-
throw error;
|
|
626
|
-
} finally {
|
|
627
|
-
await WebBrowser.coolDownAsync();
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
isAvailable() {
|
|
631
|
-
return Promise.resolve(true);
|
|
632
|
-
}
|
|
633
|
-
};
|
|
634
|
-
|
|
635
535
|
// src/providers/embedded/ExpoAuth2AuthProvider.ts
|
|
636
|
-
var
|
|
536
|
+
var WebBrowser = __toESM(require("expo-web-browser"));
|
|
637
537
|
var import_auth2 = require("@phantom/auth2");
|
|
638
538
|
var ExpoAuth2AuthProvider = class {
|
|
639
539
|
constructor(stamper, auth2ProviderOptions, kmsClientOptions) {
|
|
@@ -649,246 +549,111 @@ var ExpoAuth2AuthProvider = class {
|
|
|
649
549
|
* so the token exchange and KMS calls all happen here before returning AuthResult.
|
|
650
550
|
*/
|
|
651
551
|
async authenticate(options) {
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
const keyPair = this.stamper.getCryptoKeyPair();
|
|
656
|
-
if (!keyPair) {
|
|
657
|
-
throw new Error("Stamper key pair not found.");
|
|
658
|
-
}
|
|
659
|
-
const codeVerifier = (0, import_auth2.createCodeVerifier)();
|
|
660
|
-
const url = await (0, import_auth2.createConnectStartUrl)({
|
|
661
|
-
keyPair,
|
|
662
|
-
connectLoginUrl: this.auth2ProviderOptions.connectLoginUrl,
|
|
663
|
-
clientId: this.auth2ProviderOptions.clientId,
|
|
664
|
-
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
552
|
+
const { url, codeVerifier } = await (0, import_auth2.prepareAuth2Flow)({
|
|
553
|
+
stamper: this.stamper,
|
|
554
|
+
auth2Options: this.auth2ProviderOptions,
|
|
665
555
|
sessionId: options.sessionId,
|
|
666
|
-
provider: options.provider
|
|
667
|
-
codeVerifier,
|
|
668
|
-
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
669
|
-
salt: ""
|
|
556
|
+
provider: options.provider
|
|
670
557
|
});
|
|
671
|
-
await
|
|
558
|
+
await WebBrowser.warmUpAsync();
|
|
672
559
|
let result;
|
|
673
560
|
try {
|
|
674
|
-
result = await
|
|
561
|
+
result = await WebBrowser.openAuthSessionAsync(url, this.auth2ProviderOptions.redirectUri);
|
|
675
562
|
} finally {
|
|
676
|
-
await
|
|
563
|
+
await WebBrowser.coolDownAsync();
|
|
677
564
|
}
|
|
678
565
|
if (!result.url) {
|
|
679
566
|
throw new Error("Authentication failed");
|
|
680
567
|
}
|
|
681
568
|
const callbackUrl = new URL(result.url);
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
}
|
|
691
|
-
const code = callbackUrl.searchParams.get("code");
|
|
692
|
-
if (!code) {
|
|
693
|
-
throw new Error("Auth2 callback missing authorization code");
|
|
694
|
-
}
|
|
695
|
-
const { idToken, bearerToken, authUserId, expiresInMs, refreshToken } = await (0, import_auth2.exchangeAuthCode)({
|
|
696
|
-
authApiBaseUrl: this.auth2ProviderOptions.authApiBaseUrl,
|
|
697
|
-
clientId: this.auth2ProviderOptions.clientId,
|
|
698
|
-
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
569
|
+
const code = (0, import_auth2.validateAuth2Callback)({
|
|
570
|
+
getParam: (key) => callbackUrl.searchParams.get(key),
|
|
571
|
+
expectedSessionId: options.sessionId
|
|
572
|
+
});
|
|
573
|
+
return (0, import_auth2.completeAuth2Exchange)({
|
|
574
|
+
stamper: this.stamper,
|
|
575
|
+
kms: this.kms,
|
|
576
|
+
auth2Options: this.auth2ProviderOptions,
|
|
699
577
|
code,
|
|
700
|
-
codeVerifier
|
|
578
|
+
codeVerifier,
|
|
579
|
+
provider: options.provider
|
|
701
580
|
});
|
|
702
|
-
await this.stamper.setTokens({ idToken, bearerToken, refreshToken, expiresInMs });
|
|
703
|
-
const { organizationId, walletId } = await this.kms.discoverOrganizationAndWalletId(bearerToken, authUserId);
|
|
704
|
-
return {
|
|
705
|
-
walletId,
|
|
706
|
-
organizationId,
|
|
707
|
-
provider: options.provider,
|
|
708
|
-
accountDerivationIndex: 0,
|
|
709
|
-
// discoverWalletId uses derivation index of 0.
|
|
710
|
-
expiresInMs,
|
|
711
|
-
authUserId,
|
|
712
|
-
bearerToken
|
|
713
|
-
};
|
|
714
581
|
}
|
|
715
582
|
};
|
|
716
583
|
|
|
717
|
-
// src/
|
|
584
|
+
// src/PhantomProvider.tsx
|
|
585
|
+
var import_auth22 = require("@phantom/auth2");
|
|
586
|
+
|
|
587
|
+
// src/providers/embedded/SecureStoreAuth2StamperStorage.ts
|
|
718
588
|
var SecureStore2 = __toESM(require("expo-secure-store"));
|
|
719
589
|
var import_bs58 = __toESM(require("bs58"));
|
|
720
590
|
var import_buffer = require("buffer");
|
|
721
|
-
var
|
|
722
|
-
|
|
723
|
-
var import_auth22 = require("@phantom/auth2");
|
|
724
|
-
var import_constants2 = require("@phantom/constants");
|
|
725
|
-
var ExpoAuth2Stamper = class {
|
|
726
|
-
/**
|
|
727
|
-
* @param storageKey - expo-secure-store key used to persist the P-256 private key.
|
|
728
|
-
* Use a unique key per app, e.g. `phantom-auth2-<appId>`.
|
|
729
|
-
* @param refreshConfig - When provided, the stamper will automatically refresh
|
|
730
|
-
* the id_token using the refresh_token before it expires.
|
|
731
|
-
*/
|
|
732
|
-
constructor(storageKey, refreshConfig) {
|
|
591
|
+
var SecureStoreAuth2StamperStorage = class {
|
|
592
|
+
constructor(storageKey) {
|
|
733
593
|
this.storageKey = storageKey;
|
|
734
|
-
this.
|
|
735
|
-
this._keyPair = null;
|
|
736
|
-
this._keyInfo = null;
|
|
737
|
-
this._idToken = null;
|
|
738
|
-
this._bearerToken = null;
|
|
739
|
-
this._refreshToken = null;
|
|
740
|
-
this._tokenExpiresAt = null;
|
|
741
|
-
this.algorithm = import_sdk_types.Algorithm.secp256r1;
|
|
742
|
-
this.type = "OIDC";
|
|
594
|
+
this.requiresExtractableKeys = true;
|
|
743
595
|
}
|
|
744
|
-
async
|
|
745
|
-
const
|
|
746
|
-
if (
|
|
747
|
-
|
|
748
|
-
privateKey: await this.importPrivateKey(stored.privateKeyPkcs8),
|
|
749
|
-
publicKey: await this.importPublicKeyFromBase58(stored.keyInfo.publicKey)
|
|
750
|
-
};
|
|
751
|
-
this._keyInfo = stored.keyInfo;
|
|
752
|
-
if (stored.idToken) {
|
|
753
|
-
this._idToken = stored.idToken;
|
|
754
|
-
}
|
|
755
|
-
if (stored.bearerToken) {
|
|
756
|
-
this._bearerToken = stored.bearerToken;
|
|
757
|
-
}
|
|
758
|
-
if (stored.refreshToken) {
|
|
759
|
-
this._refreshToken = stored.refreshToken;
|
|
760
|
-
}
|
|
761
|
-
if (stored.tokenExpiresAt) {
|
|
762
|
-
this._tokenExpiresAt = stored.tokenExpiresAt;
|
|
763
|
-
}
|
|
764
|
-
return this._keyInfo;
|
|
596
|
+
async load() {
|
|
597
|
+
const raw = await SecureStore2.getItemAsync(this.storageKey);
|
|
598
|
+
if (raw === null) {
|
|
599
|
+
return null;
|
|
765
600
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
return this._keyPair;
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Returns the current token state (refreshing proactively if near expiry),
|
|
776
|
-
* or null if no token has been set yet.
|
|
777
|
-
*/
|
|
778
|
-
async getTokens() {
|
|
779
|
-
if (this.refreshConfig && this._refreshToken && this._tokenExpiresAt !== null && Date.now() >= this._tokenExpiresAt - import_constants2.TOKEN_REFRESH_BUFFER_MS) {
|
|
780
|
-
const refreshed = await (0, import_auth22.refreshToken)({
|
|
781
|
-
authApiBaseUrl: this.refreshConfig.authApiBaseUrl,
|
|
782
|
-
clientId: this.refreshConfig.clientId,
|
|
783
|
-
redirectUri: this.refreshConfig.redirectUri,
|
|
784
|
-
refreshToken: this._refreshToken
|
|
785
|
-
});
|
|
786
|
-
await this.setTokens(refreshed);
|
|
601
|
+
let record;
|
|
602
|
+
try {
|
|
603
|
+
record = JSON.parse(raw);
|
|
604
|
+
} catch (err) {
|
|
605
|
+
await SecureStore2.deleteItemAsync(this.storageKey);
|
|
606
|
+
throw new Error(`SecureStoreAuth2StamperStorage: corrupt stored record (JSON parse failed): ${err}`);
|
|
787
607
|
}
|
|
788
|
-
|
|
789
|
-
|
|
608
|
+
let privateKey;
|
|
609
|
+
let publicKey;
|
|
610
|
+
try {
|
|
611
|
+
privateKey = await this.importPrivateKey(record.privateKeyPkcs8);
|
|
612
|
+
publicKey = await this.importPublicKeyFromBase58(record.keyInfo.publicKey);
|
|
613
|
+
} catch (err) {
|
|
614
|
+
await SecureStore2.deleteItemAsync(this.storageKey);
|
|
615
|
+
throw new Error(`SecureStoreAuth2StamperStorage: corrupt stored record (key import failed): ${err}`);
|
|
790
616
|
}
|
|
791
617
|
return {
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
618
|
+
keyPair: { privateKey, publicKey },
|
|
619
|
+
keyInfo: record.keyInfo,
|
|
620
|
+
accessToken: record.accessToken,
|
|
621
|
+
idType: record.idType,
|
|
622
|
+
refreshToken: record.refreshToken,
|
|
623
|
+
tokenExpiresAt: record.tokenExpiresAt
|
|
795
624
|
};
|
|
796
625
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
*/
|
|
808
|
-
async setTokens({
|
|
809
|
-
idToken,
|
|
810
|
-
bearerToken,
|
|
811
|
-
refreshToken,
|
|
812
|
-
expiresInMs
|
|
813
|
-
}) {
|
|
814
|
-
this._idToken = idToken;
|
|
815
|
-
this._bearerToken = bearerToken;
|
|
816
|
-
this._refreshToken = refreshToken ?? null;
|
|
817
|
-
this._tokenExpiresAt = expiresInMs != null ? Date.now() + expiresInMs : null;
|
|
818
|
-
const existing = await this.loadRecord();
|
|
819
|
-
if (existing) {
|
|
820
|
-
await this.storeRecord({
|
|
821
|
-
...existing,
|
|
822
|
-
idToken,
|
|
823
|
-
bearerToken,
|
|
824
|
-
refreshToken,
|
|
825
|
-
tokenExpiresAt: this._tokenExpiresAt ?? void 0
|
|
826
|
-
});
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
async stamp(params) {
|
|
830
|
-
if (!this._keyPair || !this._keyInfo || this._idToken === null) {
|
|
831
|
-
throw new Error("ExpoAuth2Stamper not initialized. Call init() first.");
|
|
832
|
-
}
|
|
833
|
-
const signatureRaw = await crypto.subtle.sign(
|
|
834
|
-
{ name: "ECDSA", hash: "SHA-256" },
|
|
835
|
-
this._keyPair.privateKey,
|
|
836
|
-
new Uint8Array(params.data)
|
|
837
|
-
);
|
|
838
|
-
const rawPublicKey = import_bs58.default.decode(this._keyInfo.publicKey);
|
|
839
|
-
const stampData = {
|
|
840
|
-
kind: this.type,
|
|
841
|
-
idToken: this._idToken,
|
|
842
|
-
publicKey: (0, import_base64url.base64urlEncode)(rawPublicKey),
|
|
843
|
-
algorithm: this.algorithm,
|
|
844
|
-
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
845
|
-
salt: "",
|
|
846
|
-
signature: (0, import_base64url.base64urlEncode)(new Uint8Array(signatureRaw))
|
|
626
|
+
async save(record) {
|
|
627
|
+
const pkcs8Buffer = await crypto.subtle.exportKey("pkcs8", record.keyPair.privateKey);
|
|
628
|
+
const privateKeyPkcs8 = import_buffer.Buffer.from(pkcs8Buffer).toString("base64");
|
|
629
|
+
const serialized = {
|
|
630
|
+
privateKeyPkcs8,
|
|
631
|
+
keyInfo: record.keyInfo,
|
|
632
|
+
accessToken: record.accessToken,
|
|
633
|
+
idType: record.idType,
|
|
634
|
+
refreshToken: record.refreshToken,
|
|
635
|
+
tokenExpiresAt: record.tokenExpiresAt
|
|
847
636
|
};
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
await this.clear();
|
|
852
|
-
return this.generateAndStore();
|
|
637
|
+
await SecureStore2.setItemAsync(this.storageKey, JSON.stringify(serialized), {
|
|
638
|
+
requireAuthentication: false
|
|
639
|
+
});
|
|
853
640
|
}
|
|
854
641
|
async clear() {
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
this._idToken = null;
|
|
859
|
-
this._bearerToken = null;
|
|
860
|
-
this._refreshToken = null;
|
|
861
|
-
this._tokenExpiresAt = null;
|
|
862
|
-
}
|
|
863
|
-
// Auth2 doesn't use key rotation; minimal no-op implementations.
|
|
864
|
-
async rotateKeyPair() {
|
|
865
|
-
return this.init();
|
|
866
|
-
}
|
|
867
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
868
|
-
async commitRotation(authenticatorId) {
|
|
869
|
-
if (this._keyInfo) {
|
|
870
|
-
this._keyInfo.authenticatorId = authenticatorId;
|
|
642
|
+
try {
|
|
643
|
+
await SecureStore2.deleteItemAsync(this.storageKey);
|
|
644
|
+
} catch {
|
|
871
645
|
}
|
|
872
646
|
}
|
|
873
|
-
async
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
647
|
+
async importPrivateKey(pkcs8Base64) {
|
|
648
|
+
const pkcs8Bytes = import_buffer.Buffer.from(pkcs8Base64, "base64");
|
|
649
|
+
return crypto.subtle.importKey(
|
|
650
|
+
"pkcs8",
|
|
651
|
+
pkcs8Bytes,
|
|
877
652
|
{ name: "ECDSA", namedCurve: "P-256" },
|
|
878
|
-
|
|
879
|
-
// extractable
|
|
880
|
-
["sign"
|
|
653
|
+
this.requiresExtractableKeys,
|
|
654
|
+
// extractable so save() can re-export via pkcs8
|
|
655
|
+
["sign"]
|
|
881
656
|
);
|
|
882
|
-
const rawPublicKey = new Uint8Array(await crypto.subtle.exportKey("raw", keyPair.publicKey));
|
|
883
|
-
const publicKeyBase58 = import_bs58.default.encode(rawPublicKey);
|
|
884
|
-
const keyIdBuffer = await crypto.subtle.digest("SHA-256", rawPublicKey.buffer);
|
|
885
|
-
const keyId = (0, import_base64url.base64urlEncode)(new Uint8Array(keyIdBuffer)).substring(0, 16);
|
|
886
|
-
this._keyPair = keyPair;
|
|
887
|
-
this._keyInfo = { keyId, publicKey: publicKeyBase58, createdAt: Date.now() };
|
|
888
|
-
const pkcs8Buffer = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
|
|
889
|
-
const privateKeyPkcs8 = import_buffer.Buffer.from(pkcs8Buffer).toString("base64");
|
|
890
|
-
await this.storeRecord({ privateKeyPkcs8, keyInfo: this._keyInfo });
|
|
891
|
-
return this._keyInfo;
|
|
892
657
|
}
|
|
893
658
|
async importPublicKeyFromBase58(base58PublicKey) {
|
|
894
659
|
const rawBytes = import_bs58.default.decode(base58PublicKey);
|
|
@@ -901,38 +666,10 @@ var ExpoAuth2Stamper = class {
|
|
|
901
666
|
["verify"]
|
|
902
667
|
);
|
|
903
668
|
}
|
|
904
|
-
async importPrivateKey(pkcs8Base64) {
|
|
905
|
-
const pkcs8Bytes = import_buffer.Buffer.from(pkcs8Base64, "base64");
|
|
906
|
-
return crypto.subtle.importKey(
|
|
907
|
-
"pkcs8",
|
|
908
|
-
pkcs8Bytes,
|
|
909
|
-
{ name: "ECDSA", namedCurve: "P-256" },
|
|
910
|
-
false,
|
|
911
|
-
// non-extractable once loaded into memory
|
|
912
|
-
["sign"]
|
|
913
|
-
);
|
|
914
|
-
}
|
|
915
|
-
async loadRecord() {
|
|
916
|
-
try {
|
|
917
|
-
const raw = await SecureStore2.getItemAsync(this.storageKey);
|
|
918
|
-
return raw ? JSON.parse(raw) : null;
|
|
919
|
-
} catch {
|
|
920
|
-
return null;
|
|
921
|
-
}
|
|
922
|
-
}
|
|
923
|
-
async storeRecord(record) {
|
|
924
|
-
await SecureStore2.setItemAsync(this.storageKey, JSON.stringify(record), { requireAuthentication: false });
|
|
925
|
-
}
|
|
926
|
-
async clearStoredRecord() {
|
|
927
|
-
try {
|
|
928
|
-
await SecureStore2.deleteItemAsync(this.storageKey);
|
|
929
|
-
} catch {
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
669
|
};
|
|
933
670
|
|
|
934
671
|
// src/providers/embedded/url-params.ts
|
|
935
|
-
var
|
|
672
|
+
var import_react_native5 = require("react-native");
|
|
936
673
|
var ExpoURLParamsAccessor = class {
|
|
937
674
|
constructor() {
|
|
938
675
|
this.listeners = /* @__PURE__ */ new Set();
|
|
@@ -944,7 +681,7 @@ var ExpoURLParamsAccessor = class {
|
|
|
944
681
|
}
|
|
945
682
|
async getInitialParams() {
|
|
946
683
|
try {
|
|
947
|
-
const url = await
|
|
684
|
+
const url = await import_react_native5.Linking.getInitialURL();
|
|
948
685
|
if (!url) {
|
|
949
686
|
return null;
|
|
950
687
|
}
|
|
@@ -960,7 +697,7 @@ var ExpoURLParamsAccessor = class {
|
|
|
960
697
|
if (this.subscription) {
|
|
961
698
|
return;
|
|
962
699
|
}
|
|
963
|
-
this.subscription =
|
|
700
|
+
this.subscription = import_react_native5.Linking.addEventListener("url", ({ url }) => {
|
|
964
701
|
const params = this.parseURLParams(url);
|
|
965
702
|
if (params && Object.keys(params).length > 0) {
|
|
966
703
|
this.currentParams = { ...this.currentParams, ...params };
|
|
@@ -999,180 +736,6 @@ var ExpoURLParamsAccessor = class {
|
|
|
999
736
|
}
|
|
1000
737
|
};
|
|
1001
738
|
|
|
1002
|
-
// src/providers/embedded/stamper.ts
|
|
1003
|
-
var SecureStore3 = __toESM(require("expo-secure-store"));
|
|
1004
|
-
var import_api_key_stamper = require("@phantom/api-key-stamper");
|
|
1005
|
-
var import_constants3 = require("@phantom/constants");
|
|
1006
|
-
var import_crypto = require("@phantom/crypto");
|
|
1007
|
-
var import_base64url2 = require("@phantom/base64url");
|
|
1008
|
-
var ReactNativeStamper = class {
|
|
1009
|
-
// Optional for PKI, required for OIDC
|
|
1010
|
-
constructor(config = {}) {
|
|
1011
|
-
this.activeKeyRecord = null;
|
|
1012
|
-
this.pendingKeyRecord = null;
|
|
1013
|
-
this.algorithm = import_constants3.DEFAULT_AUTHENTICATOR_ALGORITHM;
|
|
1014
|
-
this.type = "PKI";
|
|
1015
|
-
this.keyPrefix = config.keyPrefix || "phantom-rn-stamper";
|
|
1016
|
-
this.appId = config.appId || "default";
|
|
1017
|
-
}
|
|
1018
|
-
/**
|
|
1019
|
-
* Initialize the stamper and generate/load cryptographic keys
|
|
1020
|
-
*/
|
|
1021
|
-
async init() {
|
|
1022
|
-
this.activeKeyRecord = await this.loadActiveKeyRecord();
|
|
1023
|
-
if (!this.activeKeyRecord) {
|
|
1024
|
-
this.activeKeyRecord = await this.generateAndStoreNewKeyRecord("active");
|
|
1025
|
-
}
|
|
1026
|
-
this.pendingKeyRecord = await this.loadPendingKeyRecord();
|
|
1027
|
-
return this.activeKeyRecord.keyInfo;
|
|
1028
|
-
}
|
|
1029
|
-
/**
|
|
1030
|
-
* Get the current key information
|
|
1031
|
-
*/
|
|
1032
|
-
getKeyInfo() {
|
|
1033
|
-
return this.activeKeyRecord?.keyInfo || null;
|
|
1034
|
-
}
|
|
1035
|
-
/**
|
|
1036
|
-
* Generate and store a new key pair, replacing any existing keys
|
|
1037
|
-
*/
|
|
1038
|
-
async resetKeyPair() {
|
|
1039
|
-
await this.clear();
|
|
1040
|
-
this.activeKeyRecord = await this.generateAndStoreNewKeyRecord("active");
|
|
1041
|
-
this.pendingKeyRecord = null;
|
|
1042
|
-
return this.activeKeyRecord.keyInfo;
|
|
1043
|
-
}
|
|
1044
|
-
/**
|
|
1045
|
-
* Create X-Phantom-Stamp header value using stored secret key
|
|
1046
|
-
* @param params - Parameters object with data to sign and optional override params
|
|
1047
|
-
* @returns Complete X-Phantom-Stamp header value
|
|
1048
|
-
*/
|
|
1049
|
-
async stamp(params) {
|
|
1050
|
-
if (!this.activeKeyRecord) {
|
|
1051
|
-
throw new Error("Stamper not initialized. Call init() first.");
|
|
1052
|
-
}
|
|
1053
|
-
const apiKeyStamper = new import_api_key_stamper.ApiKeyStamper({ apiSecretKey: this.activeKeyRecord.secretKey });
|
|
1054
|
-
return await apiKeyStamper.stamp(params);
|
|
1055
|
-
}
|
|
1056
|
-
/**
|
|
1057
|
-
* Clear all stored keys from SecureStore
|
|
1058
|
-
*/
|
|
1059
|
-
async clear() {
|
|
1060
|
-
const activeKey = this.getActiveKeyName();
|
|
1061
|
-
const pendingKey = this.getPendingKeyName();
|
|
1062
|
-
try {
|
|
1063
|
-
await SecureStore3.deleteItemAsync(activeKey);
|
|
1064
|
-
} catch (error) {
|
|
1065
|
-
}
|
|
1066
|
-
try {
|
|
1067
|
-
await SecureStore3.deleteItemAsync(pendingKey);
|
|
1068
|
-
} catch (error) {
|
|
1069
|
-
}
|
|
1070
|
-
this.activeKeyRecord = null;
|
|
1071
|
-
this.pendingKeyRecord = null;
|
|
1072
|
-
}
|
|
1073
|
-
/**
|
|
1074
|
-
* Generate a new keypair for rotation without making it active
|
|
1075
|
-
*/
|
|
1076
|
-
async rotateKeyPair() {
|
|
1077
|
-
this.pendingKeyRecord = await this.generateAndStoreNewKeyRecord("pending");
|
|
1078
|
-
return this.pendingKeyRecord.keyInfo;
|
|
1079
|
-
}
|
|
1080
|
-
/**
|
|
1081
|
-
* Switch to the pending keypair, making it active and cleaning up the old one
|
|
1082
|
-
*/
|
|
1083
|
-
async commitRotation(authenticatorId) {
|
|
1084
|
-
if (!this.pendingKeyRecord) {
|
|
1085
|
-
throw new Error("No pending keypair to commit");
|
|
1086
|
-
}
|
|
1087
|
-
if (this.activeKeyRecord) {
|
|
1088
|
-
try {
|
|
1089
|
-
await SecureStore3.deleteItemAsync(this.getActiveKeyName());
|
|
1090
|
-
} catch (error) {
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
this.pendingKeyRecord.status = "active";
|
|
1094
|
-
this.pendingKeyRecord.authenticatorId = authenticatorId;
|
|
1095
|
-
this.pendingKeyRecord.keyInfo.authenticatorId = authenticatorId;
|
|
1096
|
-
this.activeKeyRecord = this.pendingKeyRecord;
|
|
1097
|
-
this.pendingKeyRecord = null;
|
|
1098
|
-
await this.storeKeyRecord(this.activeKeyRecord, "active");
|
|
1099
|
-
try {
|
|
1100
|
-
await SecureStore3.deleteItemAsync(this.getPendingKeyName());
|
|
1101
|
-
} catch (error) {
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
/**
|
|
1105
|
-
* Discard the pending keypair on rotation failure
|
|
1106
|
-
*/
|
|
1107
|
-
async rollbackRotation() {
|
|
1108
|
-
if (!this.pendingKeyRecord) {
|
|
1109
|
-
return;
|
|
1110
|
-
}
|
|
1111
|
-
try {
|
|
1112
|
-
await SecureStore3.deleteItemAsync(this.getPendingKeyName());
|
|
1113
|
-
} catch (error) {
|
|
1114
|
-
}
|
|
1115
|
-
this.pendingKeyRecord = null;
|
|
1116
|
-
}
|
|
1117
|
-
async generateAndStoreNewKeyRecord(type) {
|
|
1118
|
-
const keypair = (0, import_crypto.generateKeyPair)();
|
|
1119
|
-
const keyId = this.createKeyId(keypair.publicKey);
|
|
1120
|
-
const now = Date.now();
|
|
1121
|
-
const keyInfo = {
|
|
1122
|
-
keyId,
|
|
1123
|
-
publicKey: keypair.publicKey,
|
|
1124
|
-
createdAt: now
|
|
1125
|
-
};
|
|
1126
|
-
const record = {
|
|
1127
|
-
keyInfo,
|
|
1128
|
-
secretKey: keypair.secretKey,
|
|
1129
|
-
createdAt: now,
|
|
1130
|
-
expiresAt: 0,
|
|
1131
|
-
// Not used anymore, kept for backward compatibility
|
|
1132
|
-
status: type
|
|
1133
|
-
};
|
|
1134
|
-
await this.storeKeyRecord(record, type);
|
|
1135
|
-
return record;
|
|
1136
|
-
}
|
|
1137
|
-
createKeyId(publicKey) {
|
|
1138
|
-
return (0, import_base64url2.base64urlEncode)(new TextEncoder().encode(publicKey)).substring(0, 16);
|
|
1139
|
-
}
|
|
1140
|
-
async storeKeyRecord(record, type) {
|
|
1141
|
-
const keyName = type === "active" ? this.getActiveKeyName() : this.getPendingKeyName();
|
|
1142
|
-
await SecureStore3.setItemAsync(keyName, JSON.stringify(record), {
|
|
1143
|
-
requireAuthentication: false
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
async loadActiveKeyRecord() {
|
|
1147
|
-
try {
|
|
1148
|
-
const activeKey = this.getActiveKeyName();
|
|
1149
|
-
const storedRecord = await SecureStore3.getItemAsync(activeKey);
|
|
1150
|
-
if (storedRecord) {
|
|
1151
|
-
return JSON.parse(storedRecord);
|
|
1152
|
-
}
|
|
1153
|
-
} catch (error) {
|
|
1154
|
-
}
|
|
1155
|
-
return null;
|
|
1156
|
-
}
|
|
1157
|
-
async loadPendingKeyRecord() {
|
|
1158
|
-
try {
|
|
1159
|
-
const pendingKey = this.getPendingKeyName();
|
|
1160
|
-
const storedRecord = await SecureStore3.getItemAsync(pendingKey);
|
|
1161
|
-
if (storedRecord) {
|
|
1162
|
-
return JSON.parse(storedRecord);
|
|
1163
|
-
}
|
|
1164
|
-
} catch (error) {
|
|
1165
|
-
}
|
|
1166
|
-
return null;
|
|
1167
|
-
}
|
|
1168
|
-
getActiveKeyName() {
|
|
1169
|
-
return `${this.keyPrefix}-${this.appId}-active`;
|
|
1170
|
-
}
|
|
1171
|
-
getPendingKeyName() {
|
|
1172
|
-
return `${this.keyPrefix}-${this.appId}-pending`;
|
|
1173
|
-
}
|
|
1174
|
-
};
|
|
1175
|
-
|
|
1176
739
|
// src/providers/embedded/logger.ts
|
|
1177
740
|
var ExpoLogger = class {
|
|
1178
741
|
constructor(enabled = false) {
|
|
@@ -1215,7 +778,7 @@ var ReactNativePhantomAppProvider = class {
|
|
|
1215
778
|
};
|
|
1216
779
|
|
|
1217
780
|
// src/PhantomProvider.tsx
|
|
1218
|
-
var
|
|
781
|
+
var import_react_native6 = require("react-native");
|
|
1219
782
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1220
783
|
function PhantomProvider({ children, config, debugConfig, theme, appIcon, appName }) {
|
|
1221
784
|
const [isConnected, setIsConnected] = (0, import_react8.useState)(false);
|
|
@@ -1228,12 +791,12 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
|
|
|
1228
791
|
const redirectUrl = config.authOptions?.redirectUrl || `${config.scheme}://phantom-auth-callback`;
|
|
1229
792
|
return {
|
|
1230
793
|
...config,
|
|
1231
|
-
apiBaseUrl: config.apiBaseUrl ||
|
|
1232
|
-
embeddedWalletType: config.embeddedWalletType ||
|
|
794
|
+
apiBaseUrl: config.apiBaseUrl || import_constants.DEFAULT_WALLET_API_URL,
|
|
795
|
+
embeddedWalletType: config.embeddedWalletType || import_constants.DEFAULT_EMBEDDED_WALLET_TYPE,
|
|
1233
796
|
authOptions: {
|
|
1234
|
-
...config.authOptions || {},
|
|
1235
797
|
redirectUrl,
|
|
1236
|
-
authUrl: config.authOptions?.authUrl ||
|
|
798
|
+
authUrl: config.authOptions?.authUrl || import_constants.DEFAULT_AUTH_URL,
|
|
799
|
+
authApiBaseUrl: config.authOptions?.authApiBaseUrl || import_constants.DEFAULT_AUTH_API_BASE_URL
|
|
1237
800
|
}
|
|
1238
801
|
};
|
|
1239
802
|
}, [config]);
|
|
@@ -1241,28 +804,25 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
|
|
|
1241
804
|
const storage = new ExpoSecureStorage();
|
|
1242
805
|
const urlParamsAccessor = new ExpoURLParamsAccessor();
|
|
1243
806
|
const logger = new ExpoLogger(debugConfig?.enabled || false);
|
|
1244
|
-
const stamper =
|
|
1245
|
-
authApiBaseUrl:
|
|
1246
|
-
clientId:
|
|
1247
|
-
redirectUri:
|
|
1248
|
-
}) : new ReactNativeStamper({
|
|
1249
|
-
keyPrefix: `phantom-rn-${memoizedConfig.appId}`,
|
|
1250
|
-
appId: memoizedConfig.appId
|
|
807
|
+
const stamper = new import_auth22.Auth2Stamper(new SecureStoreAuth2StamperStorage(`phantom-auth2-${memoizedConfig.appId}`), {
|
|
808
|
+
authApiBaseUrl: memoizedConfig.authOptions.authApiBaseUrl,
|
|
809
|
+
clientId: memoizedConfig.appId,
|
|
810
|
+
redirectUri: memoizedConfig.authOptions.redirectUrl
|
|
1251
811
|
});
|
|
1252
|
-
const authProvider =
|
|
812
|
+
const authProvider = new ExpoAuth2AuthProvider(
|
|
1253
813
|
stamper,
|
|
1254
814
|
{
|
|
1255
|
-
redirectUri:
|
|
1256
|
-
connectLoginUrl:
|
|
1257
|
-
clientId:
|
|
1258
|
-
authApiBaseUrl:
|
|
815
|
+
redirectUri: memoizedConfig.authOptions.redirectUrl,
|
|
816
|
+
connectLoginUrl: memoizedConfig.authOptions.authUrl,
|
|
817
|
+
clientId: memoizedConfig.appId,
|
|
818
|
+
authApiBaseUrl: memoizedConfig.authOptions.authApiBaseUrl
|
|
1259
819
|
},
|
|
1260
820
|
{
|
|
1261
|
-
apiBaseUrl:
|
|
1262
|
-
appId:
|
|
821
|
+
apiBaseUrl: memoizedConfig.apiBaseUrl,
|
|
822
|
+
appId: memoizedConfig.appId
|
|
1263
823
|
}
|
|
1264
|
-
)
|
|
1265
|
-
const platformName = `${
|
|
824
|
+
);
|
|
825
|
+
const platformName = `${import_react_native6.Platform.OS}-${import_react_native6.Platform.Version}`;
|
|
1266
826
|
const platform = {
|
|
1267
827
|
storage,
|
|
1268
828
|
authProvider,
|
|
@@ -1271,13 +831,13 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
|
|
|
1271
831
|
phantomAppProvider: new ReactNativePhantomAppProvider(),
|
|
1272
832
|
name: platformName,
|
|
1273
833
|
analyticsHeaders: {
|
|
1274
|
-
[
|
|
1275
|
-
[
|
|
1276
|
-
[
|
|
1277
|
-
[
|
|
1278
|
-
[
|
|
1279
|
-
[
|
|
1280
|
-
[
|
|
834
|
+
[import_constants.ANALYTICS_HEADERS.SDK_TYPE]: "react-native",
|
|
835
|
+
[import_constants.ANALYTICS_HEADERS.PLATFORM]: "ext-sdk",
|
|
836
|
+
[import_constants.ANALYTICS_HEADERS.PLATFORM_VERSION]: `${import_react_native6.Platform.Version}`,
|
|
837
|
+
[import_constants.ANALYTICS_HEADERS.CLIENT]: import_react_native6.Platform.OS,
|
|
838
|
+
[import_constants.ANALYTICS_HEADERS.APP_ID]: config.appId,
|
|
839
|
+
[import_constants.ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
|
|
840
|
+
[import_constants.ANALYTICS_HEADERS.SDK_VERSION]: "2.0.0-beta.0"
|
|
1281
841
|
// Replaced at build time
|
|
1282
842
|
}
|
|
1283
843
|
};
|
|
@@ -1391,13 +951,16 @@ function useEthereum() {
|
|
|
1391
951
|
|
|
1392
952
|
// src/index.ts
|
|
1393
953
|
var import_client = require("@phantom/client");
|
|
1394
|
-
var
|
|
954
|
+
var import_constants2 = require("@phantom/constants");
|
|
955
|
+
var import_base64url = require("@phantom/base64url");
|
|
1395
956
|
var import_wallet_sdk_ui6 = require("@phantom/wallet-sdk-ui");
|
|
1396
957
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1397
958
|
0 && (module.exports = {
|
|
1398
959
|
AddressType,
|
|
1399
960
|
NetworkId,
|
|
1400
961
|
PhantomProvider,
|
|
962
|
+
base64urlDecode,
|
|
963
|
+
base64urlEncode,
|
|
1401
964
|
darkTheme,
|
|
1402
965
|
lightTheme,
|
|
1403
966
|
useAccounts,
|