@phantom/browser-sdk 1.0.3 → 1.0.5
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.d.ts +5 -0
- package/dist/index.js +317 -23
- package/dist/index.mjs +322 -23
- package/package.json +11 -10
package/dist/index.d.ts
CHANGED
|
@@ -119,6 +119,11 @@ type BrowserSDKConfig = Prettify<Omit<EmbeddedProviderConfig, "authOptions" | "a
|
|
|
119
119
|
authUrl?: string;
|
|
120
120
|
redirectUrl?: string;
|
|
121
121
|
};
|
|
122
|
+
/** When also provided, the Auth2 PKCE flow is used instead of the legacy Phantom Connect flow. */
|
|
123
|
+
unstable__auth2Options?: {
|
|
124
|
+
authApiBaseUrl: string;
|
|
125
|
+
clientId: string;
|
|
126
|
+
};
|
|
122
127
|
}>;
|
|
123
128
|
type Prettify<T> = {
|
|
124
129
|
[K in keyof T]: T[K];
|
package/dist/index.js
CHANGED
|
@@ -1805,16 +1805,11 @@ var InjectedProvider = class {
|
|
|
1805
1805
|
walletName: walletInfo.name
|
|
1806
1806
|
});
|
|
1807
1807
|
} catch (err) {
|
|
1808
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana,
|
|
1808
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana, continuing with other chains", {
|
|
1809
1809
|
error: err,
|
|
1810
1810
|
walletId: this.selectedWalletId,
|
|
1811
1811
|
walletName: walletInfo.name
|
|
1812
1812
|
});
|
|
1813
|
-
this.emit("connect_error", {
|
|
1814
|
-
error: err instanceof Error ? err.message : "Failed to connect",
|
|
1815
|
-
source: options?.skipEventListeners ? "auto-connect" : "manual-connect"
|
|
1816
|
-
});
|
|
1817
|
-
throw err;
|
|
1818
1813
|
}
|
|
1819
1814
|
}
|
|
1820
1815
|
if (this.addressTypes.includes(import_client4.AddressType.ethereum) && walletInfo.providers?.ethereum) {
|
|
@@ -1844,16 +1839,11 @@ var InjectedProvider = class {
|
|
|
1844
1839
|
});
|
|
1845
1840
|
}
|
|
1846
1841
|
} catch (err) {
|
|
1847
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum,
|
|
1842
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum, continuing with other chains", {
|
|
1848
1843
|
error: err,
|
|
1849
1844
|
walletId: this.selectedWalletId,
|
|
1850
1845
|
walletName: walletInfo.name
|
|
1851
1846
|
});
|
|
1852
|
-
this.emit("connect_error", {
|
|
1853
|
-
error: err instanceof Error ? err.message : "Failed to connect",
|
|
1854
|
-
source: options?.skipEventListeners ? "auto-connect" : "manual-connect"
|
|
1855
|
-
});
|
|
1856
|
-
throw err;
|
|
1857
1847
|
}
|
|
1858
1848
|
}
|
|
1859
1849
|
return connectedAddresses;
|
|
@@ -2774,7 +2764,7 @@ var BrowserAuthProvider = class {
|
|
|
2774
2764
|
// OAuth session management - defaults to allow refresh unless explicitly clearing after logout
|
|
2775
2765
|
clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
|
|
2776
2766
|
allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
|
|
2777
|
-
sdk_version: "1.0.
|
|
2767
|
+
sdk_version: "1.0.5",
|
|
2778
2768
|
sdk_type: "browser",
|
|
2779
2769
|
platform: detectBrowser().name,
|
|
2780
2770
|
algorithm: phantomOptions.algorithm || import_constants3.DEFAULT_AUTHENTICATOR_ALGORITHM
|
|
@@ -2805,7 +2795,7 @@ var BrowserAuthProvider = class {
|
|
|
2805
2795
|
resolve();
|
|
2806
2796
|
});
|
|
2807
2797
|
}
|
|
2808
|
-
resumeAuthFromRedirect(provider) {
|
|
2798
|
+
async resumeAuthFromRedirect(provider) {
|
|
2809
2799
|
try {
|
|
2810
2800
|
const walletId = this.urlParamsAccessor.getParam("wallet_id");
|
|
2811
2801
|
const sessionId = this.urlParamsAccessor.getParam("session_id");
|
|
@@ -2880,14 +2870,14 @@ var BrowserAuthProvider = class {
|
|
|
2880
2870
|
}
|
|
2881
2871
|
);
|
|
2882
2872
|
}
|
|
2883
|
-
return {
|
|
2873
|
+
return Promise.resolve({
|
|
2884
2874
|
walletId,
|
|
2885
2875
|
organizationId,
|
|
2886
2876
|
accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
|
|
2887
2877
|
expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
|
|
2888
2878
|
authUserId: authUserId || void 0,
|
|
2889
2879
|
provider
|
|
2890
|
-
};
|
|
2880
|
+
});
|
|
2891
2881
|
} catch (error) {
|
|
2892
2882
|
sessionStorage.removeItem("phantom-auth-context");
|
|
2893
2883
|
throw error;
|
|
@@ -2895,6 +2885,294 @@ var BrowserAuthProvider = class {
|
|
|
2895
2885
|
}
|
|
2896
2886
|
};
|
|
2897
2887
|
|
|
2888
|
+
// src/providers/embedded/adapters/Auth2AuthProvider.ts
|
|
2889
|
+
var import_auth2 = require("@phantom/auth2");
|
|
2890
|
+
var Auth2AuthProvider = class {
|
|
2891
|
+
constructor(stamper, storage, urlParamsAccessor, auth2ProviderOptions, kmsClientOptions) {
|
|
2892
|
+
this.stamper = stamper;
|
|
2893
|
+
this.storage = storage;
|
|
2894
|
+
this.urlParamsAccessor = urlParamsAccessor;
|
|
2895
|
+
this.auth2ProviderOptions = auth2ProviderOptions;
|
|
2896
|
+
this.kms = new import_auth2.Auth2KmsRpcClient(stamper, kmsClientOptions);
|
|
2897
|
+
}
|
|
2898
|
+
/** Redirect the browser. Extracted as a static method so tests can spy on it. */
|
|
2899
|
+
static navigate(url) {
|
|
2900
|
+
window.location.href = url;
|
|
2901
|
+
}
|
|
2902
|
+
/**
|
|
2903
|
+
* Builds the Auth2 /login/start URL and redirects the browser.
|
|
2904
|
+
*
|
|
2905
|
+
* Called by EmbeddedProvider.handleRedirectAuth() after the stamper has
|
|
2906
|
+
* already been initialized and a pending Session has been saved to storage.
|
|
2907
|
+
* We store the PKCE code_verifier into that session so it survives the page
|
|
2908
|
+
* redirect without ever touching sessionStorage.
|
|
2909
|
+
*/
|
|
2910
|
+
async authenticate(options) {
|
|
2911
|
+
if (!this.stamper.getKeyInfo()) {
|
|
2912
|
+
await this.stamper.init();
|
|
2913
|
+
}
|
|
2914
|
+
const keyPair = this.stamper.getCryptoKeyPair();
|
|
2915
|
+
if (!keyPair) {
|
|
2916
|
+
throw new Error("Stamper key pair not found.");
|
|
2917
|
+
}
|
|
2918
|
+
const codeVerifier = (0, import_auth2.createCodeVerifier)();
|
|
2919
|
+
const session = await this.storage.getSession();
|
|
2920
|
+
if (!session) {
|
|
2921
|
+
throw new Error("Session not found.");
|
|
2922
|
+
}
|
|
2923
|
+
await this.storage.saveSession({ ...session, pkceCodeVerifier: codeVerifier });
|
|
2924
|
+
const url = await (0, import_auth2.createConnectStartUrl)({
|
|
2925
|
+
keyPair,
|
|
2926
|
+
connectLoginUrl: this.auth2ProviderOptions.connectLoginUrl,
|
|
2927
|
+
clientId: this.auth2ProviderOptions.clientId,
|
|
2928
|
+
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
2929
|
+
sessionId: options.sessionId,
|
|
2930
|
+
provider: options.provider,
|
|
2931
|
+
codeVerifier,
|
|
2932
|
+
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
2933
|
+
salt: ""
|
|
2934
|
+
});
|
|
2935
|
+
Auth2AuthProvider.navigate(url);
|
|
2936
|
+
}
|
|
2937
|
+
/**
|
|
2938
|
+
* Processes the Auth2 callback after the browser returns from /login/start.
|
|
2939
|
+
*
|
|
2940
|
+
* Exchanges the authorization code for tokens, discovers the organization
|
|
2941
|
+
* and wallet via KMS RPC, then returns a completed AuthResult.
|
|
2942
|
+
*/
|
|
2943
|
+
async resumeAuthFromRedirect(provider) {
|
|
2944
|
+
const code = this.urlParamsAccessor.getParam("code");
|
|
2945
|
+
if (!code) {
|
|
2946
|
+
return null;
|
|
2947
|
+
}
|
|
2948
|
+
if (!this.stamper.getKeyInfo()) {
|
|
2949
|
+
await this.stamper.init();
|
|
2950
|
+
}
|
|
2951
|
+
const session = await this.storage.getSession();
|
|
2952
|
+
if (!session) {
|
|
2953
|
+
throw new Error("Session not found.");
|
|
2954
|
+
}
|
|
2955
|
+
const codeVerifier = session?.pkceCodeVerifier;
|
|
2956
|
+
if (!codeVerifier) {
|
|
2957
|
+
return null;
|
|
2958
|
+
}
|
|
2959
|
+
const state = this.urlParamsAccessor.getParam("state");
|
|
2960
|
+
if (!state || state !== session.sessionId) {
|
|
2961
|
+
throw new Error("Missing or invalid Auth2 state parameter \u2014 possible CSRF attack.");
|
|
2962
|
+
}
|
|
2963
|
+
const error = this.urlParamsAccessor.getParam("error");
|
|
2964
|
+
if (error) {
|
|
2965
|
+
const description = this.urlParamsAccessor.getParam("error_description");
|
|
2966
|
+
throw new Error(`Auth2 callback error: ${description ?? error}`);
|
|
2967
|
+
}
|
|
2968
|
+
const { idToken, bearerToken, authUserId, expiresInMs } = await (0, import_auth2.exchangeAuthCode)({
|
|
2969
|
+
authApiBaseUrl: this.auth2ProviderOptions.authApiBaseUrl,
|
|
2970
|
+
clientId: this.auth2ProviderOptions.clientId,
|
|
2971
|
+
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
2972
|
+
code,
|
|
2973
|
+
codeVerifier
|
|
2974
|
+
});
|
|
2975
|
+
await this.stamper.setIdToken(idToken);
|
|
2976
|
+
await this.storage.saveSession({
|
|
2977
|
+
...session,
|
|
2978
|
+
status: "completed",
|
|
2979
|
+
bearerToken,
|
|
2980
|
+
authUserId,
|
|
2981
|
+
pkceCodeVerifier: void 0
|
|
2982
|
+
// no longer needed after code exchange
|
|
2983
|
+
});
|
|
2984
|
+
const { organizationId, walletId } = await this.kms.discoverOrganizationAndWalletId(bearerToken, authUserId);
|
|
2985
|
+
return {
|
|
2986
|
+
walletId,
|
|
2987
|
+
organizationId,
|
|
2988
|
+
provider,
|
|
2989
|
+
accountDerivationIndex: 0,
|
|
2990
|
+
// discoverWalletId uses derivation index of 0.
|
|
2991
|
+
expiresInMs,
|
|
2992
|
+
authUserId,
|
|
2993
|
+
bearerToken
|
|
2994
|
+
};
|
|
2995
|
+
}
|
|
2996
|
+
};
|
|
2997
|
+
|
|
2998
|
+
// src/providers/embedded/adapters/Auth2Stamper.ts
|
|
2999
|
+
var import_bs582 = __toESM(require("bs58"));
|
|
3000
|
+
var import_base64url = require("@phantom/base64url");
|
|
3001
|
+
var import_sdk_types = require("@phantom/sdk-types");
|
|
3002
|
+
var STORE_NAME = "crypto-keys";
|
|
3003
|
+
var ACTIVE_KEY = "auth2-p256-signing-key";
|
|
3004
|
+
var Auth2Stamper = class {
|
|
3005
|
+
/**
|
|
3006
|
+
* @param dbName - IndexedDB database name (use a unique name per app to
|
|
3007
|
+
* avoid key collisions with other stampers, e.g. `phantom-auth2-<appId>`).
|
|
3008
|
+
*/
|
|
3009
|
+
constructor(dbName) {
|
|
3010
|
+
this.dbName = dbName;
|
|
3011
|
+
this.db = null;
|
|
3012
|
+
this._keyPair = null;
|
|
3013
|
+
this._keyInfo = null;
|
|
3014
|
+
this._idToken = null;
|
|
3015
|
+
this.algorithm = import_sdk_types.Algorithm.secp256r1;
|
|
3016
|
+
this.type = "OIDC";
|
|
3017
|
+
}
|
|
3018
|
+
async init() {
|
|
3019
|
+
await this.openDB();
|
|
3020
|
+
const stored = await this.loadRecord();
|
|
3021
|
+
if (stored) {
|
|
3022
|
+
this._keyPair = stored.keyPair;
|
|
3023
|
+
this._keyInfo = stored.keyInfo;
|
|
3024
|
+
if (stored.idToken) {
|
|
3025
|
+
this._idToken = stored.idToken;
|
|
3026
|
+
}
|
|
3027
|
+
return this._keyInfo;
|
|
3028
|
+
}
|
|
3029
|
+
return this.generateAndStore();
|
|
3030
|
+
}
|
|
3031
|
+
getKeyInfo() {
|
|
3032
|
+
return this._keyInfo;
|
|
3033
|
+
}
|
|
3034
|
+
getCryptoKeyPair() {
|
|
3035
|
+
return this._keyPair;
|
|
3036
|
+
}
|
|
3037
|
+
/**
|
|
3038
|
+
* Arms the stamper with the OIDC id token for subsequent KMS stamp() calls.
|
|
3039
|
+
*
|
|
3040
|
+
* Persists the token to IndexedDB alongside the key pair so that
|
|
3041
|
+
* auto-connect can restore it on the next page load without a new login.
|
|
3042
|
+
*/
|
|
3043
|
+
async setIdToken(idToken) {
|
|
3044
|
+
if (!this.db) {
|
|
3045
|
+
await this.openDB();
|
|
3046
|
+
}
|
|
3047
|
+
this._idToken = idToken;
|
|
3048
|
+
const existing = await this.loadRecord();
|
|
3049
|
+
if (existing) {
|
|
3050
|
+
await this.storeRecord({ ...existing, idToken });
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
async stamp(params) {
|
|
3054
|
+
if (!this._keyPair || !this._keyInfo || this._idToken === null) {
|
|
3055
|
+
throw new Error("Auth2Stamper not initialized. Call init() first.");
|
|
3056
|
+
}
|
|
3057
|
+
const signatureRaw = await crypto.subtle.sign(
|
|
3058
|
+
{ name: "ECDSA", hash: "SHA-256" },
|
|
3059
|
+
this._keyPair.privateKey,
|
|
3060
|
+
new Uint8Array(params.data)
|
|
3061
|
+
);
|
|
3062
|
+
const rawPublicKey = import_bs582.default.decode(this._keyInfo.publicKey);
|
|
3063
|
+
const stampData = {
|
|
3064
|
+
kind: this.type,
|
|
3065
|
+
idToken: this._idToken,
|
|
3066
|
+
publicKey: (0, import_base64url.base64urlEncode)(rawPublicKey),
|
|
3067
|
+
algorithm: this.algorithm,
|
|
3068
|
+
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
3069
|
+
salt: "",
|
|
3070
|
+
signature: (0, import_base64url.base64urlEncode)(new Uint8Array(signatureRaw))
|
|
3071
|
+
};
|
|
3072
|
+
return (0, import_base64url.base64urlEncode)(new TextEncoder().encode(JSON.stringify(stampData)));
|
|
3073
|
+
}
|
|
3074
|
+
async resetKeyPair() {
|
|
3075
|
+
await this.clear();
|
|
3076
|
+
return this.generateAndStore();
|
|
3077
|
+
}
|
|
3078
|
+
async clear() {
|
|
3079
|
+
await this.clearStoredRecord();
|
|
3080
|
+
this._keyPair = null;
|
|
3081
|
+
this._keyInfo = null;
|
|
3082
|
+
this._idToken = null;
|
|
3083
|
+
}
|
|
3084
|
+
// Auth2 doesn't use key rotation; provide minimal no-op implementations.
|
|
3085
|
+
async rotateKeyPair() {
|
|
3086
|
+
return this.init();
|
|
3087
|
+
}
|
|
3088
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
3089
|
+
async commitRotation(authenticatorId) {
|
|
3090
|
+
if (this._keyInfo) {
|
|
3091
|
+
this._keyInfo.authenticatorId = authenticatorId;
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
async rollbackRotation() {
|
|
3095
|
+
}
|
|
3096
|
+
async generateAndStore() {
|
|
3097
|
+
const keyPair = await crypto.subtle.generateKey(
|
|
3098
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
3099
|
+
false,
|
|
3100
|
+
// non-extractable — private key never leaves Web Crypto
|
|
3101
|
+
["sign", "verify"]
|
|
3102
|
+
);
|
|
3103
|
+
const rawPublicKey = new Uint8Array(await crypto.subtle.exportKey("raw", keyPair.publicKey));
|
|
3104
|
+
const publicKeyBase58 = import_bs582.default.encode(rawPublicKey);
|
|
3105
|
+
const keyIdBuffer = await crypto.subtle.digest("SHA-256", rawPublicKey.buffer);
|
|
3106
|
+
const keyId = (0, import_base64url.base64urlEncode)(new Uint8Array(keyIdBuffer)).substring(0, 16);
|
|
3107
|
+
this._keyPair = keyPair;
|
|
3108
|
+
this._keyInfo = {
|
|
3109
|
+
keyId,
|
|
3110
|
+
publicKey: publicKeyBase58,
|
|
3111
|
+
createdAt: Date.now()
|
|
3112
|
+
};
|
|
3113
|
+
await this.storeRecord({ keyPair, keyInfo: this._keyInfo });
|
|
3114
|
+
return this._keyInfo;
|
|
3115
|
+
}
|
|
3116
|
+
async openDB() {
|
|
3117
|
+
return new Promise((resolve, reject) => {
|
|
3118
|
+
const request = indexedDB.open(this.dbName, 1);
|
|
3119
|
+
request.onsuccess = () => {
|
|
3120
|
+
this.db = request.result;
|
|
3121
|
+
resolve();
|
|
3122
|
+
};
|
|
3123
|
+
request.onerror = () => reject(request.error);
|
|
3124
|
+
request.onupgradeneeded = (event) => {
|
|
3125
|
+
const db = event.target.result;
|
|
3126
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
3127
|
+
db.createObjectStore(STORE_NAME);
|
|
3128
|
+
}
|
|
3129
|
+
};
|
|
3130
|
+
});
|
|
3131
|
+
}
|
|
3132
|
+
async loadRecord() {
|
|
3133
|
+
return new Promise((resolve, reject) => {
|
|
3134
|
+
if (!this.db) {
|
|
3135
|
+
throw new Error("Database not initialized");
|
|
3136
|
+
}
|
|
3137
|
+
const request = this.db.transaction([STORE_NAME], "readonly").objectStore(STORE_NAME).get(ACTIVE_KEY);
|
|
3138
|
+
request.onsuccess = () => {
|
|
3139
|
+
resolve(request.result ?? null);
|
|
3140
|
+
};
|
|
3141
|
+
request.onerror = () => {
|
|
3142
|
+
reject(request.error);
|
|
3143
|
+
};
|
|
3144
|
+
});
|
|
3145
|
+
}
|
|
3146
|
+
async storeRecord(record) {
|
|
3147
|
+
return new Promise((resolve, reject) => {
|
|
3148
|
+
if (!this.db) {
|
|
3149
|
+
throw new Error("Database not initialized");
|
|
3150
|
+
}
|
|
3151
|
+
const request = this.db.transaction([STORE_NAME], "readwrite").objectStore(STORE_NAME).put(record, ACTIVE_KEY);
|
|
3152
|
+
request.onsuccess = () => {
|
|
3153
|
+
resolve();
|
|
3154
|
+
};
|
|
3155
|
+
request.onerror = () => {
|
|
3156
|
+
reject(request.error);
|
|
3157
|
+
};
|
|
3158
|
+
});
|
|
3159
|
+
}
|
|
3160
|
+
async clearStoredRecord() {
|
|
3161
|
+
return new Promise((resolve, reject) => {
|
|
3162
|
+
if (!this.db) {
|
|
3163
|
+
throw new Error("Database not initialized");
|
|
3164
|
+
}
|
|
3165
|
+
const request = this.db.transaction([STORE_NAME], "readwrite").objectStore(STORE_NAME).delete(ACTIVE_KEY);
|
|
3166
|
+
request.onsuccess = () => {
|
|
3167
|
+
resolve();
|
|
3168
|
+
};
|
|
3169
|
+
request.onerror = () => {
|
|
3170
|
+
reject(request.error);
|
|
3171
|
+
};
|
|
3172
|
+
});
|
|
3173
|
+
}
|
|
3174
|
+
};
|
|
3175
|
+
|
|
2898
3176
|
// src/providers/embedded/adapters/phantom-app.ts
|
|
2899
3177
|
var import_browser_injected_sdk4 = require("@phantom/browser-injected-sdk");
|
|
2900
3178
|
|
|
@@ -3018,16 +3296,32 @@ var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvi
|
|
|
3018
3296
|
constructor(config) {
|
|
3019
3297
|
debug.log(DebugCategory.EMBEDDED_PROVIDER, "Initializing Browser EmbeddedProvider", { config });
|
|
3020
3298
|
const urlParamsAccessor = new BrowserURLParamsAccessor();
|
|
3021
|
-
const
|
|
3299
|
+
const storage = new BrowserStorage();
|
|
3300
|
+
const stamper = config.unstable__auth2Options ? new Auth2Stamper(`phantom-auth2-${config.appId}`) : new import_indexed_db_stamper.IndexedDbStamper({
|
|
3022
3301
|
dbName: `phantom-embedded-sdk-${config.appId}`,
|
|
3023
3302
|
storeName: "crypto-keys",
|
|
3024
3303
|
keyName: "signing-key"
|
|
3025
3304
|
});
|
|
3026
3305
|
const platformName = getPlatformName();
|
|
3027
3306
|
const { name: browserName, version } = detectBrowser();
|
|
3307
|
+
const authProvider = config.unstable__auth2Options && config.authOptions?.authUrl && config.authOptions?.redirectUrl && stamper instanceof Auth2Stamper ? new Auth2AuthProvider(
|
|
3308
|
+
stamper,
|
|
3309
|
+
storage,
|
|
3310
|
+
urlParamsAccessor,
|
|
3311
|
+
{
|
|
3312
|
+
redirectUri: config.authOptions.redirectUrl,
|
|
3313
|
+
connectLoginUrl: config.authOptions.authUrl,
|
|
3314
|
+
clientId: config.unstable__auth2Options.clientId,
|
|
3315
|
+
authApiBaseUrl: config.unstable__auth2Options.authApiBaseUrl
|
|
3316
|
+
},
|
|
3317
|
+
{
|
|
3318
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
3319
|
+
appId: config.appId
|
|
3320
|
+
}
|
|
3321
|
+
) : new BrowserAuthProvider(urlParamsAccessor);
|
|
3028
3322
|
const platform = {
|
|
3029
|
-
storage
|
|
3030
|
-
authProvider
|
|
3323
|
+
storage,
|
|
3324
|
+
authProvider,
|
|
3031
3325
|
phantomAppProvider: new BrowserPhantomAppProvider(),
|
|
3032
3326
|
urlParamsAccessor,
|
|
3033
3327
|
stamper,
|
|
@@ -3035,13 +3329,12 @@ var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvi
|
|
|
3035
3329
|
// Use detected browser name and version for identification
|
|
3036
3330
|
analyticsHeaders: {
|
|
3037
3331
|
[import_constants4.ANALYTICS_HEADERS.SDK_TYPE]: "browser",
|
|
3038
|
-
[import_constants4.ANALYTICS_HEADERS.PLATFORM]:
|
|
3039
|
-
// firefox, chrome, safari, etc.
|
|
3332
|
+
[import_constants4.ANALYTICS_HEADERS.PLATFORM]: "ext-sdk",
|
|
3040
3333
|
[import_constants4.ANALYTICS_HEADERS.PLATFORM_VERSION]: version,
|
|
3041
|
-
|
|
3334
|
+
[import_constants4.ANALYTICS_HEADERS.CLIENT]: browserName,
|
|
3042
3335
|
[import_constants4.ANALYTICS_HEADERS.APP_ID]: config.appId,
|
|
3043
3336
|
[import_constants4.ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
|
|
3044
|
-
[import_constants4.ANALYTICS_HEADERS.SDK_VERSION]: "1.0.
|
|
3337
|
+
[import_constants4.ANALYTICS_HEADERS.SDK_VERSION]: "1.0.5"
|
|
3045
3338
|
// Replaced at build time
|
|
3046
3339
|
}
|
|
3047
3340
|
};
|
|
@@ -3459,6 +3752,7 @@ var ProviderManager = class {
|
|
|
3459
3752
|
authUrl,
|
|
3460
3753
|
redirectUrl: this.config.authOptions?.redirectUrl || this.getValidatedCurrentUrl()
|
|
3461
3754
|
},
|
|
3755
|
+
unstable__auth2Options: this.config.unstable__auth2Options,
|
|
3462
3756
|
embeddedWalletType: embeddedWalletType || import_constants5.DEFAULT_EMBEDDED_WALLET_TYPE,
|
|
3463
3757
|
addressTypes: this.config.addressTypes || [import_client.AddressType.solana]
|
|
3464
3758
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1755,16 +1755,11 @@ var InjectedProvider = class {
|
|
|
1755
1755
|
walletName: walletInfo.name
|
|
1756
1756
|
});
|
|
1757
1757
|
} catch (err) {
|
|
1758
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana,
|
|
1758
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Solana, continuing with other chains", {
|
|
1759
1759
|
error: err,
|
|
1760
1760
|
walletId: this.selectedWalletId,
|
|
1761
1761
|
walletName: walletInfo.name
|
|
1762
1762
|
});
|
|
1763
|
-
this.emit("connect_error", {
|
|
1764
|
-
error: err instanceof Error ? err.message : "Failed to connect",
|
|
1765
|
-
source: options?.skipEventListeners ? "auto-connect" : "manual-connect"
|
|
1766
|
-
});
|
|
1767
|
-
throw err;
|
|
1768
1763
|
}
|
|
1769
1764
|
}
|
|
1770
1765
|
if (this.addressTypes.includes(AddressType3.ethereum) && walletInfo.providers?.ethereum) {
|
|
@@ -1794,16 +1789,11 @@ var InjectedProvider = class {
|
|
|
1794
1789
|
});
|
|
1795
1790
|
}
|
|
1796
1791
|
} catch (err) {
|
|
1797
|
-
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum,
|
|
1792
|
+
debug.warn(DebugCategory.INJECTED_PROVIDER, "Failed to connect Ethereum, continuing with other chains", {
|
|
1798
1793
|
error: err,
|
|
1799
1794
|
walletId: this.selectedWalletId,
|
|
1800
1795
|
walletName: walletInfo.name
|
|
1801
1796
|
});
|
|
1802
|
-
this.emit("connect_error", {
|
|
1803
|
-
error: err instanceof Error ? err.message : "Failed to connect",
|
|
1804
|
-
source: options?.skipEventListeners ? "auto-connect" : "manual-connect"
|
|
1805
|
-
});
|
|
1806
|
-
throw err;
|
|
1807
1797
|
}
|
|
1808
1798
|
}
|
|
1809
1799
|
return connectedAddresses;
|
|
@@ -2724,7 +2714,7 @@ var BrowserAuthProvider = class {
|
|
|
2724
2714
|
// OAuth session management - defaults to allow refresh unless explicitly clearing after logout
|
|
2725
2715
|
clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
|
|
2726
2716
|
allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
|
|
2727
|
-
sdk_version: "1.0.
|
|
2717
|
+
sdk_version: "1.0.5",
|
|
2728
2718
|
sdk_type: "browser",
|
|
2729
2719
|
platform: detectBrowser().name,
|
|
2730
2720
|
algorithm: phantomOptions.algorithm || DEFAULT_AUTHENTICATOR_ALGORITHM
|
|
@@ -2755,7 +2745,7 @@ var BrowserAuthProvider = class {
|
|
|
2755
2745
|
resolve();
|
|
2756
2746
|
});
|
|
2757
2747
|
}
|
|
2758
|
-
resumeAuthFromRedirect(provider) {
|
|
2748
|
+
async resumeAuthFromRedirect(provider) {
|
|
2759
2749
|
try {
|
|
2760
2750
|
const walletId = this.urlParamsAccessor.getParam("wallet_id");
|
|
2761
2751
|
const sessionId = this.urlParamsAccessor.getParam("session_id");
|
|
@@ -2830,14 +2820,14 @@ var BrowserAuthProvider = class {
|
|
|
2830
2820
|
}
|
|
2831
2821
|
);
|
|
2832
2822
|
}
|
|
2833
|
-
return {
|
|
2823
|
+
return Promise.resolve({
|
|
2834
2824
|
walletId,
|
|
2835
2825
|
organizationId,
|
|
2836
2826
|
accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
|
|
2837
2827
|
expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
|
|
2838
2828
|
authUserId: authUserId || void 0,
|
|
2839
2829
|
provider
|
|
2840
|
-
};
|
|
2830
|
+
});
|
|
2841
2831
|
} catch (error) {
|
|
2842
2832
|
sessionStorage.removeItem("phantom-auth-context");
|
|
2843
2833
|
throw error;
|
|
@@ -2845,6 +2835,299 @@ var BrowserAuthProvider = class {
|
|
|
2845
2835
|
}
|
|
2846
2836
|
};
|
|
2847
2837
|
|
|
2838
|
+
// src/providers/embedded/adapters/Auth2AuthProvider.ts
|
|
2839
|
+
import {
|
|
2840
|
+
createCodeVerifier,
|
|
2841
|
+
createConnectStartUrl,
|
|
2842
|
+
exchangeAuthCode,
|
|
2843
|
+
Auth2KmsRpcClient
|
|
2844
|
+
} from "@phantom/auth2";
|
|
2845
|
+
var Auth2AuthProvider = class {
|
|
2846
|
+
constructor(stamper, storage, urlParamsAccessor, auth2ProviderOptions, kmsClientOptions) {
|
|
2847
|
+
this.stamper = stamper;
|
|
2848
|
+
this.storage = storage;
|
|
2849
|
+
this.urlParamsAccessor = urlParamsAccessor;
|
|
2850
|
+
this.auth2ProviderOptions = auth2ProviderOptions;
|
|
2851
|
+
this.kms = new Auth2KmsRpcClient(stamper, kmsClientOptions);
|
|
2852
|
+
}
|
|
2853
|
+
/** Redirect the browser. Extracted as a static method so tests can spy on it. */
|
|
2854
|
+
static navigate(url) {
|
|
2855
|
+
window.location.href = url;
|
|
2856
|
+
}
|
|
2857
|
+
/**
|
|
2858
|
+
* Builds the Auth2 /login/start URL and redirects the browser.
|
|
2859
|
+
*
|
|
2860
|
+
* Called by EmbeddedProvider.handleRedirectAuth() after the stamper has
|
|
2861
|
+
* already been initialized and a pending Session has been saved to storage.
|
|
2862
|
+
* We store the PKCE code_verifier into that session so it survives the page
|
|
2863
|
+
* redirect without ever touching sessionStorage.
|
|
2864
|
+
*/
|
|
2865
|
+
async authenticate(options) {
|
|
2866
|
+
if (!this.stamper.getKeyInfo()) {
|
|
2867
|
+
await this.stamper.init();
|
|
2868
|
+
}
|
|
2869
|
+
const keyPair = this.stamper.getCryptoKeyPair();
|
|
2870
|
+
if (!keyPair) {
|
|
2871
|
+
throw new Error("Stamper key pair not found.");
|
|
2872
|
+
}
|
|
2873
|
+
const codeVerifier = createCodeVerifier();
|
|
2874
|
+
const session = await this.storage.getSession();
|
|
2875
|
+
if (!session) {
|
|
2876
|
+
throw new Error("Session not found.");
|
|
2877
|
+
}
|
|
2878
|
+
await this.storage.saveSession({ ...session, pkceCodeVerifier: codeVerifier });
|
|
2879
|
+
const url = await createConnectStartUrl({
|
|
2880
|
+
keyPair,
|
|
2881
|
+
connectLoginUrl: this.auth2ProviderOptions.connectLoginUrl,
|
|
2882
|
+
clientId: this.auth2ProviderOptions.clientId,
|
|
2883
|
+
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
2884
|
+
sessionId: options.sessionId,
|
|
2885
|
+
provider: options.provider,
|
|
2886
|
+
codeVerifier,
|
|
2887
|
+
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
2888
|
+
salt: ""
|
|
2889
|
+
});
|
|
2890
|
+
Auth2AuthProvider.navigate(url);
|
|
2891
|
+
}
|
|
2892
|
+
/**
|
|
2893
|
+
* Processes the Auth2 callback after the browser returns from /login/start.
|
|
2894
|
+
*
|
|
2895
|
+
* Exchanges the authorization code for tokens, discovers the organization
|
|
2896
|
+
* and wallet via KMS RPC, then returns a completed AuthResult.
|
|
2897
|
+
*/
|
|
2898
|
+
async resumeAuthFromRedirect(provider) {
|
|
2899
|
+
const code = this.urlParamsAccessor.getParam("code");
|
|
2900
|
+
if (!code) {
|
|
2901
|
+
return null;
|
|
2902
|
+
}
|
|
2903
|
+
if (!this.stamper.getKeyInfo()) {
|
|
2904
|
+
await this.stamper.init();
|
|
2905
|
+
}
|
|
2906
|
+
const session = await this.storage.getSession();
|
|
2907
|
+
if (!session) {
|
|
2908
|
+
throw new Error("Session not found.");
|
|
2909
|
+
}
|
|
2910
|
+
const codeVerifier = session?.pkceCodeVerifier;
|
|
2911
|
+
if (!codeVerifier) {
|
|
2912
|
+
return null;
|
|
2913
|
+
}
|
|
2914
|
+
const state = this.urlParamsAccessor.getParam("state");
|
|
2915
|
+
if (!state || state !== session.sessionId) {
|
|
2916
|
+
throw new Error("Missing or invalid Auth2 state parameter \u2014 possible CSRF attack.");
|
|
2917
|
+
}
|
|
2918
|
+
const error = this.urlParamsAccessor.getParam("error");
|
|
2919
|
+
if (error) {
|
|
2920
|
+
const description = this.urlParamsAccessor.getParam("error_description");
|
|
2921
|
+
throw new Error(`Auth2 callback error: ${description ?? error}`);
|
|
2922
|
+
}
|
|
2923
|
+
const { idToken, bearerToken, authUserId, expiresInMs } = await exchangeAuthCode({
|
|
2924
|
+
authApiBaseUrl: this.auth2ProviderOptions.authApiBaseUrl,
|
|
2925
|
+
clientId: this.auth2ProviderOptions.clientId,
|
|
2926
|
+
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
2927
|
+
code,
|
|
2928
|
+
codeVerifier
|
|
2929
|
+
});
|
|
2930
|
+
await this.stamper.setIdToken(idToken);
|
|
2931
|
+
await this.storage.saveSession({
|
|
2932
|
+
...session,
|
|
2933
|
+
status: "completed",
|
|
2934
|
+
bearerToken,
|
|
2935
|
+
authUserId,
|
|
2936
|
+
pkceCodeVerifier: void 0
|
|
2937
|
+
// no longer needed after code exchange
|
|
2938
|
+
});
|
|
2939
|
+
const { organizationId, walletId } = await this.kms.discoverOrganizationAndWalletId(bearerToken, authUserId);
|
|
2940
|
+
return {
|
|
2941
|
+
walletId,
|
|
2942
|
+
organizationId,
|
|
2943
|
+
provider,
|
|
2944
|
+
accountDerivationIndex: 0,
|
|
2945
|
+
// discoverWalletId uses derivation index of 0.
|
|
2946
|
+
expiresInMs,
|
|
2947
|
+
authUserId,
|
|
2948
|
+
bearerToken
|
|
2949
|
+
};
|
|
2950
|
+
}
|
|
2951
|
+
};
|
|
2952
|
+
|
|
2953
|
+
// src/providers/embedded/adapters/Auth2Stamper.ts
|
|
2954
|
+
import bs582 from "bs58";
|
|
2955
|
+
import { base64urlEncode } from "@phantom/base64url";
|
|
2956
|
+
import { Algorithm } from "@phantom/sdk-types";
|
|
2957
|
+
var STORE_NAME = "crypto-keys";
|
|
2958
|
+
var ACTIVE_KEY = "auth2-p256-signing-key";
|
|
2959
|
+
var Auth2Stamper = class {
|
|
2960
|
+
/**
|
|
2961
|
+
* @param dbName - IndexedDB database name (use a unique name per app to
|
|
2962
|
+
* avoid key collisions with other stampers, e.g. `phantom-auth2-<appId>`).
|
|
2963
|
+
*/
|
|
2964
|
+
constructor(dbName) {
|
|
2965
|
+
this.dbName = dbName;
|
|
2966
|
+
this.db = null;
|
|
2967
|
+
this._keyPair = null;
|
|
2968
|
+
this._keyInfo = null;
|
|
2969
|
+
this._idToken = null;
|
|
2970
|
+
this.algorithm = Algorithm.secp256r1;
|
|
2971
|
+
this.type = "OIDC";
|
|
2972
|
+
}
|
|
2973
|
+
async init() {
|
|
2974
|
+
await this.openDB();
|
|
2975
|
+
const stored = await this.loadRecord();
|
|
2976
|
+
if (stored) {
|
|
2977
|
+
this._keyPair = stored.keyPair;
|
|
2978
|
+
this._keyInfo = stored.keyInfo;
|
|
2979
|
+
if (stored.idToken) {
|
|
2980
|
+
this._idToken = stored.idToken;
|
|
2981
|
+
}
|
|
2982
|
+
return this._keyInfo;
|
|
2983
|
+
}
|
|
2984
|
+
return this.generateAndStore();
|
|
2985
|
+
}
|
|
2986
|
+
getKeyInfo() {
|
|
2987
|
+
return this._keyInfo;
|
|
2988
|
+
}
|
|
2989
|
+
getCryptoKeyPair() {
|
|
2990
|
+
return this._keyPair;
|
|
2991
|
+
}
|
|
2992
|
+
/**
|
|
2993
|
+
* Arms the stamper with the OIDC id token for subsequent KMS stamp() calls.
|
|
2994
|
+
*
|
|
2995
|
+
* Persists the token to IndexedDB alongside the key pair so that
|
|
2996
|
+
* auto-connect can restore it on the next page load without a new login.
|
|
2997
|
+
*/
|
|
2998
|
+
async setIdToken(idToken) {
|
|
2999
|
+
if (!this.db) {
|
|
3000
|
+
await this.openDB();
|
|
3001
|
+
}
|
|
3002
|
+
this._idToken = idToken;
|
|
3003
|
+
const existing = await this.loadRecord();
|
|
3004
|
+
if (existing) {
|
|
3005
|
+
await this.storeRecord({ ...existing, idToken });
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
async stamp(params) {
|
|
3009
|
+
if (!this._keyPair || !this._keyInfo || this._idToken === null) {
|
|
3010
|
+
throw new Error("Auth2Stamper not initialized. Call init() first.");
|
|
3011
|
+
}
|
|
3012
|
+
const signatureRaw = await crypto.subtle.sign(
|
|
3013
|
+
{ name: "ECDSA", hash: "SHA-256" },
|
|
3014
|
+
this._keyPair.privateKey,
|
|
3015
|
+
new Uint8Array(params.data)
|
|
3016
|
+
);
|
|
3017
|
+
const rawPublicKey = bs582.decode(this._keyInfo.publicKey);
|
|
3018
|
+
const stampData = {
|
|
3019
|
+
kind: this.type,
|
|
3020
|
+
idToken: this._idToken,
|
|
3021
|
+
publicKey: base64urlEncode(rawPublicKey),
|
|
3022
|
+
algorithm: this.algorithm,
|
|
3023
|
+
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
3024
|
+
salt: "",
|
|
3025
|
+
signature: base64urlEncode(new Uint8Array(signatureRaw))
|
|
3026
|
+
};
|
|
3027
|
+
return base64urlEncode(new TextEncoder().encode(JSON.stringify(stampData)));
|
|
3028
|
+
}
|
|
3029
|
+
async resetKeyPair() {
|
|
3030
|
+
await this.clear();
|
|
3031
|
+
return this.generateAndStore();
|
|
3032
|
+
}
|
|
3033
|
+
async clear() {
|
|
3034
|
+
await this.clearStoredRecord();
|
|
3035
|
+
this._keyPair = null;
|
|
3036
|
+
this._keyInfo = null;
|
|
3037
|
+
this._idToken = null;
|
|
3038
|
+
}
|
|
3039
|
+
// Auth2 doesn't use key rotation; provide minimal no-op implementations.
|
|
3040
|
+
async rotateKeyPair() {
|
|
3041
|
+
return this.init();
|
|
3042
|
+
}
|
|
3043
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
3044
|
+
async commitRotation(authenticatorId) {
|
|
3045
|
+
if (this._keyInfo) {
|
|
3046
|
+
this._keyInfo.authenticatorId = authenticatorId;
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
async rollbackRotation() {
|
|
3050
|
+
}
|
|
3051
|
+
async generateAndStore() {
|
|
3052
|
+
const keyPair = await crypto.subtle.generateKey(
|
|
3053
|
+
{ name: "ECDSA", namedCurve: "P-256" },
|
|
3054
|
+
false,
|
|
3055
|
+
// non-extractable — private key never leaves Web Crypto
|
|
3056
|
+
["sign", "verify"]
|
|
3057
|
+
);
|
|
3058
|
+
const rawPublicKey = new Uint8Array(await crypto.subtle.exportKey("raw", keyPair.publicKey));
|
|
3059
|
+
const publicKeyBase58 = bs582.encode(rawPublicKey);
|
|
3060
|
+
const keyIdBuffer = await crypto.subtle.digest("SHA-256", rawPublicKey.buffer);
|
|
3061
|
+
const keyId = base64urlEncode(new Uint8Array(keyIdBuffer)).substring(0, 16);
|
|
3062
|
+
this._keyPair = keyPair;
|
|
3063
|
+
this._keyInfo = {
|
|
3064
|
+
keyId,
|
|
3065
|
+
publicKey: publicKeyBase58,
|
|
3066
|
+
createdAt: Date.now()
|
|
3067
|
+
};
|
|
3068
|
+
await this.storeRecord({ keyPair, keyInfo: this._keyInfo });
|
|
3069
|
+
return this._keyInfo;
|
|
3070
|
+
}
|
|
3071
|
+
async openDB() {
|
|
3072
|
+
return new Promise((resolve, reject) => {
|
|
3073
|
+
const request = indexedDB.open(this.dbName, 1);
|
|
3074
|
+
request.onsuccess = () => {
|
|
3075
|
+
this.db = request.result;
|
|
3076
|
+
resolve();
|
|
3077
|
+
};
|
|
3078
|
+
request.onerror = () => reject(request.error);
|
|
3079
|
+
request.onupgradeneeded = (event) => {
|
|
3080
|
+
const db = event.target.result;
|
|
3081
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
3082
|
+
db.createObjectStore(STORE_NAME);
|
|
3083
|
+
}
|
|
3084
|
+
};
|
|
3085
|
+
});
|
|
3086
|
+
}
|
|
3087
|
+
async loadRecord() {
|
|
3088
|
+
return new Promise((resolve, reject) => {
|
|
3089
|
+
if (!this.db) {
|
|
3090
|
+
throw new Error("Database not initialized");
|
|
3091
|
+
}
|
|
3092
|
+
const request = this.db.transaction([STORE_NAME], "readonly").objectStore(STORE_NAME).get(ACTIVE_KEY);
|
|
3093
|
+
request.onsuccess = () => {
|
|
3094
|
+
resolve(request.result ?? null);
|
|
3095
|
+
};
|
|
3096
|
+
request.onerror = () => {
|
|
3097
|
+
reject(request.error);
|
|
3098
|
+
};
|
|
3099
|
+
});
|
|
3100
|
+
}
|
|
3101
|
+
async storeRecord(record) {
|
|
3102
|
+
return new Promise((resolve, reject) => {
|
|
3103
|
+
if (!this.db) {
|
|
3104
|
+
throw new Error("Database not initialized");
|
|
3105
|
+
}
|
|
3106
|
+
const request = this.db.transaction([STORE_NAME], "readwrite").objectStore(STORE_NAME).put(record, ACTIVE_KEY);
|
|
3107
|
+
request.onsuccess = () => {
|
|
3108
|
+
resolve();
|
|
3109
|
+
};
|
|
3110
|
+
request.onerror = () => {
|
|
3111
|
+
reject(request.error);
|
|
3112
|
+
};
|
|
3113
|
+
});
|
|
3114
|
+
}
|
|
3115
|
+
async clearStoredRecord() {
|
|
3116
|
+
return new Promise((resolve, reject) => {
|
|
3117
|
+
if (!this.db) {
|
|
3118
|
+
throw new Error("Database not initialized");
|
|
3119
|
+
}
|
|
3120
|
+
const request = this.db.transaction([STORE_NAME], "readwrite").objectStore(STORE_NAME).delete(ACTIVE_KEY);
|
|
3121
|
+
request.onsuccess = () => {
|
|
3122
|
+
resolve();
|
|
3123
|
+
};
|
|
3124
|
+
request.onerror = () => {
|
|
3125
|
+
reject(request.error);
|
|
3126
|
+
};
|
|
3127
|
+
});
|
|
3128
|
+
}
|
|
3129
|
+
};
|
|
3130
|
+
|
|
2848
3131
|
// src/providers/embedded/adapters/phantom-app.ts
|
|
2849
3132
|
import { isPhantomExtensionInstalled as isPhantomExtensionInstalled3 } from "@phantom/browser-injected-sdk";
|
|
2850
3133
|
|
|
@@ -2968,16 +3251,32 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
|
2968
3251
|
constructor(config) {
|
|
2969
3252
|
debug.log(DebugCategory.EMBEDDED_PROVIDER, "Initializing Browser EmbeddedProvider", { config });
|
|
2970
3253
|
const urlParamsAccessor = new BrowserURLParamsAccessor();
|
|
2971
|
-
const
|
|
3254
|
+
const storage = new BrowserStorage();
|
|
3255
|
+
const stamper = config.unstable__auth2Options ? new Auth2Stamper(`phantom-auth2-${config.appId}`) : new IndexedDbStamper({
|
|
2972
3256
|
dbName: `phantom-embedded-sdk-${config.appId}`,
|
|
2973
3257
|
storeName: "crypto-keys",
|
|
2974
3258
|
keyName: "signing-key"
|
|
2975
3259
|
});
|
|
2976
3260
|
const platformName = getPlatformName();
|
|
2977
3261
|
const { name: browserName, version } = detectBrowser();
|
|
3262
|
+
const authProvider = config.unstable__auth2Options && config.authOptions?.authUrl && config.authOptions?.redirectUrl && stamper instanceof Auth2Stamper ? new Auth2AuthProvider(
|
|
3263
|
+
stamper,
|
|
3264
|
+
storage,
|
|
3265
|
+
urlParamsAccessor,
|
|
3266
|
+
{
|
|
3267
|
+
redirectUri: config.authOptions.redirectUrl,
|
|
3268
|
+
connectLoginUrl: config.authOptions.authUrl,
|
|
3269
|
+
clientId: config.unstable__auth2Options.clientId,
|
|
3270
|
+
authApiBaseUrl: config.unstable__auth2Options.authApiBaseUrl
|
|
3271
|
+
},
|
|
3272
|
+
{
|
|
3273
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
3274
|
+
appId: config.appId
|
|
3275
|
+
}
|
|
3276
|
+
) : new BrowserAuthProvider(urlParamsAccessor);
|
|
2978
3277
|
const platform = {
|
|
2979
|
-
storage
|
|
2980
|
-
authProvider
|
|
3278
|
+
storage,
|
|
3279
|
+
authProvider,
|
|
2981
3280
|
phantomAppProvider: new BrowserPhantomAppProvider(),
|
|
2982
3281
|
urlParamsAccessor,
|
|
2983
3282
|
stamper,
|
|
@@ -2985,13 +3284,12 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
|
2985
3284
|
// Use detected browser name and version for identification
|
|
2986
3285
|
analyticsHeaders: {
|
|
2987
3286
|
[ANALYTICS_HEADERS.SDK_TYPE]: "browser",
|
|
2988
|
-
[ANALYTICS_HEADERS.PLATFORM]:
|
|
2989
|
-
// firefox, chrome, safari, etc.
|
|
3287
|
+
[ANALYTICS_HEADERS.PLATFORM]: "ext-sdk",
|
|
2990
3288
|
[ANALYTICS_HEADERS.PLATFORM_VERSION]: version,
|
|
2991
|
-
|
|
3289
|
+
[ANALYTICS_HEADERS.CLIENT]: browserName,
|
|
2992
3290
|
[ANALYTICS_HEADERS.APP_ID]: config.appId,
|
|
2993
3291
|
[ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
|
|
2994
|
-
[ANALYTICS_HEADERS.SDK_VERSION]: "1.0.
|
|
3292
|
+
[ANALYTICS_HEADERS.SDK_VERSION]: "1.0.5"
|
|
2995
3293
|
// Replaced at build time
|
|
2996
3294
|
}
|
|
2997
3295
|
};
|
|
@@ -3411,6 +3709,7 @@ var ProviderManager = class {
|
|
|
3411
3709
|
authUrl,
|
|
3412
3710
|
redirectUrl: this.config.authOptions?.redirectUrl || this.getValidatedCurrentUrl()
|
|
3413
3711
|
},
|
|
3712
|
+
unstable__auth2Options: this.config.unstable__auth2Options,
|
|
3414
3713
|
embeddedWalletType: embeddedWalletType || DEFAULT_EMBEDDED_WALLET_TYPE,
|
|
3415
3714
|
addressTypes: this.config.addressTypes || [AddressType.solana]
|
|
3416
3715
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phantom/browser-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Browser SDK for Phantom Wallet",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,15 +33,16 @@
|
|
|
33
33
|
"prettier": "prettier --write \"src/**/*.{ts,tsx}\""
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@phantom/
|
|
37
|
-
"@phantom/
|
|
38
|
-
"@phantom/
|
|
39
|
-
"@phantom/
|
|
40
|
-
"@phantom/
|
|
41
|
-
"@phantom/
|
|
42
|
-
"@phantom/
|
|
43
|
-
"@phantom/
|
|
44
|
-
"@phantom/
|
|
36
|
+
"@phantom/auth2": "^1.0.1",
|
|
37
|
+
"@phantom/base64url": "^1.0.5",
|
|
38
|
+
"@phantom/browser-injected-sdk": "^1.0.5",
|
|
39
|
+
"@phantom/chain-interfaces": "^1.0.5",
|
|
40
|
+
"@phantom/client": "^1.0.5",
|
|
41
|
+
"@phantom/constants": "^1.0.5",
|
|
42
|
+
"@phantom/embedded-provider-core": "^1.0.5",
|
|
43
|
+
"@phantom/indexed-db-stamper": "^1.0.5",
|
|
44
|
+
"@phantom/parsers": "^1.0.5",
|
|
45
|
+
"@phantom/sdk-types": "^1.0.5",
|
|
45
46
|
"axios": "^1.10.0",
|
|
46
47
|
"bs58": "^6.0.0",
|
|
47
48
|
"buffer": "^6.0.3",
|