@phantom/react-native-sdk 1.0.4 → 1.0.6
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 +133 -59
- package/dist/index.mjs +119 -46
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ 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_constants5.NetworkId,
|
|
35
35
|
PhantomProvider: () => PhantomProvider,
|
|
36
36
|
darkTheme: () => import_wallet_sdk_ui6.darkTheme,
|
|
37
37
|
lightTheme: () => import_wallet_sdk_ui6.lightTheme,
|
|
@@ -48,7 +48,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
48
48
|
// src/PhantomProvider.tsx
|
|
49
49
|
var import_react8 = require("react");
|
|
50
50
|
var import_embedded_provider_core = require("@phantom/embedded-provider-core");
|
|
51
|
-
var
|
|
51
|
+
var import_constants4 = require("@phantom/constants");
|
|
52
52
|
var import_wallet_sdk_ui5 = require("@phantom/wallet-sdk-ui");
|
|
53
53
|
|
|
54
54
|
// src/ModalProvider.tsx
|
|
@@ -557,7 +557,7 @@ var ExpoAuthProvider = class {
|
|
|
557
557
|
// OAuth session management - defaults to allow refresh unless explicitly clearing after logout
|
|
558
558
|
clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
|
|
559
559
|
allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
|
|
560
|
-
sdk_version: "1.0.
|
|
560
|
+
sdk_version: "1.0.6",
|
|
561
561
|
sdk_type: "react-native",
|
|
562
562
|
platform: import_react_native5.Platform.OS
|
|
563
563
|
});
|
|
@@ -657,7 +657,6 @@ var ExpoAuth2AuthProvider = class {
|
|
|
657
657
|
throw new Error("Stamper key pair not found.");
|
|
658
658
|
}
|
|
659
659
|
const codeVerifier = (0, import_auth2.createCodeVerifier)();
|
|
660
|
-
const salt = (0, import_auth2.createSalt)();
|
|
661
660
|
const url = await (0, import_auth2.createConnectStartUrl)({
|
|
662
661
|
keyPair,
|
|
663
662
|
connectLoginUrl: this.auth2ProviderOptions.connectLoginUrl,
|
|
@@ -666,7 +665,8 @@ var ExpoAuth2AuthProvider = class {
|
|
|
666
665
|
sessionId: options.sessionId,
|
|
667
666
|
provider: options.provider,
|
|
668
667
|
codeVerifier,
|
|
669
|
-
salt
|
|
668
|
+
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
669
|
+
salt: ""
|
|
670
670
|
});
|
|
671
671
|
await WebBrowser2.warmUpAsync();
|
|
672
672
|
let result;
|
|
@@ -692,15 +692,14 @@ var ExpoAuth2AuthProvider = class {
|
|
|
692
692
|
if (!code) {
|
|
693
693
|
throw new Error("Auth2 callback missing authorization code");
|
|
694
694
|
}
|
|
695
|
-
const { idToken, bearerToken, authUserId, expiresInMs } = await (0, import_auth2.exchangeAuthCode)({
|
|
695
|
+
const { idToken, bearerToken, authUserId, expiresInMs, refreshToken } = await (0, import_auth2.exchangeAuthCode)({
|
|
696
696
|
authApiBaseUrl: this.auth2ProviderOptions.authApiBaseUrl,
|
|
697
697
|
clientId: this.auth2ProviderOptions.clientId,
|
|
698
698
|
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
699
699
|
code,
|
|
700
700
|
codeVerifier
|
|
701
701
|
});
|
|
702
|
-
this.stamper.idToken
|
|
703
|
-
this.stamper.salt = salt;
|
|
702
|
+
await this.stamper.setTokens({ idToken, bearerToken, refreshToken, expiresInMs });
|
|
704
703
|
const { organizationId, walletId } = await this.kms.discoverOrganizationAndWalletId(bearerToken, authUserId);
|
|
705
704
|
return {
|
|
706
705
|
walletId,
|
|
@@ -721,25 +720,47 @@ var import_bs58 = __toESM(require("bs58"));
|
|
|
721
720
|
var import_buffer = require("buffer");
|
|
722
721
|
var import_base64url = require("@phantom/base64url");
|
|
723
722
|
var import_sdk_types = require("@phantom/sdk-types");
|
|
723
|
+
var import_auth22 = require("@phantom/auth2");
|
|
724
|
+
var import_constants2 = require("@phantom/constants");
|
|
724
725
|
var ExpoAuth2Stamper = class {
|
|
725
726
|
/**
|
|
726
727
|
* @param storageKey - expo-secure-store key used to persist the P-256 private key.
|
|
727
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.
|
|
728
731
|
*/
|
|
729
|
-
constructor(storageKey) {
|
|
732
|
+
constructor(storageKey, refreshConfig) {
|
|
730
733
|
this.storageKey = storageKey;
|
|
731
|
-
this.
|
|
732
|
-
this.
|
|
734
|
+
this.refreshConfig = refreshConfig;
|
|
735
|
+
this._keyPair = null;
|
|
733
736
|
this._keyInfo = null;
|
|
737
|
+
this._idToken = null;
|
|
738
|
+
this._bearerToken = null;
|
|
739
|
+
this._refreshToken = null;
|
|
740
|
+
this._tokenExpiresAt = null;
|
|
734
741
|
this.algorithm = import_sdk_types.Algorithm.secp256r1;
|
|
735
742
|
this.type = "OIDC";
|
|
736
743
|
}
|
|
737
744
|
async init() {
|
|
738
745
|
const stored = await this.loadRecord();
|
|
739
746
|
if (stored) {
|
|
740
|
-
this.
|
|
741
|
-
|
|
747
|
+
this._keyPair = {
|
|
748
|
+
privateKey: await this.importPrivateKey(stored.privateKeyPkcs8),
|
|
749
|
+
publicKey: await this.importPublicKeyFromBase58(stored.keyInfo.publicKey)
|
|
750
|
+
};
|
|
742
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
|
+
}
|
|
743
764
|
return this._keyInfo;
|
|
744
765
|
}
|
|
745
766
|
return this.generateAndStore();
|
|
@@ -748,45 +769,96 @@ var ExpoAuth2Stamper = class {
|
|
|
748
769
|
return this._keyInfo;
|
|
749
770
|
}
|
|
750
771
|
getCryptoKeyPair() {
|
|
751
|
-
|
|
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);
|
|
787
|
+
}
|
|
788
|
+
if (!this._idToken || !this._bearerToken) {
|
|
752
789
|
return null;
|
|
753
|
-
|
|
790
|
+
}
|
|
791
|
+
return {
|
|
792
|
+
idToken: this._idToken,
|
|
793
|
+
bearerToken: this._bearerToken,
|
|
794
|
+
refreshToken: this._refreshToken ?? void 0
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Arms the stamper with the OIDC token data for subsequent KMS stamp() calls.
|
|
799
|
+
*
|
|
800
|
+
* Persists the tokens to SecureStore alongside the key pair so that
|
|
801
|
+
* auto-connect can restore them on the next app launch without a new login.
|
|
802
|
+
*
|
|
803
|
+
* @param refreshToken - When provided alongside a `refreshConfig`, enables
|
|
804
|
+
* silent token refresh before the token expires.
|
|
805
|
+
* @param expiresInMs - Token lifetime in milliseconds (from `expires_in * 1000`).
|
|
806
|
+
* Used to compute the absolute expiry time for proactive refresh.
|
|
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
|
+
}
|
|
754
828
|
}
|
|
755
829
|
async stamp(params) {
|
|
756
|
-
if (!this.
|
|
830
|
+
if (!this._keyPair || !this._keyInfo || this._idToken === null) {
|
|
757
831
|
throw new Error("ExpoAuth2Stamper not initialized. Call init() first.");
|
|
758
832
|
}
|
|
759
833
|
const signatureRaw = await crypto.subtle.sign(
|
|
760
834
|
{ name: "ECDSA", hash: "SHA-256" },
|
|
761
|
-
this.privateKey,
|
|
835
|
+
this._keyPair.privateKey,
|
|
762
836
|
new Uint8Array(params.data)
|
|
763
837
|
);
|
|
764
838
|
const rawPublicKey = import_bs58.default.decode(this._keyInfo.publicKey);
|
|
765
|
-
if (this.idToken === void 0 || this.salt === void 0) {
|
|
766
|
-
throw new Error("ExpoAuth2Stamper not initialized with idToken or salt.");
|
|
767
|
-
}
|
|
768
839
|
const stampData = {
|
|
769
|
-
kind:
|
|
770
|
-
idToken: this.
|
|
840
|
+
kind: this.type,
|
|
841
|
+
idToken: this._idToken,
|
|
771
842
|
publicKey: (0, import_base64url.base64urlEncode)(rawPublicKey),
|
|
772
|
-
algorithm:
|
|
773
|
-
salt
|
|
843
|
+
algorithm: this.algorithm,
|
|
844
|
+
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
845
|
+
salt: "",
|
|
774
846
|
signature: (0, import_base64url.base64urlEncode)(new Uint8Array(signatureRaw))
|
|
775
847
|
};
|
|
776
848
|
return (0, import_base64url.base64urlEncode)(new TextEncoder().encode(JSON.stringify(stampData)));
|
|
777
849
|
}
|
|
778
850
|
async resetKeyPair() {
|
|
779
|
-
await this.
|
|
780
|
-
this.privateKey = null;
|
|
781
|
-
this.publicKey = null;
|
|
782
|
-
this._keyInfo = null;
|
|
851
|
+
await this.clear();
|
|
783
852
|
return this.generateAndStore();
|
|
784
853
|
}
|
|
785
854
|
async clear() {
|
|
786
855
|
await this.clearStoredRecord();
|
|
787
|
-
this.
|
|
788
|
-
this.publicKey = null;
|
|
856
|
+
this._keyPair = null;
|
|
789
857
|
this._keyInfo = null;
|
|
858
|
+
this._idToken = null;
|
|
859
|
+
this._bearerToken = null;
|
|
860
|
+
this._refreshToken = null;
|
|
861
|
+
this._tokenExpiresAt = null;
|
|
790
862
|
}
|
|
791
863
|
// Auth2 doesn't use key rotation; minimal no-op implementations.
|
|
792
864
|
async rotateKeyPair() {
|
|
@@ -811,16 +883,11 @@ var ExpoAuth2Stamper = class {
|
|
|
811
883
|
const publicKeyBase58 = import_bs58.default.encode(rawPublicKey);
|
|
812
884
|
const keyIdBuffer = await crypto.subtle.digest("SHA-256", rawPublicKey.buffer);
|
|
813
885
|
const keyId = (0, import_base64url.base64urlEncode)(new Uint8Array(keyIdBuffer)).substring(0, 16);
|
|
886
|
+
this._keyPair = keyPair;
|
|
814
887
|
this._keyInfo = { keyId, publicKey: publicKeyBase58, createdAt: Date.now() };
|
|
815
888
|
const pkcs8Buffer = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
|
|
816
889
|
const privateKeyPkcs8 = import_buffer.Buffer.from(pkcs8Buffer).toString("base64");
|
|
817
|
-
await
|
|
818
|
-
this.storageKey,
|
|
819
|
-
JSON.stringify({ privateKeyPkcs8, keyInfo: this._keyInfo }),
|
|
820
|
-
{ requireAuthentication: false }
|
|
821
|
-
);
|
|
822
|
-
this.privateKey = await this.importPrivateKey(privateKeyPkcs8);
|
|
823
|
-
this.publicKey = keyPair.publicKey;
|
|
890
|
+
await this.storeRecord({ privateKeyPkcs8, keyInfo: this._keyInfo });
|
|
824
891
|
return this._keyInfo;
|
|
825
892
|
}
|
|
826
893
|
async importPublicKeyFromBase58(base58PublicKey) {
|
|
@@ -853,6 +920,9 @@ var ExpoAuth2Stamper = class {
|
|
|
853
920
|
return null;
|
|
854
921
|
}
|
|
855
922
|
}
|
|
923
|
+
async storeRecord(record) {
|
|
924
|
+
await SecureStore2.setItemAsync(this.storageKey, JSON.stringify(record), { requireAuthentication: false });
|
|
925
|
+
}
|
|
856
926
|
async clearStoredRecord() {
|
|
857
927
|
try {
|
|
858
928
|
await SecureStore2.deleteItemAsync(this.storageKey);
|
|
@@ -932,7 +1002,7 @@ var ExpoURLParamsAccessor = class {
|
|
|
932
1002
|
// src/providers/embedded/stamper.ts
|
|
933
1003
|
var SecureStore3 = __toESM(require("expo-secure-store"));
|
|
934
1004
|
var import_api_key_stamper = require("@phantom/api-key-stamper");
|
|
935
|
-
var
|
|
1005
|
+
var import_constants3 = require("@phantom/constants");
|
|
936
1006
|
var import_crypto = require("@phantom/crypto");
|
|
937
1007
|
var import_base64url2 = require("@phantom/base64url");
|
|
938
1008
|
var ReactNativeStamper = class {
|
|
@@ -940,7 +1010,7 @@ var ReactNativeStamper = class {
|
|
|
940
1010
|
constructor(config = {}) {
|
|
941
1011
|
this.activeKeyRecord = null;
|
|
942
1012
|
this.pendingKeyRecord = null;
|
|
943
|
-
this.algorithm =
|
|
1013
|
+
this.algorithm = import_constants3.DEFAULT_AUTHENTICATOR_ALGORITHM;
|
|
944
1014
|
this.type = "PKI";
|
|
945
1015
|
this.keyPrefix = config.keyPrefix || "phantom-rn-stamper";
|
|
946
1016
|
this.appId = config.appId || "default";
|
|
@@ -1108,24 +1178,24 @@ var ExpoLogger = class {
|
|
|
1108
1178
|
constructor(enabled = false) {
|
|
1109
1179
|
this.enabled = enabled;
|
|
1110
1180
|
}
|
|
1111
|
-
info(
|
|
1181
|
+
info(message, ...args) {
|
|
1112
1182
|
if (this.enabled) {
|
|
1113
|
-
console.info(`[
|
|
1183
|
+
console.info(`[PHANTOM] ${message}`, ...args);
|
|
1114
1184
|
}
|
|
1115
1185
|
}
|
|
1116
|
-
warn(
|
|
1186
|
+
warn(message, ...args) {
|
|
1117
1187
|
if (this.enabled) {
|
|
1118
|
-
console.warn(`[
|
|
1188
|
+
console.warn(`[PHANTOM] ${message}`, ...args);
|
|
1119
1189
|
}
|
|
1120
1190
|
}
|
|
1121
|
-
error(
|
|
1191
|
+
error(message, ...args) {
|
|
1122
1192
|
if (this.enabled) {
|
|
1123
|
-
console.error(`[
|
|
1193
|
+
console.error(`[PHANTOM] ${message}`, ...args);
|
|
1124
1194
|
}
|
|
1125
1195
|
}
|
|
1126
|
-
|
|
1196
|
+
debug(message, ...args) {
|
|
1127
1197
|
if (this.enabled) {
|
|
1128
|
-
console.log(`[
|
|
1198
|
+
console.log(`[PHANTOM] ${message}`, ...args);
|
|
1129
1199
|
}
|
|
1130
1200
|
}
|
|
1131
1201
|
};
|
|
@@ -1158,12 +1228,12 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
|
|
|
1158
1228
|
const redirectUrl = config.authOptions?.redirectUrl || `${config.scheme}://phantom-auth-callback`;
|
|
1159
1229
|
return {
|
|
1160
1230
|
...config,
|
|
1161
|
-
apiBaseUrl: config.apiBaseUrl ||
|
|
1162
|
-
embeddedWalletType: config.embeddedWalletType ||
|
|
1231
|
+
apiBaseUrl: config.apiBaseUrl || import_constants4.DEFAULT_WALLET_API_URL,
|
|
1232
|
+
embeddedWalletType: config.embeddedWalletType || import_constants4.DEFAULT_EMBEDDED_WALLET_TYPE,
|
|
1163
1233
|
authOptions: {
|
|
1164
1234
|
...config.authOptions || {},
|
|
1165
1235
|
redirectUrl,
|
|
1166
|
-
authUrl: config.authOptions?.authUrl ||
|
|
1236
|
+
authUrl: config.authOptions?.authUrl || import_constants4.DEFAULT_AUTH_URL
|
|
1167
1237
|
}
|
|
1168
1238
|
};
|
|
1169
1239
|
}, [config]);
|
|
@@ -1171,7 +1241,11 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
|
|
|
1171
1241
|
const storage = new ExpoSecureStorage();
|
|
1172
1242
|
const urlParamsAccessor = new ExpoURLParamsAccessor();
|
|
1173
1243
|
const logger = new ExpoLogger(debugConfig?.enabled || false);
|
|
1174
|
-
const stamper = config.unstable__auth2Options ? new ExpoAuth2Stamper(`phantom-auth2-${memoizedConfig.appId}
|
|
1244
|
+
const stamper = config.unstable__auth2Options && config.authOptions?.redirectUrl ? new ExpoAuth2Stamper(`phantom-auth2-${memoizedConfig.appId}`, {
|
|
1245
|
+
authApiBaseUrl: config.unstable__auth2Options.authApiBaseUrl,
|
|
1246
|
+
clientId: config.unstable__auth2Options.clientId,
|
|
1247
|
+
redirectUri: config.authOptions.redirectUrl
|
|
1248
|
+
}) : new ReactNativeStamper({
|
|
1175
1249
|
keyPrefix: `phantom-rn-${memoizedConfig.appId}`,
|
|
1176
1250
|
appId: memoizedConfig.appId
|
|
1177
1251
|
});
|
|
@@ -1197,13 +1271,13 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
|
|
|
1197
1271
|
phantomAppProvider: new ReactNativePhantomAppProvider(),
|
|
1198
1272
|
name: platformName,
|
|
1199
1273
|
analyticsHeaders: {
|
|
1200
|
-
[
|
|
1201
|
-
[
|
|
1202
|
-
[
|
|
1203
|
-
[
|
|
1204
|
-
[
|
|
1205
|
-
[
|
|
1206
|
-
[
|
|
1274
|
+
[import_constants4.ANALYTICS_HEADERS.SDK_TYPE]: "react-native",
|
|
1275
|
+
[import_constants4.ANALYTICS_HEADERS.PLATFORM]: "ext-sdk",
|
|
1276
|
+
[import_constants4.ANALYTICS_HEADERS.PLATFORM_VERSION]: `${import_react_native7.Platform.Version}`,
|
|
1277
|
+
[import_constants4.ANALYTICS_HEADERS.CLIENT]: import_react_native7.Platform.OS,
|
|
1278
|
+
[import_constants4.ANALYTICS_HEADERS.APP_ID]: config.appId,
|
|
1279
|
+
[import_constants4.ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
|
|
1280
|
+
[import_constants4.ANALYTICS_HEADERS.SDK_VERSION]: "1.0.6"
|
|
1207
1281
|
// Replaced at build time
|
|
1208
1282
|
}
|
|
1209
1283
|
};
|
|
@@ -1317,7 +1391,7 @@ function useEthereum() {
|
|
|
1317
1391
|
|
|
1318
1392
|
// src/index.ts
|
|
1319
1393
|
var import_client = require("@phantom/client");
|
|
1320
|
-
var
|
|
1394
|
+
var import_constants5 = require("@phantom/constants");
|
|
1321
1395
|
var import_wallet_sdk_ui6 = require("@phantom/wallet-sdk-ui");
|
|
1322
1396
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1323
1397
|
0 && (module.exports = {
|
package/dist/index.mjs
CHANGED
|
@@ -515,7 +515,7 @@ var ExpoAuthProvider = class {
|
|
|
515
515
|
// OAuth session management - defaults to allow refresh unless explicitly clearing after logout
|
|
516
516
|
clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
|
|
517
517
|
allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
|
|
518
|
-
sdk_version: "1.0.
|
|
518
|
+
sdk_version: "1.0.6",
|
|
519
519
|
sdk_type: "react-native",
|
|
520
520
|
platform: Platform.OS
|
|
521
521
|
});
|
|
@@ -594,7 +594,6 @@ var ExpoAuthProvider = class {
|
|
|
594
594
|
import * as WebBrowser2 from "expo-web-browser";
|
|
595
595
|
import {
|
|
596
596
|
createCodeVerifier,
|
|
597
|
-
createSalt,
|
|
598
597
|
exchangeAuthCode,
|
|
599
598
|
Auth2KmsRpcClient,
|
|
600
599
|
createConnectStartUrl
|
|
@@ -621,7 +620,6 @@ var ExpoAuth2AuthProvider = class {
|
|
|
621
620
|
throw new Error("Stamper key pair not found.");
|
|
622
621
|
}
|
|
623
622
|
const codeVerifier = createCodeVerifier();
|
|
624
|
-
const salt = createSalt();
|
|
625
623
|
const url = await createConnectStartUrl({
|
|
626
624
|
keyPair,
|
|
627
625
|
connectLoginUrl: this.auth2ProviderOptions.connectLoginUrl,
|
|
@@ -630,7 +628,8 @@ var ExpoAuth2AuthProvider = class {
|
|
|
630
628
|
sessionId: options.sessionId,
|
|
631
629
|
provider: options.provider,
|
|
632
630
|
codeVerifier,
|
|
633
|
-
salt
|
|
631
|
+
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
632
|
+
salt: ""
|
|
634
633
|
});
|
|
635
634
|
await WebBrowser2.warmUpAsync();
|
|
636
635
|
let result;
|
|
@@ -656,15 +655,14 @@ var ExpoAuth2AuthProvider = class {
|
|
|
656
655
|
if (!code) {
|
|
657
656
|
throw new Error("Auth2 callback missing authorization code");
|
|
658
657
|
}
|
|
659
|
-
const { idToken, bearerToken, authUserId, expiresInMs } = await exchangeAuthCode({
|
|
658
|
+
const { idToken, bearerToken, authUserId, expiresInMs, refreshToken } = await exchangeAuthCode({
|
|
660
659
|
authApiBaseUrl: this.auth2ProviderOptions.authApiBaseUrl,
|
|
661
660
|
clientId: this.auth2ProviderOptions.clientId,
|
|
662
661
|
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
663
662
|
code,
|
|
664
663
|
codeVerifier
|
|
665
664
|
});
|
|
666
|
-
this.stamper.idToken
|
|
667
|
-
this.stamper.salt = salt;
|
|
665
|
+
await this.stamper.setTokens({ idToken, bearerToken, refreshToken, expiresInMs });
|
|
668
666
|
const { organizationId, walletId } = await this.kms.discoverOrganizationAndWalletId(bearerToken, authUserId);
|
|
669
667
|
return {
|
|
670
668
|
walletId,
|
|
@@ -685,25 +683,47 @@ import bs58 from "bs58";
|
|
|
685
683
|
import { Buffer } from "buffer";
|
|
686
684
|
import { base64urlEncode } from "@phantom/base64url";
|
|
687
685
|
import { Algorithm } from "@phantom/sdk-types";
|
|
686
|
+
import { refreshToken as refreshTokenRequest } from "@phantom/auth2";
|
|
687
|
+
import { TOKEN_REFRESH_BUFFER_MS } from "@phantom/constants";
|
|
688
688
|
var ExpoAuth2Stamper = class {
|
|
689
689
|
/**
|
|
690
690
|
* @param storageKey - expo-secure-store key used to persist the P-256 private key.
|
|
691
691
|
* Use a unique key per app, e.g. `phantom-auth2-<appId>`.
|
|
692
|
+
* @param refreshConfig - When provided, the stamper will automatically refresh
|
|
693
|
+
* the id_token using the refresh_token before it expires.
|
|
692
694
|
*/
|
|
693
|
-
constructor(storageKey) {
|
|
695
|
+
constructor(storageKey, refreshConfig) {
|
|
694
696
|
this.storageKey = storageKey;
|
|
695
|
-
this.
|
|
696
|
-
this.
|
|
697
|
+
this.refreshConfig = refreshConfig;
|
|
698
|
+
this._keyPair = null;
|
|
697
699
|
this._keyInfo = null;
|
|
700
|
+
this._idToken = null;
|
|
701
|
+
this._bearerToken = null;
|
|
702
|
+
this._refreshToken = null;
|
|
703
|
+
this._tokenExpiresAt = null;
|
|
698
704
|
this.algorithm = Algorithm.secp256r1;
|
|
699
705
|
this.type = "OIDC";
|
|
700
706
|
}
|
|
701
707
|
async init() {
|
|
702
708
|
const stored = await this.loadRecord();
|
|
703
709
|
if (stored) {
|
|
704
|
-
this.
|
|
705
|
-
|
|
710
|
+
this._keyPair = {
|
|
711
|
+
privateKey: await this.importPrivateKey(stored.privateKeyPkcs8),
|
|
712
|
+
publicKey: await this.importPublicKeyFromBase58(stored.keyInfo.publicKey)
|
|
713
|
+
};
|
|
706
714
|
this._keyInfo = stored.keyInfo;
|
|
715
|
+
if (stored.idToken) {
|
|
716
|
+
this._idToken = stored.idToken;
|
|
717
|
+
}
|
|
718
|
+
if (stored.bearerToken) {
|
|
719
|
+
this._bearerToken = stored.bearerToken;
|
|
720
|
+
}
|
|
721
|
+
if (stored.refreshToken) {
|
|
722
|
+
this._refreshToken = stored.refreshToken;
|
|
723
|
+
}
|
|
724
|
+
if (stored.tokenExpiresAt) {
|
|
725
|
+
this._tokenExpiresAt = stored.tokenExpiresAt;
|
|
726
|
+
}
|
|
707
727
|
return this._keyInfo;
|
|
708
728
|
}
|
|
709
729
|
return this.generateAndStore();
|
|
@@ -712,45 +732,96 @@ var ExpoAuth2Stamper = class {
|
|
|
712
732
|
return this._keyInfo;
|
|
713
733
|
}
|
|
714
734
|
getCryptoKeyPair() {
|
|
715
|
-
|
|
735
|
+
return this._keyPair;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Returns the current token state (refreshing proactively if near expiry),
|
|
739
|
+
* or null if no token has been set yet.
|
|
740
|
+
*/
|
|
741
|
+
async getTokens() {
|
|
742
|
+
if (this.refreshConfig && this._refreshToken && this._tokenExpiresAt !== null && Date.now() >= this._tokenExpiresAt - TOKEN_REFRESH_BUFFER_MS) {
|
|
743
|
+
const refreshed = await refreshTokenRequest({
|
|
744
|
+
authApiBaseUrl: this.refreshConfig.authApiBaseUrl,
|
|
745
|
+
clientId: this.refreshConfig.clientId,
|
|
746
|
+
redirectUri: this.refreshConfig.redirectUri,
|
|
747
|
+
refreshToken: this._refreshToken
|
|
748
|
+
});
|
|
749
|
+
await this.setTokens(refreshed);
|
|
750
|
+
}
|
|
751
|
+
if (!this._idToken || !this._bearerToken) {
|
|
716
752
|
return null;
|
|
717
|
-
|
|
753
|
+
}
|
|
754
|
+
return {
|
|
755
|
+
idToken: this._idToken,
|
|
756
|
+
bearerToken: this._bearerToken,
|
|
757
|
+
refreshToken: this._refreshToken ?? void 0
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Arms the stamper with the OIDC token data for subsequent KMS stamp() calls.
|
|
762
|
+
*
|
|
763
|
+
* Persists the tokens to SecureStore alongside the key pair so that
|
|
764
|
+
* auto-connect can restore them on the next app launch without a new login.
|
|
765
|
+
*
|
|
766
|
+
* @param refreshToken - When provided alongside a `refreshConfig`, enables
|
|
767
|
+
* silent token refresh before the token expires.
|
|
768
|
+
* @param expiresInMs - Token lifetime in milliseconds (from `expires_in * 1000`).
|
|
769
|
+
* Used to compute the absolute expiry time for proactive refresh.
|
|
770
|
+
*/
|
|
771
|
+
async setTokens({
|
|
772
|
+
idToken,
|
|
773
|
+
bearerToken,
|
|
774
|
+
refreshToken,
|
|
775
|
+
expiresInMs
|
|
776
|
+
}) {
|
|
777
|
+
this._idToken = idToken;
|
|
778
|
+
this._bearerToken = bearerToken;
|
|
779
|
+
this._refreshToken = refreshToken ?? null;
|
|
780
|
+
this._tokenExpiresAt = expiresInMs != null ? Date.now() + expiresInMs : null;
|
|
781
|
+
const existing = await this.loadRecord();
|
|
782
|
+
if (existing) {
|
|
783
|
+
await this.storeRecord({
|
|
784
|
+
...existing,
|
|
785
|
+
idToken,
|
|
786
|
+
bearerToken,
|
|
787
|
+
refreshToken,
|
|
788
|
+
tokenExpiresAt: this._tokenExpiresAt ?? void 0
|
|
789
|
+
});
|
|
790
|
+
}
|
|
718
791
|
}
|
|
719
792
|
async stamp(params) {
|
|
720
|
-
if (!this.
|
|
793
|
+
if (!this._keyPair || !this._keyInfo || this._idToken === null) {
|
|
721
794
|
throw new Error("ExpoAuth2Stamper not initialized. Call init() first.");
|
|
722
795
|
}
|
|
723
796
|
const signatureRaw = await crypto.subtle.sign(
|
|
724
797
|
{ name: "ECDSA", hash: "SHA-256" },
|
|
725
|
-
this.privateKey,
|
|
798
|
+
this._keyPair.privateKey,
|
|
726
799
|
new Uint8Array(params.data)
|
|
727
800
|
);
|
|
728
801
|
const rawPublicKey = bs58.decode(this._keyInfo.publicKey);
|
|
729
|
-
if (this.idToken === void 0 || this.salt === void 0) {
|
|
730
|
-
throw new Error("ExpoAuth2Stamper not initialized with idToken or salt.");
|
|
731
|
-
}
|
|
732
802
|
const stampData = {
|
|
733
|
-
kind:
|
|
734
|
-
idToken: this.
|
|
803
|
+
kind: this.type,
|
|
804
|
+
idToken: this._idToken,
|
|
735
805
|
publicKey: base64urlEncode(rawPublicKey),
|
|
736
|
-
algorithm:
|
|
737
|
-
salt
|
|
806
|
+
algorithm: this.algorithm,
|
|
807
|
+
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
808
|
+
salt: "",
|
|
738
809
|
signature: base64urlEncode(new Uint8Array(signatureRaw))
|
|
739
810
|
};
|
|
740
811
|
return base64urlEncode(new TextEncoder().encode(JSON.stringify(stampData)));
|
|
741
812
|
}
|
|
742
813
|
async resetKeyPair() {
|
|
743
|
-
await this.
|
|
744
|
-
this.privateKey = null;
|
|
745
|
-
this.publicKey = null;
|
|
746
|
-
this._keyInfo = null;
|
|
814
|
+
await this.clear();
|
|
747
815
|
return this.generateAndStore();
|
|
748
816
|
}
|
|
749
817
|
async clear() {
|
|
750
818
|
await this.clearStoredRecord();
|
|
751
|
-
this.
|
|
752
|
-
this.publicKey = null;
|
|
819
|
+
this._keyPair = null;
|
|
753
820
|
this._keyInfo = null;
|
|
821
|
+
this._idToken = null;
|
|
822
|
+
this._bearerToken = null;
|
|
823
|
+
this._refreshToken = null;
|
|
824
|
+
this._tokenExpiresAt = null;
|
|
754
825
|
}
|
|
755
826
|
// Auth2 doesn't use key rotation; minimal no-op implementations.
|
|
756
827
|
async rotateKeyPair() {
|
|
@@ -775,16 +846,11 @@ var ExpoAuth2Stamper = class {
|
|
|
775
846
|
const publicKeyBase58 = bs58.encode(rawPublicKey);
|
|
776
847
|
const keyIdBuffer = await crypto.subtle.digest("SHA-256", rawPublicKey.buffer);
|
|
777
848
|
const keyId = base64urlEncode(new Uint8Array(keyIdBuffer)).substring(0, 16);
|
|
849
|
+
this._keyPair = keyPair;
|
|
778
850
|
this._keyInfo = { keyId, publicKey: publicKeyBase58, createdAt: Date.now() };
|
|
779
851
|
const pkcs8Buffer = await crypto.subtle.exportKey("pkcs8", keyPair.privateKey);
|
|
780
852
|
const privateKeyPkcs8 = Buffer.from(pkcs8Buffer).toString("base64");
|
|
781
|
-
await
|
|
782
|
-
this.storageKey,
|
|
783
|
-
JSON.stringify({ privateKeyPkcs8, keyInfo: this._keyInfo }),
|
|
784
|
-
{ requireAuthentication: false }
|
|
785
|
-
);
|
|
786
|
-
this.privateKey = await this.importPrivateKey(privateKeyPkcs8);
|
|
787
|
-
this.publicKey = keyPair.publicKey;
|
|
853
|
+
await this.storeRecord({ privateKeyPkcs8, keyInfo: this._keyInfo });
|
|
788
854
|
return this._keyInfo;
|
|
789
855
|
}
|
|
790
856
|
async importPublicKeyFromBase58(base58PublicKey) {
|
|
@@ -817,6 +883,9 @@ var ExpoAuth2Stamper = class {
|
|
|
817
883
|
return null;
|
|
818
884
|
}
|
|
819
885
|
}
|
|
886
|
+
async storeRecord(record) {
|
|
887
|
+
await SecureStore2.setItemAsync(this.storageKey, JSON.stringify(record), { requireAuthentication: false });
|
|
888
|
+
}
|
|
820
889
|
async clearStoredRecord() {
|
|
821
890
|
try {
|
|
822
891
|
await SecureStore2.deleteItemAsync(this.storageKey);
|
|
@@ -1072,24 +1141,24 @@ var ExpoLogger = class {
|
|
|
1072
1141
|
constructor(enabled = false) {
|
|
1073
1142
|
this.enabled = enabled;
|
|
1074
1143
|
}
|
|
1075
|
-
info(
|
|
1144
|
+
info(message, ...args) {
|
|
1076
1145
|
if (this.enabled) {
|
|
1077
|
-
console.info(`[
|
|
1146
|
+
console.info(`[PHANTOM] ${message}`, ...args);
|
|
1078
1147
|
}
|
|
1079
1148
|
}
|
|
1080
|
-
warn(
|
|
1149
|
+
warn(message, ...args) {
|
|
1081
1150
|
if (this.enabled) {
|
|
1082
|
-
console.warn(`[
|
|
1151
|
+
console.warn(`[PHANTOM] ${message}`, ...args);
|
|
1083
1152
|
}
|
|
1084
1153
|
}
|
|
1085
|
-
error(
|
|
1154
|
+
error(message, ...args) {
|
|
1086
1155
|
if (this.enabled) {
|
|
1087
|
-
console.error(`[
|
|
1156
|
+
console.error(`[PHANTOM] ${message}`, ...args);
|
|
1088
1157
|
}
|
|
1089
1158
|
}
|
|
1090
|
-
|
|
1159
|
+
debug(message, ...args) {
|
|
1091
1160
|
if (this.enabled) {
|
|
1092
|
-
console.log(`[
|
|
1161
|
+
console.log(`[PHANTOM] ${message}`, ...args);
|
|
1093
1162
|
}
|
|
1094
1163
|
}
|
|
1095
1164
|
};
|
|
@@ -1135,7 +1204,11 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
|
|
|
1135
1204
|
const storage = new ExpoSecureStorage();
|
|
1136
1205
|
const urlParamsAccessor = new ExpoURLParamsAccessor();
|
|
1137
1206
|
const logger = new ExpoLogger(debugConfig?.enabled || false);
|
|
1138
|
-
const stamper = config.unstable__auth2Options ? new ExpoAuth2Stamper(`phantom-auth2-${memoizedConfig.appId}
|
|
1207
|
+
const stamper = config.unstable__auth2Options && config.authOptions?.redirectUrl ? new ExpoAuth2Stamper(`phantom-auth2-${memoizedConfig.appId}`, {
|
|
1208
|
+
authApiBaseUrl: config.unstable__auth2Options.authApiBaseUrl,
|
|
1209
|
+
clientId: config.unstable__auth2Options.clientId,
|
|
1210
|
+
redirectUri: config.authOptions.redirectUrl
|
|
1211
|
+
}) : new ReactNativeStamper({
|
|
1139
1212
|
keyPrefix: `phantom-rn-${memoizedConfig.appId}`,
|
|
1140
1213
|
appId: memoizedConfig.appId
|
|
1141
1214
|
});
|
|
@@ -1167,7 +1240,7 @@ function PhantomProvider({ children, config, debugConfig, theme, appIcon, appNam
|
|
|
1167
1240
|
[ANALYTICS_HEADERS.CLIENT]: Platform2.OS,
|
|
1168
1241
|
[ANALYTICS_HEADERS.APP_ID]: config.appId,
|
|
1169
1242
|
[ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
|
|
1170
|
-
[ANALYTICS_HEADERS.SDK_VERSION]: "1.0.
|
|
1243
|
+
[ANALYTICS_HEADERS.SDK_VERSION]: "1.0.6"
|
|
1171
1244
|
// Replaced at build time
|
|
1172
1245
|
}
|
|
1173
1246
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phantom/react-native-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Phantom Wallet SDK for React Native and Expo applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -45,16 +45,16 @@
|
|
|
45
45
|
"directory": "packages/react-native-sdk"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@phantom/api-key-stamper": "^1.0.
|
|
49
|
-
"@phantom/auth2": "^1.0.
|
|
50
|
-
"@phantom/base64url": "^1.0.
|
|
51
|
-
"@phantom/chain-interfaces": "^1.0.
|
|
52
|
-
"@phantom/client": "^1.0.
|
|
53
|
-
"@phantom/constants": "^1.0.
|
|
54
|
-
"@phantom/crypto": "^1.0.
|
|
55
|
-
"@phantom/embedded-provider-core": "^1.0.
|
|
56
|
-
"@phantom/sdk-types": "^1.0.
|
|
57
|
-
"@phantom/wallet-sdk-ui": "^1.0.
|
|
48
|
+
"@phantom/api-key-stamper": "^1.0.6",
|
|
49
|
+
"@phantom/auth2": "^1.0.2",
|
|
50
|
+
"@phantom/base64url": "^1.0.6",
|
|
51
|
+
"@phantom/chain-interfaces": "^1.0.6",
|
|
52
|
+
"@phantom/client": "^1.0.6",
|
|
53
|
+
"@phantom/constants": "^1.0.6",
|
|
54
|
+
"@phantom/crypto": "^1.0.6",
|
|
55
|
+
"@phantom/embedded-provider-core": "^1.0.6",
|
|
56
|
+
"@phantom/sdk-types": "^1.0.6",
|
|
57
|
+
"@phantom/wallet-sdk-ui": "^1.0.6",
|
|
58
58
|
"@types/bs58": "^5.0.0",
|
|
59
59
|
"bs58": "^6.0.0",
|
|
60
60
|
"buffer": "^6.0.3"
|