@phantom/browser-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 +46 -2
- package/dist/index.d.ts +4 -7
- package/dist/index.js +60 -416
- package/dist/index.mjs +53 -404
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -216,6 +216,7 @@ const sdk = new BrowserSDK({
|
|
|
216
216
|
authOptions: {
|
|
217
217
|
authUrl: "https://connect.phantom.app/login", // optional
|
|
218
218
|
redirectUrl: "https://yourapp.com/callback", // optional, defaults to current page
|
|
219
|
+
authApiBaseUrl: "https://auth.phantom.app", // optional
|
|
219
220
|
},
|
|
220
221
|
autoConnect: true, // optional, auto-connect to existing session (default: true when embedded providers are used)
|
|
221
222
|
});
|
|
@@ -231,8 +232,9 @@ const sdk = new BrowserSDK({
|
|
|
231
232
|
addressTypes: [AddressType.solana, AddressType.ethereum],
|
|
232
233
|
appId: "your-app-id", // Required for deeplink
|
|
233
234
|
authOptions: {
|
|
234
|
-
authUrl: "https://connect.phantom.app/login",
|
|
235
|
+
authUrl: "https://connect.phantom.app/login/start",
|
|
235
236
|
redirectUrl: "https://yourapp.com/callback",
|
|
237
|
+
authApiBaseUrl: "https://auth.phantom.app",
|
|
236
238
|
},
|
|
237
239
|
});
|
|
238
240
|
```
|
|
@@ -325,9 +327,11 @@ interface BrowserSDKConfig {
|
|
|
325
327
|
|
|
326
328
|
// Optional configuration
|
|
327
329
|
apiBaseUrl?: string; // Phantom API base URL (optional, has default)
|
|
330
|
+
|
|
328
331
|
authOptions?: {
|
|
329
|
-
authUrl?: string; // Custom auth URL (optional, defaults to "https://connect.phantom.app/login")
|
|
332
|
+
authUrl?: string; // Custom auth URL (optional, defaults to "https://connect.phantom.app/login/start")
|
|
330
333
|
redirectUrl?: string; // Custom redirect URL after authentication (optional)
|
|
334
|
+
authApiBaseUrl?: string; // Custom OAuth URL (optional, defaults to "https://auth.phantom.app")
|
|
331
335
|
};
|
|
332
336
|
embeddedWalletType?: "user-wallet"; // Wallet type (optional, defaults to "user-wallet")
|
|
333
337
|
autoConnect?: boolean; // Auto-connect to existing session (default: true when embedded providers used)
|
|
@@ -985,6 +989,46 @@ function toggleDebug(enabled: boolean) {
|
|
|
985
989
|
}
|
|
986
990
|
```
|
|
987
991
|
|
|
992
|
+
## Dapp-Sponsored Transactions (presignTransaction)
|
|
993
|
+
|
|
994
|
+
Pass `presignTransaction` directly to `signAndSendTransaction` for calls that need co-signing. Calls without it proceed normally — it is never applied globally.
|
|
995
|
+
|
|
996
|
+
> **Important:** Phantom embedded wallets do not accept pre-signed transactions. If your use case requires a second signer (e.g. your app as the fee payer), that signing must happen via this option, after Phantom has constructed and validated the transaction. This restriction does not apply to injected providers (e.g. the Phantom browser extension).
|
|
997
|
+
|
|
998
|
+
> **Note:** `presignTransaction` only fires for Solana transactions via the embedded provider. EVM transactions and injected providers are unaffected.
|
|
999
|
+
|
|
1000
|
+
### Transaction format
|
|
1001
|
+
|
|
1002
|
+
The `transaction` string is **base64url-encoded** (URL-safe base64 without `=` padding, using `-` and `_` instead of `+` and `/`).
|
|
1003
|
+
|
|
1004
|
+
The SDK exports `base64urlDecode` and `base64urlEncode` utilities:
|
|
1005
|
+
|
|
1006
|
+
```ts
|
|
1007
|
+
import { base64urlDecode, base64urlEncode } from "@phantom/browser-sdk";
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
### Example: Dapp fee payer
|
|
1011
|
+
|
|
1012
|
+
```ts
|
|
1013
|
+
import { base64urlDecode, base64urlEncode } from "@phantom/browser-sdk";
|
|
1014
|
+
import { Keypair, VersionedTransaction } from "@solana/web3.js";
|
|
1015
|
+
|
|
1016
|
+
const feePayerKeypair = Keypair.fromSecretKey(/* your fee payer secret key */);
|
|
1017
|
+
|
|
1018
|
+
// This call co-signs as fee payer
|
|
1019
|
+
const result = await sdk.solana.signAndSendTransaction(transaction, {
|
|
1020
|
+
presignTransaction: async (tx, context) => {
|
|
1021
|
+
const txBytes = base64urlDecode(tx);
|
|
1022
|
+
const versionedTx = VersionedTransaction.deserialize(txBytes);
|
|
1023
|
+
versionedTx.sign([feePayerKeypair]);
|
|
1024
|
+
return base64urlEncode(versionedTx.serialize());
|
|
1025
|
+
},
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
// This call has no co-signer
|
|
1029
|
+
const result2 = await sdk.solana.signAndSendTransaction(otherTransaction);
|
|
1030
|
+
```
|
|
1031
|
+
|
|
988
1032
|
## Transaction Examples
|
|
989
1033
|
|
|
990
1034
|
### Solana Transactions
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { EmbeddedProviderConfig, EmbeddedProviderAuthType, ConnectResult as ConnectResult$1, WalletAddress, EmbeddedProviderEvent, EventCallback } from '@phantom/embedded-provider-core';
|
|
2
2
|
export { ConnectErrorEventData, ConnectEventData, ConnectStartEventData, DisconnectEventData, EmbeddedProviderEvent, EmbeddedProviderEventMap, EventCallback, SignAndSendTransactionParams, SignMessageParams, SignMessageResult, SignedTransaction, WalletAddress } from '@phantom/embedded-provider-core';
|
|
3
3
|
import { IEthereumChain, ISolanaChain } from '@phantom/chain-interfaces';
|
|
4
|
-
export { EthTransactionRequest, IEthereumChain, ISolanaChain } from '@phantom/chain-interfaces';
|
|
4
|
+
export { EthTransactionRequest, IEthereumChain, ISolanaChain, SignAndSendTransactionOptions } from '@phantom/chain-interfaces';
|
|
5
5
|
import { AddressType } from '@phantom/client';
|
|
6
|
-
export { AddressType } from '@phantom/client';
|
|
6
|
+
export { AddressType, PresignTransactionContext } from '@phantom/client';
|
|
7
7
|
import { AutoConfirmEnableParams, AutoConfirmResult, AutoConfirmSupportedChainsResult } from '@phantom/browser-injected-sdk/auto-confirm';
|
|
8
8
|
export { AutoConfirmEnableParams, AutoConfirmResult, AutoConfirmSupportedChainsResult } from '@phantom/browser-injected-sdk/auto-confirm';
|
|
9
9
|
export { NetworkId, PHANTOM_ICON } from '@phantom/constants';
|
|
10
|
+
export { base64urlDecode, base64urlEncode } from '@phantom/base64url';
|
|
10
11
|
|
|
11
12
|
declare enum DebugLevel {
|
|
12
13
|
ERROR = 0,
|
|
@@ -118,11 +119,7 @@ type BrowserSDKConfig = Prettify<Omit<EmbeddedProviderConfig, "authOptions" | "a
|
|
|
118
119
|
authOptions?: {
|
|
119
120
|
authUrl?: string;
|
|
120
121
|
redirectUrl?: string;
|
|
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;
|
|
122
|
+
authApiBaseUrl?: string;
|
|
126
123
|
};
|
|
127
124
|
}>;
|
|
128
125
|
type Prettify<T> = {
|
package/dist/index.js
CHANGED
|
@@ -34,8 +34,10 @@ __export(src_exports, {
|
|
|
34
34
|
BrowserSDK: () => BrowserSDK,
|
|
35
35
|
DebugCategory: () => DebugCategory,
|
|
36
36
|
DebugLevel: () => DebugLevel,
|
|
37
|
-
NetworkId: () =>
|
|
38
|
-
PHANTOM_ICON: () =>
|
|
37
|
+
NetworkId: () => import_constants7.NetworkId,
|
|
38
|
+
PHANTOM_ICON: () => import_constants8.PHANTOM_ICON,
|
|
39
|
+
base64urlDecode: () => import_base64url.base64urlDecode,
|
|
40
|
+
base64urlEncode: () => import_base64url.base64urlEncode,
|
|
39
41
|
debug: () => debug,
|
|
40
42
|
detectBrowser: () => detectBrowser,
|
|
41
43
|
getBrowserDisplayName: () => getBrowserDisplayName,
|
|
@@ -2455,7 +2457,7 @@ var InjectedProvider = class {
|
|
|
2455
2457
|
|
|
2456
2458
|
// src/providers/embedded/index.ts
|
|
2457
2459
|
var import_embedded_provider_core = require("@phantom/embedded-provider-core");
|
|
2458
|
-
var
|
|
2460
|
+
var import_auth22 = require("@phantom/auth2");
|
|
2459
2461
|
|
|
2460
2462
|
// src/providers/embedded/adapters/storage.ts
|
|
2461
2463
|
var BrowserStorage = class {
|
|
@@ -2730,161 +2732,6 @@ function isMobileDevice() {
|
|
|
2730
2732
|
return isMobileUA || isSmallScreen && isTouchDevice;
|
|
2731
2733
|
}
|
|
2732
2734
|
|
|
2733
|
-
// src/providers/embedded/adapters/auth.ts
|
|
2734
|
-
var BrowserAuthProvider = class {
|
|
2735
|
-
constructor(urlParamsAccessor) {
|
|
2736
|
-
this.urlParamsAccessor = urlParamsAccessor;
|
|
2737
|
-
}
|
|
2738
|
-
getValidatedCurrentUrl() {
|
|
2739
|
-
const currentUrl = window.location.href;
|
|
2740
|
-
if (!currentUrl.startsWith("http:") && !currentUrl.startsWith("https:")) {
|
|
2741
|
-
throw new Error("Invalid URL protocol - only HTTP/HTTPS URLs are supported");
|
|
2742
|
-
}
|
|
2743
|
-
return currentUrl;
|
|
2744
|
-
}
|
|
2745
|
-
authenticate(options) {
|
|
2746
|
-
return new Promise((resolve) => {
|
|
2747
|
-
if ("jwtToken" in options) {
|
|
2748
|
-
throw new Error("JWT authentication should be handled by the core JWTAuth class");
|
|
2749
|
-
}
|
|
2750
|
-
const phantomOptions = options;
|
|
2751
|
-
debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Starting Phantom Connect authentication", {
|
|
2752
|
-
publicKey: phantomOptions.publicKey,
|
|
2753
|
-
appId: phantomOptions.appId,
|
|
2754
|
-
provider: phantomOptions.provider,
|
|
2755
|
-
authUrl: phantomOptions.authUrl
|
|
2756
|
-
});
|
|
2757
|
-
const baseUrl = phantomOptions.authUrl || import_constants3.DEFAULT_AUTH_URL;
|
|
2758
|
-
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Using auth URL", { baseUrl });
|
|
2759
|
-
const params = new URLSearchParams({
|
|
2760
|
-
public_key: phantomOptions.publicKey,
|
|
2761
|
-
app_id: phantomOptions.appId,
|
|
2762
|
-
redirect_uri: phantomOptions.redirectUrl || (typeof window !== "undefined" ? this.getValidatedCurrentUrl() : ""),
|
|
2763
|
-
session_id: phantomOptions.sessionId,
|
|
2764
|
-
// OAuth session management - defaults to allow refresh unless explicitly clearing after logout
|
|
2765
|
-
clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
|
|
2766
|
-
allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
|
|
2767
|
-
sdk_version: "1.0.6",
|
|
2768
|
-
sdk_type: "browser",
|
|
2769
|
-
platform: detectBrowser().name,
|
|
2770
|
-
algorithm: phantomOptions.algorithm || import_constants3.DEFAULT_AUTHENTICATOR_ALGORITHM
|
|
2771
|
-
});
|
|
2772
|
-
if (phantomOptions.provider) {
|
|
2773
|
-
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Provider specified, will skip selection", {
|
|
2774
|
-
provider: phantomOptions.provider
|
|
2775
|
-
});
|
|
2776
|
-
params.append("provider", phantomOptions.provider);
|
|
2777
|
-
} else {
|
|
2778
|
-
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "No provider specified, defaulting to Google");
|
|
2779
|
-
params.append("provider", "google");
|
|
2780
|
-
}
|
|
2781
|
-
const authContext = {
|
|
2782
|
-
publicKey: phantomOptions.publicKey,
|
|
2783
|
-
appId: phantomOptions.appId,
|
|
2784
|
-
provider: phantomOptions.provider,
|
|
2785
|
-
sessionId: phantomOptions.sessionId
|
|
2786
|
-
};
|
|
2787
|
-
sessionStorage.setItem("phantom-auth-context", JSON.stringify(authContext));
|
|
2788
|
-
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Stored auth context in session storage", { authContext });
|
|
2789
|
-
const authUrl = `${baseUrl}?${params.toString()}`;
|
|
2790
|
-
debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Redirecting to Phantom Connect", { authUrl });
|
|
2791
|
-
if (!authUrl.startsWith("https:") && !authUrl.startsWith("http://localhost")) {
|
|
2792
|
-
throw new Error("Invalid auth URL - only HTTPS URLs are allowed for authentication");
|
|
2793
|
-
}
|
|
2794
|
-
window.location.href = authUrl;
|
|
2795
|
-
resolve();
|
|
2796
|
-
});
|
|
2797
|
-
}
|
|
2798
|
-
async resumeAuthFromRedirect(provider) {
|
|
2799
|
-
try {
|
|
2800
|
-
const walletId = this.urlParamsAccessor.getParam("wallet_id");
|
|
2801
|
-
const sessionId = this.urlParamsAccessor.getParam("session_id");
|
|
2802
|
-
const accountDerivationIndex = this.urlParamsAccessor.getParam("selected_account_index");
|
|
2803
|
-
const error = this.urlParamsAccessor.getParam("error");
|
|
2804
|
-
const errorDescription = this.urlParamsAccessor.getParam("error_description");
|
|
2805
|
-
if (error) {
|
|
2806
|
-
const errorMsg = errorDescription || error;
|
|
2807
|
-
switch (error) {
|
|
2808
|
-
case "access_denied":
|
|
2809
|
-
throw new Error(`Authentication cancelled: ${errorMsg}`);
|
|
2810
|
-
case "invalid_request":
|
|
2811
|
-
throw new Error(`Invalid authentication request: ${errorMsg}`);
|
|
2812
|
-
case "server_error":
|
|
2813
|
-
throw new Error(`Authentication server error: ${errorMsg}`);
|
|
2814
|
-
case "temporarily_unavailable":
|
|
2815
|
-
throw new Error(`Authentication service temporarily unavailable: ${errorMsg}`);
|
|
2816
|
-
default:
|
|
2817
|
-
throw new Error(`Authentication failed: ${errorMsg}`);
|
|
2818
|
-
}
|
|
2819
|
-
}
|
|
2820
|
-
if (!walletId || !sessionId) {
|
|
2821
|
-
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Missing auth parameters in URL", {
|
|
2822
|
-
hasWalletId: !!walletId,
|
|
2823
|
-
hasSessionId: !!sessionId
|
|
2824
|
-
});
|
|
2825
|
-
return null;
|
|
2826
|
-
}
|
|
2827
|
-
const contextStr = sessionStorage.getItem("phantom-auth-context");
|
|
2828
|
-
let context = {};
|
|
2829
|
-
if (contextStr) {
|
|
2830
|
-
try {
|
|
2831
|
-
context = JSON.parse(contextStr);
|
|
2832
|
-
} catch (parseError) {
|
|
2833
|
-
debug.warn(DebugCategory.PHANTOM_CONNECT_AUTH, "Failed to parse stored auth context", { error: parseError });
|
|
2834
|
-
}
|
|
2835
|
-
}
|
|
2836
|
-
if (context.sessionId && sessionId !== context.sessionId) {
|
|
2837
|
-
debug.error(DebugCategory.PHANTOM_CONNECT_AUTH, "Session ID mismatch", {
|
|
2838
|
-
urlSessionId: sessionId,
|
|
2839
|
-
storedSessionId: context.sessionId
|
|
2840
|
-
});
|
|
2841
|
-
throw new Error("Session ID mismatch - possible session corruption or replay attack");
|
|
2842
|
-
}
|
|
2843
|
-
sessionStorage.removeItem("phantom-auth-context");
|
|
2844
|
-
debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Successfully resumed auth from redirect", {
|
|
2845
|
-
walletId,
|
|
2846
|
-
sessionId,
|
|
2847
|
-
accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : void 0
|
|
2848
|
-
});
|
|
2849
|
-
const organizationId = this.urlParamsAccessor.getParam("organization_id");
|
|
2850
|
-
const expiresInMs = this.urlParamsAccessor.getParam("expires_in_ms");
|
|
2851
|
-
const authUserId = this.urlParamsAccessor.getParam("auth_user_id");
|
|
2852
|
-
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Auth redirect parameters", {
|
|
2853
|
-
walletId,
|
|
2854
|
-
organizationId,
|
|
2855
|
-
sessionId,
|
|
2856
|
-
accountDerivationIndex,
|
|
2857
|
-
expiresInMs,
|
|
2858
|
-
authUserId
|
|
2859
|
-
});
|
|
2860
|
-
if (!organizationId) {
|
|
2861
|
-
debug.error(DebugCategory.PHANTOM_CONNECT_AUTH, "Missing organization_id in auth response");
|
|
2862
|
-
throw new Error("Missing organization_id in auth response");
|
|
2863
|
-
}
|
|
2864
|
-
if (organizationId.startsWith("temp-")) {
|
|
2865
|
-
debug.warn(
|
|
2866
|
-
DebugCategory.PHANTOM_CONNECT_AUTH,
|
|
2867
|
-
"Received temporary organization_id, server may not be configured properly",
|
|
2868
|
-
{
|
|
2869
|
-
organizationId
|
|
2870
|
-
}
|
|
2871
|
-
);
|
|
2872
|
-
}
|
|
2873
|
-
return Promise.resolve({
|
|
2874
|
-
walletId,
|
|
2875
|
-
organizationId,
|
|
2876
|
-
accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
|
|
2877
|
-
expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
|
|
2878
|
-
authUserId: authUserId || void 0,
|
|
2879
|
-
provider
|
|
2880
|
-
});
|
|
2881
|
-
} catch (error) {
|
|
2882
|
-
sessionStorage.removeItem("phantom-auth-context");
|
|
2883
|
-
throw error;
|
|
2884
|
-
}
|
|
2885
|
-
}
|
|
2886
|
-
};
|
|
2887
|
-
|
|
2888
2735
|
// src/providers/embedded/adapters/Auth2AuthProvider.ts
|
|
2889
2736
|
var import_auth2 = require("@phantom/auth2");
|
|
2890
2737
|
var Auth2AuthProvider = class {
|
|
@@ -2908,30 +2755,17 @@ var Auth2AuthProvider = class {
|
|
|
2908
2755
|
* redirect without ever touching sessionStorage.
|
|
2909
2756
|
*/
|
|
2910
2757
|
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
2758
|
const session = await this.storage.getSession();
|
|
2920
2759
|
if (!session) {
|
|
2921
2760
|
throw new Error("Session not found.");
|
|
2922
2761
|
}
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
connectLoginUrl: this.auth2ProviderOptions.connectLoginUrl,
|
|
2927
|
-
clientId: this.auth2ProviderOptions.clientId,
|
|
2928
|
-
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
2762
|
+
const { url, codeVerifier } = await (0, import_auth2.prepareAuth2Flow)({
|
|
2763
|
+
stamper: this.stamper,
|
|
2764
|
+
auth2Options: this.auth2ProviderOptions,
|
|
2929
2765
|
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: ""
|
|
2766
|
+
provider: options.provider
|
|
2934
2767
|
});
|
|
2768
|
+
await this.storage.saveSession({ ...session, pkceCodeVerifier: codeVerifier });
|
|
2935
2769
|
Auth2AuthProvider.navigate(url);
|
|
2936
2770
|
}
|
|
2937
2771
|
/**
|
|
@@ -2941,8 +2775,7 @@ var Auth2AuthProvider = class {
|
|
|
2941
2775
|
* and wallet via KMS RPC, then returns a completed AuthResult.
|
|
2942
2776
|
*/
|
|
2943
2777
|
async resumeAuthFromRedirect(provider) {
|
|
2944
|
-
|
|
2945
|
-
if (!code) {
|
|
2778
|
+
if (!this.urlParamsAccessor.getParam("code")) {
|
|
2946
2779
|
return null;
|
|
2947
2780
|
}
|
|
2948
2781
|
if (!this.stamper.getKeyInfo()) {
|
|
@@ -2956,226 +2789,39 @@ var Auth2AuthProvider = class {
|
|
|
2956
2789
|
if (!codeVerifier) {
|
|
2957
2790
|
return null;
|
|
2958
2791
|
}
|
|
2959
|
-
const
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
}
|
|
2963
|
-
const
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
}
|
|
2968
|
-
const { idToken, bearerToken, authUserId, expiresInMs, refreshToken } = await (0, import_auth2.exchangeAuthCode)({
|
|
2969
|
-
authApiBaseUrl: this.auth2ProviderOptions.authApiBaseUrl,
|
|
2970
|
-
clientId: this.auth2ProviderOptions.clientId,
|
|
2971
|
-
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
2792
|
+
const code = (0, import_auth2.validateAuth2Callback)({
|
|
2793
|
+
getParam: (key) => this.urlParamsAccessor.getParam(key),
|
|
2794
|
+
expectedSessionId: session.sessionId
|
|
2795
|
+
});
|
|
2796
|
+
const result = await (0, import_auth2.completeAuth2Exchange)({
|
|
2797
|
+
stamper: this.stamper,
|
|
2798
|
+
kms: this.kms,
|
|
2799
|
+
auth2Options: this.auth2ProviderOptions,
|
|
2972
2800
|
code,
|
|
2973
|
-
codeVerifier
|
|
2801
|
+
codeVerifier,
|
|
2802
|
+
provider
|
|
2974
2803
|
});
|
|
2975
|
-
await this.stamper.setTokens({ idToken, bearerToken, refreshToken, expiresInMs });
|
|
2976
2804
|
await this.storage.saveSession({
|
|
2977
2805
|
...session,
|
|
2978
2806
|
status: "completed",
|
|
2979
|
-
bearerToken,
|
|
2980
|
-
authUserId,
|
|
2807
|
+
bearerToken: result.bearerToken,
|
|
2808
|
+
authUserId: result.authUserId,
|
|
2981
2809
|
pkceCodeVerifier: void 0
|
|
2982
|
-
// no longer needed after code exchange
|
|
2983
2810
|
});
|
|
2984
|
-
|
|
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
|
-
};
|
|
2811
|
+
return result;
|
|
2995
2812
|
}
|
|
2996
2813
|
};
|
|
2997
2814
|
|
|
2998
|
-
// src/providers/embedded/adapters/
|
|
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 import_auth22 = require("@phantom/auth2");
|
|
3003
|
-
var import_constants4 = require("@phantom/constants");
|
|
2815
|
+
// src/providers/embedded/adapters/IndexedDBAuth2StamperStorage.ts
|
|
3004
2816
|
var STORE_NAME = "crypto-keys";
|
|
3005
2817
|
var ACTIVE_KEY = "auth2-p256-signing-key";
|
|
3006
|
-
var
|
|
3007
|
-
|
|
3008
|
-
* @param dbName - IndexedDB database name (use a unique name per app to
|
|
3009
|
-
* avoid key collisions with other stampers, e.g. `phantom-auth2-<appId>`).
|
|
3010
|
-
* @param refreshConfig - When provided, the stamper will automatically refresh
|
|
3011
|
-
* the id_token using the refresh_token before it expires.
|
|
3012
|
-
*/
|
|
3013
|
-
constructor(dbName, refreshConfig) {
|
|
2818
|
+
var IndexedDBAuth2StamperStorage = class {
|
|
2819
|
+
constructor(dbName) {
|
|
3014
2820
|
this.dbName = dbName;
|
|
3015
|
-
this.refreshConfig = refreshConfig;
|
|
3016
2821
|
this.db = null;
|
|
3017
|
-
this.
|
|
3018
|
-
this._keyInfo = null;
|
|
3019
|
-
this._idToken = null;
|
|
3020
|
-
this._bearerToken = null;
|
|
3021
|
-
this._refreshToken = null;
|
|
3022
|
-
this._tokenExpiresAt = null;
|
|
3023
|
-
this.algorithm = import_sdk_types.Algorithm.secp256r1;
|
|
3024
|
-
this.type = "OIDC";
|
|
3025
|
-
}
|
|
3026
|
-
async init() {
|
|
3027
|
-
await this.openDB();
|
|
3028
|
-
const stored = await this.loadRecord();
|
|
3029
|
-
if (stored) {
|
|
3030
|
-
this._keyPair = stored.keyPair;
|
|
3031
|
-
this._keyInfo = stored.keyInfo;
|
|
3032
|
-
if (stored.idToken) {
|
|
3033
|
-
this._idToken = stored.idToken;
|
|
3034
|
-
}
|
|
3035
|
-
if (stored.bearerToken) {
|
|
3036
|
-
this._bearerToken = stored.bearerToken;
|
|
3037
|
-
}
|
|
3038
|
-
if (stored.refreshToken) {
|
|
3039
|
-
this._refreshToken = stored.refreshToken;
|
|
3040
|
-
}
|
|
3041
|
-
if (stored.tokenExpiresAt) {
|
|
3042
|
-
this._tokenExpiresAt = stored.tokenExpiresAt;
|
|
3043
|
-
}
|
|
3044
|
-
return this._keyInfo;
|
|
3045
|
-
}
|
|
3046
|
-
return this.generateAndStore();
|
|
3047
|
-
}
|
|
3048
|
-
getKeyInfo() {
|
|
3049
|
-
return this._keyInfo;
|
|
3050
|
-
}
|
|
3051
|
-
getCryptoKeyPair() {
|
|
3052
|
-
return this._keyPair;
|
|
3053
|
-
}
|
|
3054
|
-
/**
|
|
3055
|
-
* Returns the current token state (refreshing proactively if near expiry),
|
|
3056
|
-
* or null if no token has been set yet.
|
|
3057
|
-
*/
|
|
3058
|
-
async getTokens() {
|
|
3059
|
-
if (this.refreshConfig && this._refreshToken && this._tokenExpiresAt !== null && Date.now() >= this._tokenExpiresAt - import_constants4.TOKEN_REFRESH_BUFFER_MS) {
|
|
3060
|
-
const refreshed = await (0, import_auth22.refreshToken)({
|
|
3061
|
-
authApiBaseUrl: this.refreshConfig.authApiBaseUrl,
|
|
3062
|
-
clientId: this.refreshConfig.clientId,
|
|
3063
|
-
redirectUri: this.refreshConfig.redirectUri,
|
|
3064
|
-
refreshToken: this._refreshToken
|
|
3065
|
-
});
|
|
3066
|
-
await this.setTokens(refreshed);
|
|
3067
|
-
}
|
|
3068
|
-
if (!this._idToken || !this._bearerToken) {
|
|
3069
|
-
return null;
|
|
3070
|
-
}
|
|
3071
|
-
return {
|
|
3072
|
-
idToken: this._idToken,
|
|
3073
|
-
bearerToken: this._bearerToken,
|
|
3074
|
-
refreshToken: this._refreshToken ?? void 0
|
|
3075
|
-
};
|
|
3076
|
-
}
|
|
3077
|
-
/**
|
|
3078
|
-
* Arms the stamper with the OIDC token data for subsequent KMS stamp() calls.
|
|
3079
|
-
*
|
|
3080
|
-
* Persists the tokens to IndexedDB alongside the key pair so that
|
|
3081
|
-
* auto-connect can restore them on the next page load without a new login.
|
|
3082
|
-
*
|
|
3083
|
-
* @param refreshToken - When provided alongside a `refreshConfig`, enables
|
|
3084
|
-
* silent token refresh before the token expires.
|
|
3085
|
-
* @param expiresInMs - Token lifetime in milliseconds (from `expires_in * 1000`).
|
|
3086
|
-
* Used to compute the absolute expiry time for proactive refresh.
|
|
3087
|
-
*/
|
|
3088
|
-
async setTokens({
|
|
3089
|
-
idToken,
|
|
3090
|
-
bearerToken,
|
|
3091
|
-
refreshToken,
|
|
3092
|
-
expiresInMs
|
|
3093
|
-
}) {
|
|
3094
|
-
if (!this.db) {
|
|
3095
|
-
await this.openDB();
|
|
3096
|
-
}
|
|
3097
|
-
this._idToken = idToken;
|
|
3098
|
-
this._bearerToken = bearerToken;
|
|
3099
|
-
this._refreshToken = refreshToken ?? null;
|
|
3100
|
-
this._tokenExpiresAt = expiresInMs != null ? Date.now() + expiresInMs : null;
|
|
3101
|
-
const existing = await this.loadRecord();
|
|
3102
|
-
if (existing) {
|
|
3103
|
-
await this.storeRecord({
|
|
3104
|
-
...existing,
|
|
3105
|
-
idToken,
|
|
3106
|
-
bearerToken,
|
|
3107
|
-
refreshToken,
|
|
3108
|
-
tokenExpiresAt: this._tokenExpiresAt ?? void 0
|
|
3109
|
-
});
|
|
3110
|
-
}
|
|
2822
|
+
this.requiresExtractableKeys = false;
|
|
3111
2823
|
}
|
|
3112
|
-
async
|
|
3113
|
-
if (!this._keyPair || !this._keyInfo || this._idToken === null) {
|
|
3114
|
-
throw new Error("Auth2Stamper not initialized. Call init() first.");
|
|
3115
|
-
}
|
|
3116
|
-
const signatureRaw = await crypto.subtle.sign(
|
|
3117
|
-
{ name: "ECDSA", hash: "SHA-256" },
|
|
3118
|
-
this._keyPair.privateKey,
|
|
3119
|
-
new Uint8Array(params.data)
|
|
3120
|
-
);
|
|
3121
|
-
const rawPublicKey = import_bs582.default.decode(this._keyInfo.publicKey);
|
|
3122
|
-
const stampData = {
|
|
3123
|
-
kind: this.type,
|
|
3124
|
-
idToken: this._idToken,
|
|
3125
|
-
publicKey: (0, import_base64url.base64urlEncode)(rawPublicKey),
|
|
3126
|
-
algorithm: this.algorithm,
|
|
3127
|
-
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
3128
|
-
salt: "",
|
|
3129
|
-
signature: (0, import_base64url.base64urlEncode)(new Uint8Array(signatureRaw))
|
|
3130
|
-
};
|
|
3131
|
-
return (0, import_base64url.base64urlEncode)(new TextEncoder().encode(JSON.stringify(stampData)));
|
|
3132
|
-
}
|
|
3133
|
-
async resetKeyPair() {
|
|
3134
|
-
await this.clear();
|
|
3135
|
-
return this.generateAndStore();
|
|
3136
|
-
}
|
|
3137
|
-
async clear() {
|
|
3138
|
-
await this.clearStoredRecord();
|
|
3139
|
-
this._keyPair = null;
|
|
3140
|
-
this._keyInfo = null;
|
|
3141
|
-
this._idToken = null;
|
|
3142
|
-
this._bearerToken = null;
|
|
3143
|
-
this._refreshToken = null;
|
|
3144
|
-
this._tokenExpiresAt = null;
|
|
3145
|
-
}
|
|
3146
|
-
// Auth2 doesn't use key rotation; provide minimal no-op implementations.
|
|
3147
|
-
async rotateKeyPair() {
|
|
3148
|
-
return this.init();
|
|
3149
|
-
}
|
|
3150
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
3151
|
-
async commitRotation(authenticatorId) {
|
|
3152
|
-
if (this._keyInfo) {
|
|
3153
|
-
this._keyInfo.authenticatorId = authenticatorId;
|
|
3154
|
-
}
|
|
3155
|
-
}
|
|
3156
|
-
async rollbackRotation() {
|
|
3157
|
-
}
|
|
3158
|
-
async generateAndStore() {
|
|
3159
|
-
const keyPair = await crypto.subtle.generateKey(
|
|
3160
|
-
{ name: "ECDSA", namedCurve: "P-256" },
|
|
3161
|
-
false,
|
|
3162
|
-
// non-extractable — private key never leaves Web Crypto
|
|
3163
|
-
["sign", "verify"]
|
|
3164
|
-
);
|
|
3165
|
-
const rawPublicKey = new Uint8Array(await crypto.subtle.exportKey("raw", keyPair.publicKey));
|
|
3166
|
-
const publicKeyBase58 = import_bs582.default.encode(rawPublicKey);
|
|
3167
|
-
const keyIdBuffer = await crypto.subtle.digest("SHA-256", rawPublicKey.buffer);
|
|
3168
|
-
const keyId = (0, import_base64url.base64urlEncode)(new Uint8Array(keyIdBuffer)).substring(0, 16);
|
|
3169
|
-
this._keyPair = keyPair;
|
|
3170
|
-
this._keyInfo = {
|
|
3171
|
-
keyId,
|
|
3172
|
-
publicKey: publicKeyBase58,
|
|
3173
|
-
createdAt: Date.now()
|
|
3174
|
-
};
|
|
3175
|
-
await this.storeRecord({ keyPair, keyInfo: this._keyInfo });
|
|
3176
|
-
return this._keyInfo;
|
|
3177
|
-
}
|
|
3178
|
-
async openDB() {
|
|
2824
|
+
async open() {
|
|
3179
2825
|
return new Promise((resolve, reject) => {
|
|
3180
2826
|
const request = indexedDB.open(this.dbName, 1);
|
|
3181
2827
|
request.onsuccess = () => {
|
|
@@ -3191,7 +2837,7 @@ var Auth2Stamper = class {
|
|
|
3191
2837
|
};
|
|
3192
2838
|
});
|
|
3193
2839
|
}
|
|
3194
|
-
async
|
|
2840
|
+
async load() {
|
|
3195
2841
|
return new Promise((resolve, reject) => {
|
|
3196
2842
|
if (!this.db) {
|
|
3197
2843
|
throw new Error("Database not initialized");
|
|
@@ -3205,7 +2851,7 @@ var Auth2Stamper = class {
|
|
|
3205
2851
|
};
|
|
3206
2852
|
});
|
|
3207
2853
|
}
|
|
3208
|
-
async
|
|
2854
|
+
async save(record) {
|
|
3209
2855
|
return new Promise((resolve, reject) => {
|
|
3210
2856
|
if (!this.db) {
|
|
3211
2857
|
throw new Error("Database not initialized");
|
|
@@ -3219,7 +2865,7 @@ var Auth2Stamper = class {
|
|
|
3219
2865
|
};
|
|
3220
2866
|
});
|
|
3221
2867
|
}
|
|
3222
|
-
async
|
|
2868
|
+
async clear() {
|
|
3223
2869
|
return new Promise((resolve, reject) => {
|
|
3224
2870
|
if (!this.db) {
|
|
3225
2871
|
throw new Error("Database not initialized");
|
|
@@ -3353,38 +2999,34 @@ var BrowserLogger = class {
|
|
|
3353
2999
|
};
|
|
3354
3000
|
|
|
3355
3001
|
// src/providers/embedded/index.ts
|
|
3356
|
-
var
|
|
3002
|
+
var import_constants4 = require("@phantom/constants");
|
|
3357
3003
|
var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvider {
|
|
3358
3004
|
constructor(config) {
|
|
3359
3005
|
debug.log(DebugCategory.EMBEDDED_PROVIDER, "Initializing Browser EmbeddedProvider", { config });
|
|
3360
3006
|
const urlParamsAccessor = new BrowserURLParamsAccessor();
|
|
3361
3007
|
const storage = new BrowserStorage();
|
|
3362
|
-
const stamper =
|
|
3363
|
-
authApiBaseUrl: config.
|
|
3364
|
-
clientId: config.
|
|
3365
|
-
redirectUri: config.authOptions
|
|
3366
|
-
}) : new import_indexed_db_stamper.IndexedDbStamper({
|
|
3367
|
-
dbName: `phantom-embedded-sdk-${config.appId}`,
|
|
3368
|
-
storeName: "crypto-keys",
|
|
3369
|
-
keyName: "signing-key"
|
|
3008
|
+
const stamper = new import_auth22.Auth2Stamper(new IndexedDBAuth2StamperStorage(`phantom-auth2-${config.appId}`), {
|
|
3009
|
+
authApiBaseUrl: config.authOptions.authApiBaseUrl,
|
|
3010
|
+
clientId: config.appId,
|
|
3011
|
+
redirectUri: config.authOptions.redirectUrl
|
|
3370
3012
|
});
|
|
3371
3013
|
const platformName = getPlatformName();
|
|
3372
3014
|
const { name: browserName, version } = detectBrowser();
|
|
3373
|
-
const authProvider =
|
|
3015
|
+
const authProvider = new Auth2AuthProvider(
|
|
3374
3016
|
stamper,
|
|
3375
3017
|
storage,
|
|
3376
3018
|
urlParamsAccessor,
|
|
3377
3019
|
{
|
|
3378
3020
|
redirectUri: config.authOptions.redirectUrl,
|
|
3379
3021
|
connectLoginUrl: config.authOptions.authUrl,
|
|
3380
|
-
clientId: config.
|
|
3381
|
-
authApiBaseUrl: config.
|
|
3022
|
+
clientId: config.appId,
|
|
3023
|
+
authApiBaseUrl: config.authOptions.authApiBaseUrl
|
|
3382
3024
|
},
|
|
3383
3025
|
{
|
|
3384
3026
|
apiBaseUrl: config.apiBaseUrl,
|
|
3385
3027
|
appId: config.appId
|
|
3386
3028
|
}
|
|
3387
|
-
)
|
|
3029
|
+
);
|
|
3388
3030
|
const platform = {
|
|
3389
3031
|
storage,
|
|
3390
3032
|
authProvider,
|
|
@@ -3394,13 +3036,13 @@ var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvi
|
|
|
3394
3036
|
name: platformName,
|
|
3395
3037
|
// Use detected browser name and version for identification
|
|
3396
3038
|
analyticsHeaders: {
|
|
3397
|
-
[
|
|
3398
|
-
[
|
|
3399
|
-
[
|
|
3400
|
-
[
|
|
3401
|
-
[
|
|
3402
|
-
[
|
|
3403
|
-
[
|
|
3039
|
+
[import_constants4.ANALYTICS_HEADERS.SDK_TYPE]: "browser",
|
|
3040
|
+
[import_constants4.ANALYTICS_HEADERS.PLATFORM]: "ext-sdk",
|
|
3041
|
+
[import_constants4.ANALYTICS_HEADERS.PLATFORM_VERSION]: version,
|
|
3042
|
+
[import_constants4.ANALYTICS_HEADERS.CLIENT]: browserName,
|
|
3043
|
+
[import_constants4.ANALYTICS_HEADERS.APP_ID]: config.appId,
|
|
3044
|
+
[import_constants4.ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
|
|
3045
|
+
[import_constants4.ANALYTICS_HEADERS.SDK_VERSION]: "2.0.0-beta.0"
|
|
3404
3046
|
// Replaced at build time
|
|
3405
3047
|
}
|
|
3406
3048
|
};
|
|
@@ -3417,7 +3059,7 @@ var EmbeddedProvider = class extends import_embedded_provider_core.EmbeddedProvi
|
|
|
3417
3059
|
|
|
3418
3060
|
// src/ProviderManager.ts
|
|
3419
3061
|
var import_embedded_provider_core2 = require("@phantom/embedded-provider-core");
|
|
3420
|
-
var
|
|
3062
|
+
var import_constants5 = require("@phantom/constants");
|
|
3421
3063
|
|
|
3422
3064
|
// src/utils/auth-callback.ts
|
|
3423
3065
|
function isAuthFailureCallback(searchParams) {
|
|
@@ -3808,18 +3450,19 @@ var ProviderManager = class {
|
|
|
3808
3450
|
if (!this.config.appId) {
|
|
3809
3451
|
throw new Error("appId is required for embedded provider");
|
|
3810
3452
|
}
|
|
3811
|
-
const apiBaseUrl = this.config.apiBaseUrl ||
|
|
3812
|
-
const authUrl = this.config.authOptions?.authUrl ||
|
|
3453
|
+
const apiBaseUrl = this.config.apiBaseUrl || import_constants5.DEFAULT_WALLET_API_URL;
|
|
3454
|
+
const authUrl = this.config.authOptions?.authUrl || import_constants5.DEFAULT_AUTH_URL;
|
|
3455
|
+
const authApiBaseUrl = this.config.authOptions?.authApiBaseUrl || import_constants5.DEFAULT_AUTH_API_BASE_URL;
|
|
3813
3456
|
provider = new EmbeddedProvider({
|
|
3814
3457
|
apiBaseUrl,
|
|
3815
3458
|
appId: this.config.appId,
|
|
3816
3459
|
authOptions: {
|
|
3817
3460
|
...this.config.authOptions || {},
|
|
3818
3461
|
authUrl,
|
|
3819
|
-
redirectUrl: this.config.authOptions?.redirectUrl || this.getValidatedCurrentUrl()
|
|
3462
|
+
redirectUrl: this.config.authOptions?.redirectUrl || this.getValidatedCurrentUrl(),
|
|
3463
|
+
authApiBaseUrl
|
|
3820
3464
|
},
|
|
3821
|
-
|
|
3822
|
-
embeddedWalletType: embeddedWalletType || import_constants6.DEFAULT_EMBEDDED_WALLET_TYPE,
|
|
3465
|
+
embeddedWalletType: embeddedWalletType || import_constants5.DEFAULT_EMBEDDED_WALLET_TYPE,
|
|
3823
3466
|
addressTypes: this.config.addressTypes || [import_client.AddressType.solana]
|
|
3824
3467
|
});
|
|
3825
3468
|
} else {
|
|
@@ -3855,7 +3498,7 @@ var ProviderManager = class {
|
|
|
3855
3498
|
|
|
3856
3499
|
// src/BrowserSDK.ts
|
|
3857
3500
|
var import_embedded_provider_core3 = require("@phantom/embedded-provider-core");
|
|
3858
|
-
var
|
|
3501
|
+
var import_constants6 = require("@phantom/constants");
|
|
3859
3502
|
var BROWSER_SDK_PROVIDER_TYPES = [
|
|
3860
3503
|
...import_embedded_provider_core3.EMBEDDED_PROVIDER_AUTH_TYPES,
|
|
3861
3504
|
"injected",
|
|
@@ -3891,7 +3534,7 @@ var BrowserSDK = class {
|
|
|
3891
3534
|
});
|
|
3892
3535
|
throw new Error("appId is required when using embedded providers (google, apple, phantom, etc.)");
|
|
3893
3536
|
}
|
|
3894
|
-
const embeddedWalletType = config.embeddedWalletType ||
|
|
3537
|
+
const embeddedWalletType = config.embeddedWalletType || import_constants6.DEFAULT_EMBEDDED_WALLET_TYPE;
|
|
3895
3538
|
if (!["app-wallet", "user-wallet"].includes(embeddedWalletType)) {
|
|
3896
3539
|
debug.error(DebugCategory.BROWSER_SDK, "Invalid embeddedWalletType", {
|
|
3897
3540
|
embeddedWalletType: config.embeddedWalletType
|
|
@@ -4166,6 +3809,7 @@ var BrowserSDK = class {
|
|
|
4166
3809
|
};
|
|
4167
3810
|
|
|
4168
3811
|
// src/index.ts
|
|
4169
|
-
var
|
|
3812
|
+
var import_constants7 = require("@phantom/constants");
|
|
4170
3813
|
var import_client5 = require("@phantom/client");
|
|
4171
|
-
var
|
|
3814
|
+
var import_base64url = require("@phantom/base64url");
|
|
3815
|
+
var import_constants8 = require("@phantom/constants");
|
package/dist/index.mjs
CHANGED
|
@@ -2405,7 +2405,7 @@ var InjectedProvider = class {
|
|
|
2405
2405
|
|
|
2406
2406
|
// src/providers/embedded/index.ts
|
|
2407
2407
|
import { EmbeddedProvider as CoreEmbeddedProvider } from "@phantom/embedded-provider-core";
|
|
2408
|
-
import {
|
|
2408
|
+
import { Auth2Stamper } from "@phantom/auth2";
|
|
2409
2409
|
|
|
2410
2410
|
// src/providers/embedded/adapters/storage.ts
|
|
2411
2411
|
var BrowserStorage = class {
|
|
@@ -2680,167 +2680,12 @@ function isMobileDevice() {
|
|
|
2680
2680
|
return isMobileUA || isSmallScreen && isTouchDevice;
|
|
2681
2681
|
}
|
|
2682
2682
|
|
|
2683
|
-
// src/providers/embedded/adapters/auth.ts
|
|
2684
|
-
var BrowserAuthProvider = class {
|
|
2685
|
-
constructor(urlParamsAccessor) {
|
|
2686
|
-
this.urlParamsAccessor = urlParamsAccessor;
|
|
2687
|
-
}
|
|
2688
|
-
getValidatedCurrentUrl() {
|
|
2689
|
-
const currentUrl = window.location.href;
|
|
2690
|
-
if (!currentUrl.startsWith("http:") && !currentUrl.startsWith("https:")) {
|
|
2691
|
-
throw new Error("Invalid URL protocol - only HTTP/HTTPS URLs are supported");
|
|
2692
|
-
}
|
|
2693
|
-
return currentUrl;
|
|
2694
|
-
}
|
|
2695
|
-
authenticate(options) {
|
|
2696
|
-
return new Promise((resolve) => {
|
|
2697
|
-
if ("jwtToken" in options) {
|
|
2698
|
-
throw new Error("JWT authentication should be handled by the core JWTAuth class");
|
|
2699
|
-
}
|
|
2700
|
-
const phantomOptions = options;
|
|
2701
|
-
debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Starting Phantom Connect authentication", {
|
|
2702
|
-
publicKey: phantomOptions.publicKey,
|
|
2703
|
-
appId: phantomOptions.appId,
|
|
2704
|
-
provider: phantomOptions.provider,
|
|
2705
|
-
authUrl: phantomOptions.authUrl
|
|
2706
|
-
});
|
|
2707
|
-
const baseUrl = phantomOptions.authUrl || DEFAULT_AUTH_URL;
|
|
2708
|
-
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Using auth URL", { baseUrl });
|
|
2709
|
-
const params = new URLSearchParams({
|
|
2710
|
-
public_key: phantomOptions.publicKey,
|
|
2711
|
-
app_id: phantomOptions.appId,
|
|
2712
|
-
redirect_uri: phantomOptions.redirectUrl || (typeof window !== "undefined" ? this.getValidatedCurrentUrl() : ""),
|
|
2713
|
-
session_id: phantomOptions.sessionId,
|
|
2714
|
-
// OAuth session management - defaults to allow refresh unless explicitly clearing after logout
|
|
2715
|
-
clear_previous_session: (phantomOptions.clearPreviousSession ?? false).toString(),
|
|
2716
|
-
allow_refresh: (phantomOptions.allowRefresh ?? true).toString(),
|
|
2717
|
-
sdk_version: "1.0.6",
|
|
2718
|
-
sdk_type: "browser",
|
|
2719
|
-
platform: detectBrowser().name,
|
|
2720
|
-
algorithm: phantomOptions.algorithm || DEFAULT_AUTHENTICATOR_ALGORITHM
|
|
2721
|
-
});
|
|
2722
|
-
if (phantomOptions.provider) {
|
|
2723
|
-
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Provider specified, will skip selection", {
|
|
2724
|
-
provider: phantomOptions.provider
|
|
2725
|
-
});
|
|
2726
|
-
params.append("provider", phantomOptions.provider);
|
|
2727
|
-
} else {
|
|
2728
|
-
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "No provider specified, defaulting to Google");
|
|
2729
|
-
params.append("provider", "google");
|
|
2730
|
-
}
|
|
2731
|
-
const authContext = {
|
|
2732
|
-
publicKey: phantomOptions.publicKey,
|
|
2733
|
-
appId: phantomOptions.appId,
|
|
2734
|
-
provider: phantomOptions.provider,
|
|
2735
|
-
sessionId: phantomOptions.sessionId
|
|
2736
|
-
};
|
|
2737
|
-
sessionStorage.setItem("phantom-auth-context", JSON.stringify(authContext));
|
|
2738
|
-
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Stored auth context in session storage", { authContext });
|
|
2739
|
-
const authUrl = `${baseUrl}?${params.toString()}`;
|
|
2740
|
-
debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Redirecting to Phantom Connect", { authUrl });
|
|
2741
|
-
if (!authUrl.startsWith("https:") && !authUrl.startsWith("http://localhost")) {
|
|
2742
|
-
throw new Error("Invalid auth URL - only HTTPS URLs are allowed for authentication");
|
|
2743
|
-
}
|
|
2744
|
-
window.location.href = authUrl;
|
|
2745
|
-
resolve();
|
|
2746
|
-
});
|
|
2747
|
-
}
|
|
2748
|
-
async resumeAuthFromRedirect(provider) {
|
|
2749
|
-
try {
|
|
2750
|
-
const walletId = this.urlParamsAccessor.getParam("wallet_id");
|
|
2751
|
-
const sessionId = this.urlParamsAccessor.getParam("session_id");
|
|
2752
|
-
const accountDerivationIndex = this.urlParamsAccessor.getParam("selected_account_index");
|
|
2753
|
-
const error = this.urlParamsAccessor.getParam("error");
|
|
2754
|
-
const errorDescription = this.urlParamsAccessor.getParam("error_description");
|
|
2755
|
-
if (error) {
|
|
2756
|
-
const errorMsg = errorDescription || error;
|
|
2757
|
-
switch (error) {
|
|
2758
|
-
case "access_denied":
|
|
2759
|
-
throw new Error(`Authentication cancelled: ${errorMsg}`);
|
|
2760
|
-
case "invalid_request":
|
|
2761
|
-
throw new Error(`Invalid authentication request: ${errorMsg}`);
|
|
2762
|
-
case "server_error":
|
|
2763
|
-
throw new Error(`Authentication server error: ${errorMsg}`);
|
|
2764
|
-
case "temporarily_unavailable":
|
|
2765
|
-
throw new Error(`Authentication service temporarily unavailable: ${errorMsg}`);
|
|
2766
|
-
default:
|
|
2767
|
-
throw new Error(`Authentication failed: ${errorMsg}`);
|
|
2768
|
-
}
|
|
2769
|
-
}
|
|
2770
|
-
if (!walletId || !sessionId) {
|
|
2771
|
-
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Missing auth parameters in URL", {
|
|
2772
|
-
hasWalletId: !!walletId,
|
|
2773
|
-
hasSessionId: !!sessionId
|
|
2774
|
-
});
|
|
2775
|
-
return null;
|
|
2776
|
-
}
|
|
2777
|
-
const contextStr = sessionStorage.getItem("phantom-auth-context");
|
|
2778
|
-
let context = {};
|
|
2779
|
-
if (contextStr) {
|
|
2780
|
-
try {
|
|
2781
|
-
context = JSON.parse(contextStr);
|
|
2782
|
-
} catch (parseError) {
|
|
2783
|
-
debug.warn(DebugCategory.PHANTOM_CONNECT_AUTH, "Failed to parse stored auth context", { error: parseError });
|
|
2784
|
-
}
|
|
2785
|
-
}
|
|
2786
|
-
if (context.sessionId && sessionId !== context.sessionId) {
|
|
2787
|
-
debug.error(DebugCategory.PHANTOM_CONNECT_AUTH, "Session ID mismatch", {
|
|
2788
|
-
urlSessionId: sessionId,
|
|
2789
|
-
storedSessionId: context.sessionId
|
|
2790
|
-
});
|
|
2791
|
-
throw new Error("Session ID mismatch - possible session corruption or replay attack");
|
|
2792
|
-
}
|
|
2793
|
-
sessionStorage.removeItem("phantom-auth-context");
|
|
2794
|
-
debug.info(DebugCategory.PHANTOM_CONNECT_AUTH, "Successfully resumed auth from redirect", {
|
|
2795
|
-
walletId,
|
|
2796
|
-
sessionId,
|
|
2797
|
-
accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : void 0
|
|
2798
|
-
});
|
|
2799
|
-
const organizationId = this.urlParamsAccessor.getParam("organization_id");
|
|
2800
|
-
const expiresInMs = this.urlParamsAccessor.getParam("expires_in_ms");
|
|
2801
|
-
const authUserId = this.urlParamsAccessor.getParam("auth_user_id");
|
|
2802
|
-
debug.log(DebugCategory.PHANTOM_CONNECT_AUTH, "Auth redirect parameters", {
|
|
2803
|
-
walletId,
|
|
2804
|
-
organizationId,
|
|
2805
|
-
sessionId,
|
|
2806
|
-
accountDerivationIndex,
|
|
2807
|
-
expiresInMs,
|
|
2808
|
-
authUserId
|
|
2809
|
-
});
|
|
2810
|
-
if (!organizationId) {
|
|
2811
|
-
debug.error(DebugCategory.PHANTOM_CONNECT_AUTH, "Missing organization_id in auth response");
|
|
2812
|
-
throw new Error("Missing organization_id in auth response");
|
|
2813
|
-
}
|
|
2814
|
-
if (organizationId.startsWith("temp-")) {
|
|
2815
|
-
debug.warn(
|
|
2816
|
-
DebugCategory.PHANTOM_CONNECT_AUTH,
|
|
2817
|
-
"Received temporary organization_id, server may not be configured properly",
|
|
2818
|
-
{
|
|
2819
|
-
organizationId
|
|
2820
|
-
}
|
|
2821
|
-
);
|
|
2822
|
-
}
|
|
2823
|
-
return Promise.resolve({
|
|
2824
|
-
walletId,
|
|
2825
|
-
organizationId,
|
|
2826
|
-
accountDerivationIndex: accountDerivationIndex ? parseInt(accountDerivationIndex) : 0,
|
|
2827
|
-
expiresInMs: expiresInMs ? parseInt(expiresInMs) : 0,
|
|
2828
|
-
authUserId: authUserId || void 0,
|
|
2829
|
-
provider
|
|
2830
|
-
});
|
|
2831
|
-
} catch (error) {
|
|
2832
|
-
sessionStorage.removeItem("phantom-auth-context");
|
|
2833
|
-
throw error;
|
|
2834
|
-
}
|
|
2835
|
-
}
|
|
2836
|
-
};
|
|
2837
|
-
|
|
2838
2683
|
// src/providers/embedded/adapters/Auth2AuthProvider.ts
|
|
2839
2684
|
import {
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2685
|
+
Auth2KmsRpcClient,
|
|
2686
|
+
prepareAuth2Flow,
|
|
2687
|
+
validateAuth2Callback,
|
|
2688
|
+
completeAuth2Exchange
|
|
2844
2689
|
} from "@phantom/auth2";
|
|
2845
2690
|
var Auth2AuthProvider = class {
|
|
2846
2691
|
constructor(stamper, storage, urlParamsAccessor, auth2ProviderOptions, kmsClientOptions) {
|
|
@@ -2863,30 +2708,17 @@ var Auth2AuthProvider = class {
|
|
|
2863
2708
|
* redirect without ever touching sessionStorage.
|
|
2864
2709
|
*/
|
|
2865
2710
|
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
2711
|
const session = await this.storage.getSession();
|
|
2875
2712
|
if (!session) {
|
|
2876
2713
|
throw new Error("Session not found.");
|
|
2877
2714
|
}
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
connectLoginUrl: this.auth2ProviderOptions.connectLoginUrl,
|
|
2882
|
-
clientId: this.auth2ProviderOptions.clientId,
|
|
2883
|
-
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
2715
|
+
const { url, codeVerifier } = await prepareAuth2Flow({
|
|
2716
|
+
stamper: this.stamper,
|
|
2717
|
+
auth2Options: this.auth2ProviderOptions,
|
|
2884
2718
|
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: ""
|
|
2719
|
+
provider: options.provider
|
|
2889
2720
|
});
|
|
2721
|
+
await this.storage.saveSession({ ...session, pkceCodeVerifier: codeVerifier });
|
|
2890
2722
|
Auth2AuthProvider.navigate(url);
|
|
2891
2723
|
}
|
|
2892
2724
|
/**
|
|
@@ -2896,8 +2728,7 @@ var Auth2AuthProvider = class {
|
|
|
2896
2728
|
* and wallet via KMS RPC, then returns a completed AuthResult.
|
|
2897
2729
|
*/
|
|
2898
2730
|
async resumeAuthFromRedirect(provider) {
|
|
2899
|
-
|
|
2900
|
-
if (!code) {
|
|
2731
|
+
if (!this.urlParamsAccessor.getParam("code")) {
|
|
2901
2732
|
return null;
|
|
2902
2733
|
}
|
|
2903
2734
|
if (!this.stamper.getKeyInfo()) {
|
|
@@ -2911,226 +2742,39 @@ var Auth2AuthProvider = class {
|
|
|
2911
2742
|
if (!codeVerifier) {
|
|
2912
2743
|
return null;
|
|
2913
2744
|
}
|
|
2914
|
-
const
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
}
|
|
2918
|
-
const
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
}
|
|
2923
|
-
const { idToken, bearerToken, authUserId, expiresInMs, refreshToken } = await exchangeAuthCode({
|
|
2924
|
-
authApiBaseUrl: this.auth2ProviderOptions.authApiBaseUrl,
|
|
2925
|
-
clientId: this.auth2ProviderOptions.clientId,
|
|
2926
|
-
redirectUri: this.auth2ProviderOptions.redirectUri,
|
|
2745
|
+
const code = validateAuth2Callback({
|
|
2746
|
+
getParam: (key) => this.urlParamsAccessor.getParam(key),
|
|
2747
|
+
expectedSessionId: session.sessionId
|
|
2748
|
+
});
|
|
2749
|
+
const result = await completeAuth2Exchange({
|
|
2750
|
+
stamper: this.stamper,
|
|
2751
|
+
kms: this.kms,
|
|
2752
|
+
auth2Options: this.auth2ProviderOptions,
|
|
2927
2753
|
code,
|
|
2928
|
-
codeVerifier
|
|
2754
|
+
codeVerifier,
|
|
2755
|
+
provider
|
|
2929
2756
|
});
|
|
2930
|
-
await this.stamper.setTokens({ idToken, bearerToken, refreshToken, expiresInMs });
|
|
2931
2757
|
await this.storage.saveSession({
|
|
2932
2758
|
...session,
|
|
2933
2759
|
status: "completed",
|
|
2934
|
-
bearerToken,
|
|
2935
|
-
authUserId,
|
|
2760
|
+
bearerToken: result.bearerToken,
|
|
2761
|
+
authUserId: result.authUserId,
|
|
2936
2762
|
pkceCodeVerifier: void 0
|
|
2937
|
-
// no longer needed after code exchange
|
|
2938
2763
|
});
|
|
2939
|
-
|
|
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
|
-
};
|
|
2764
|
+
return result;
|
|
2950
2765
|
}
|
|
2951
2766
|
};
|
|
2952
2767
|
|
|
2953
|
-
// src/providers/embedded/adapters/
|
|
2954
|
-
import bs582 from "bs58";
|
|
2955
|
-
import { base64urlEncode } from "@phantom/base64url";
|
|
2956
|
-
import { Algorithm } from "@phantom/sdk-types";
|
|
2957
|
-
import { refreshToken as refreshTokenRequest } from "@phantom/auth2";
|
|
2958
|
-
import { TOKEN_REFRESH_BUFFER_MS } from "@phantom/constants";
|
|
2768
|
+
// src/providers/embedded/adapters/IndexedDBAuth2StamperStorage.ts
|
|
2959
2769
|
var STORE_NAME = "crypto-keys";
|
|
2960
2770
|
var ACTIVE_KEY = "auth2-p256-signing-key";
|
|
2961
|
-
var
|
|
2962
|
-
|
|
2963
|
-
* @param dbName - IndexedDB database name (use a unique name per app to
|
|
2964
|
-
* avoid key collisions with other stampers, e.g. `phantom-auth2-<appId>`).
|
|
2965
|
-
* @param refreshConfig - When provided, the stamper will automatically refresh
|
|
2966
|
-
* the id_token using the refresh_token before it expires.
|
|
2967
|
-
*/
|
|
2968
|
-
constructor(dbName, refreshConfig) {
|
|
2771
|
+
var IndexedDBAuth2StamperStorage = class {
|
|
2772
|
+
constructor(dbName) {
|
|
2969
2773
|
this.dbName = dbName;
|
|
2970
|
-
this.refreshConfig = refreshConfig;
|
|
2971
2774
|
this.db = null;
|
|
2972
|
-
this.
|
|
2973
|
-
this._keyInfo = null;
|
|
2974
|
-
this._idToken = null;
|
|
2975
|
-
this._bearerToken = null;
|
|
2976
|
-
this._refreshToken = null;
|
|
2977
|
-
this._tokenExpiresAt = null;
|
|
2978
|
-
this.algorithm = Algorithm.secp256r1;
|
|
2979
|
-
this.type = "OIDC";
|
|
2980
|
-
}
|
|
2981
|
-
async init() {
|
|
2982
|
-
await this.openDB();
|
|
2983
|
-
const stored = await this.loadRecord();
|
|
2984
|
-
if (stored) {
|
|
2985
|
-
this._keyPair = stored.keyPair;
|
|
2986
|
-
this._keyInfo = stored.keyInfo;
|
|
2987
|
-
if (stored.idToken) {
|
|
2988
|
-
this._idToken = stored.idToken;
|
|
2989
|
-
}
|
|
2990
|
-
if (stored.bearerToken) {
|
|
2991
|
-
this._bearerToken = stored.bearerToken;
|
|
2992
|
-
}
|
|
2993
|
-
if (stored.refreshToken) {
|
|
2994
|
-
this._refreshToken = stored.refreshToken;
|
|
2995
|
-
}
|
|
2996
|
-
if (stored.tokenExpiresAt) {
|
|
2997
|
-
this._tokenExpiresAt = stored.tokenExpiresAt;
|
|
2998
|
-
}
|
|
2999
|
-
return this._keyInfo;
|
|
3000
|
-
}
|
|
3001
|
-
return this.generateAndStore();
|
|
3002
|
-
}
|
|
3003
|
-
getKeyInfo() {
|
|
3004
|
-
return this._keyInfo;
|
|
3005
|
-
}
|
|
3006
|
-
getCryptoKeyPair() {
|
|
3007
|
-
return this._keyPair;
|
|
3008
|
-
}
|
|
3009
|
-
/**
|
|
3010
|
-
* Returns the current token state (refreshing proactively if near expiry),
|
|
3011
|
-
* or null if no token has been set yet.
|
|
3012
|
-
*/
|
|
3013
|
-
async getTokens() {
|
|
3014
|
-
if (this.refreshConfig && this._refreshToken && this._tokenExpiresAt !== null && Date.now() >= this._tokenExpiresAt - TOKEN_REFRESH_BUFFER_MS) {
|
|
3015
|
-
const refreshed = await refreshTokenRequest({
|
|
3016
|
-
authApiBaseUrl: this.refreshConfig.authApiBaseUrl,
|
|
3017
|
-
clientId: this.refreshConfig.clientId,
|
|
3018
|
-
redirectUri: this.refreshConfig.redirectUri,
|
|
3019
|
-
refreshToken: this._refreshToken
|
|
3020
|
-
});
|
|
3021
|
-
await this.setTokens(refreshed);
|
|
3022
|
-
}
|
|
3023
|
-
if (!this._idToken || !this._bearerToken) {
|
|
3024
|
-
return null;
|
|
3025
|
-
}
|
|
3026
|
-
return {
|
|
3027
|
-
idToken: this._idToken,
|
|
3028
|
-
bearerToken: this._bearerToken,
|
|
3029
|
-
refreshToken: this._refreshToken ?? void 0
|
|
3030
|
-
};
|
|
3031
|
-
}
|
|
3032
|
-
/**
|
|
3033
|
-
* Arms the stamper with the OIDC token data for subsequent KMS stamp() calls.
|
|
3034
|
-
*
|
|
3035
|
-
* Persists the tokens to IndexedDB alongside the key pair so that
|
|
3036
|
-
* auto-connect can restore them on the next page load without a new login.
|
|
3037
|
-
*
|
|
3038
|
-
* @param refreshToken - When provided alongside a `refreshConfig`, enables
|
|
3039
|
-
* silent token refresh before the token expires.
|
|
3040
|
-
* @param expiresInMs - Token lifetime in milliseconds (from `expires_in * 1000`).
|
|
3041
|
-
* Used to compute the absolute expiry time for proactive refresh.
|
|
3042
|
-
*/
|
|
3043
|
-
async setTokens({
|
|
3044
|
-
idToken,
|
|
3045
|
-
bearerToken,
|
|
3046
|
-
refreshToken,
|
|
3047
|
-
expiresInMs
|
|
3048
|
-
}) {
|
|
3049
|
-
if (!this.db) {
|
|
3050
|
-
await this.openDB();
|
|
3051
|
-
}
|
|
3052
|
-
this._idToken = idToken;
|
|
3053
|
-
this._bearerToken = bearerToken;
|
|
3054
|
-
this._refreshToken = refreshToken ?? null;
|
|
3055
|
-
this._tokenExpiresAt = expiresInMs != null ? Date.now() + expiresInMs : null;
|
|
3056
|
-
const existing = await this.loadRecord();
|
|
3057
|
-
if (existing) {
|
|
3058
|
-
await this.storeRecord({
|
|
3059
|
-
...existing,
|
|
3060
|
-
idToken,
|
|
3061
|
-
bearerToken,
|
|
3062
|
-
refreshToken,
|
|
3063
|
-
tokenExpiresAt: this._tokenExpiresAt ?? void 0
|
|
3064
|
-
});
|
|
3065
|
-
}
|
|
2775
|
+
this.requiresExtractableKeys = false;
|
|
3066
2776
|
}
|
|
3067
|
-
async
|
|
3068
|
-
if (!this._keyPair || !this._keyInfo || this._idToken === null) {
|
|
3069
|
-
throw new Error("Auth2Stamper not initialized. Call init() first.");
|
|
3070
|
-
}
|
|
3071
|
-
const signatureRaw = await crypto.subtle.sign(
|
|
3072
|
-
{ name: "ECDSA", hash: "SHA-256" },
|
|
3073
|
-
this._keyPair.privateKey,
|
|
3074
|
-
new Uint8Array(params.data)
|
|
3075
|
-
);
|
|
3076
|
-
const rawPublicKey = bs582.decode(this._keyInfo.publicKey);
|
|
3077
|
-
const stampData = {
|
|
3078
|
-
kind: this.type,
|
|
3079
|
-
idToken: this._idToken,
|
|
3080
|
-
publicKey: base64urlEncode(rawPublicKey),
|
|
3081
|
-
algorithm: this.algorithm,
|
|
3082
|
-
// The P-256 ephemeral key is unique per wallet, so no additional salt is needed.
|
|
3083
|
-
salt: "",
|
|
3084
|
-
signature: base64urlEncode(new Uint8Array(signatureRaw))
|
|
3085
|
-
};
|
|
3086
|
-
return base64urlEncode(new TextEncoder().encode(JSON.stringify(stampData)));
|
|
3087
|
-
}
|
|
3088
|
-
async resetKeyPair() {
|
|
3089
|
-
await this.clear();
|
|
3090
|
-
return this.generateAndStore();
|
|
3091
|
-
}
|
|
3092
|
-
async clear() {
|
|
3093
|
-
await this.clearStoredRecord();
|
|
3094
|
-
this._keyPair = null;
|
|
3095
|
-
this._keyInfo = null;
|
|
3096
|
-
this._idToken = null;
|
|
3097
|
-
this._bearerToken = null;
|
|
3098
|
-
this._refreshToken = null;
|
|
3099
|
-
this._tokenExpiresAt = null;
|
|
3100
|
-
}
|
|
3101
|
-
// Auth2 doesn't use key rotation; provide minimal no-op implementations.
|
|
3102
|
-
async rotateKeyPair() {
|
|
3103
|
-
return this.init();
|
|
3104
|
-
}
|
|
3105
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
3106
|
-
async commitRotation(authenticatorId) {
|
|
3107
|
-
if (this._keyInfo) {
|
|
3108
|
-
this._keyInfo.authenticatorId = authenticatorId;
|
|
3109
|
-
}
|
|
3110
|
-
}
|
|
3111
|
-
async rollbackRotation() {
|
|
3112
|
-
}
|
|
3113
|
-
async generateAndStore() {
|
|
3114
|
-
const keyPair = await crypto.subtle.generateKey(
|
|
3115
|
-
{ name: "ECDSA", namedCurve: "P-256" },
|
|
3116
|
-
false,
|
|
3117
|
-
// non-extractable — private key never leaves Web Crypto
|
|
3118
|
-
["sign", "verify"]
|
|
3119
|
-
);
|
|
3120
|
-
const rawPublicKey = new Uint8Array(await crypto.subtle.exportKey("raw", keyPair.publicKey));
|
|
3121
|
-
const publicKeyBase58 = bs582.encode(rawPublicKey);
|
|
3122
|
-
const keyIdBuffer = await crypto.subtle.digest("SHA-256", rawPublicKey.buffer);
|
|
3123
|
-
const keyId = base64urlEncode(new Uint8Array(keyIdBuffer)).substring(0, 16);
|
|
3124
|
-
this._keyPair = keyPair;
|
|
3125
|
-
this._keyInfo = {
|
|
3126
|
-
keyId,
|
|
3127
|
-
publicKey: publicKeyBase58,
|
|
3128
|
-
createdAt: Date.now()
|
|
3129
|
-
};
|
|
3130
|
-
await this.storeRecord({ keyPair, keyInfo: this._keyInfo });
|
|
3131
|
-
return this._keyInfo;
|
|
3132
|
-
}
|
|
3133
|
-
async openDB() {
|
|
2777
|
+
async open() {
|
|
3134
2778
|
return new Promise((resolve, reject) => {
|
|
3135
2779
|
const request = indexedDB.open(this.dbName, 1);
|
|
3136
2780
|
request.onsuccess = () => {
|
|
@@ -3146,7 +2790,7 @@ var Auth2Stamper = class {
|
|
|
3146
2790
|
};
|
|
3147
2791
|
});
|
|
3148
2792
|
}
|
|
3149
|
-
async
|
|
2793
|
+
async load() {
|
|
3150
2794
|
return new Promise((resolve, reject) => {
|
|
3151
2795
|
if (!this.db) {
|
|
3152
2796
|
throw new Error("Database not initialized");
|
|
@@ -3160,7 +2804,7 @@ var Auth2Stamper = class {
|
|
|
3160
2804
|
};
|
|
3161
2805
|
});
|
|
3162
2806
|
}
|
|
3163
|
-
async
|
|
2807
|
+
async save(record) {
|
|
3164
2808
|
return new Promise((resolve, reject) => {
|
|
3165
2809
|
if (!this.db) {
|
|
3166
2810
|
throw new Error("Database not initialized");
|
|
@@ -3174,7 +2818,7 @@ var Auth2Stamper = class {
|
|
|
3174
2818
|
};
|
|
3175
2819
|
});
|
|
3176
2820
|
}
|
|
3177
|
-
async
|
|
2821
|
+
async clear() {
|
|
3178
2822
|
return new Promise((resolve, reject) => {
|
|
3179
2823
|
if (!this.db) {
|
|
3180
2824
|
throw new Error("Database not initialized");
|
|
@@ -3314,32 +2958,28 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
|
3314
2958
|
debug.log(DebugCategory.EMBEDDED_PROVIDER, "Initializing Browser EmbeddedProvider", { config });
|
|
3315
2959
|
const urlParamsAccessor = new BrowserURLParamsAccessor();
|
|
3316
2960
|
const storage = new BrowserStorage();
|
|
3317
|
-
const stamper =
|
|
3318
|
-
authApiBaseUrl: config.
|
|
3319
|
-
clientId: config.
|
|
3320
|
-
redirectUri: config.authOptions
|
|
3321
|
-
}) : new IndexedDbStamper({
|
|
3322
|
-
dbName: `phantom-embedded-sdk-${config.appId}`,
|
|
3323
|
-
storeName: "crypto-keys",
|
|
3324
|
-
keyName: "signing-key"
|
|
2961
|
+
const stamper = new Auth2Stamper(new IndexedDBAuth2StamperStorage(`phantom-auth2-${config.appId}`), {
|
|
2962
|
+
authApiBaseUrl: config.authOptions.authApiBaseUrl,
|
|
2963
|
+
clientId: config.appId,
|
|
2964
|
+
redirectUri: config.authOptions.redirectUrl
|
|
3325
2965
|
});
|
|
3326
2966
|
const platformName = getPlatformName();
|
|
3327
2967
|
const { name: browserName, version } = detectBrowser();
|
|
3328
|
-
const authProvider =
|
|
2968
|
+
const authProvider = new Auth2AuthProvider(
|
|
3329
2969
|
stamper,
|
|
3330
2970
|
storage,
|
|
3331
2971
|
urlParamsAccessor,
|
|
3332
2972
|
{
|
|
3333
2973
|
redirectUri: config.authOptions.redirectUrl,
|
|
3334
2974
|
connectLoginUrl: config.authOptions.authUrl,
|
|
3335
|
-
clientId: config.
|
|
3336
|
-
authApiBaseUrl: config.
|
|
2975
|
+
clientId: config.appId,
|
|
2976
|
+
authApiBaseUrl: config.authOptions.authApiBaseUrl
|
|
3337
2977
|
},
|
|
3338
2978
|
{
|
|
3339
2979
|
apiBaseUrl: config.apiBaseUrl,
|
|
3340
2980
|
appId: config.appId
|
|
3341
2981
|
}
|
|
3342
|
-
)
|
|
2982
|
+
);
|
|
3343
2983
|
const platform = {
|
|
3344
2984
|
storage,
|
|
3345
2985
|
authProvider,
|
|
@@ -3355,7 +2995,7 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
|
3355
2995
|
[ANALYTICS_HEADERS.CLIENT]: browserName,
|
|
3356
2996
|
[ANALYTICS_HEADERS.APP_ID]: config.appId,
|
|
3357
2997
|
[ANALYTICS_HEADERS.WALLET_TYPE]: config.embeddedWalletType,
|
|
3358
|
-
[ANALYTICS_HEADERS.SDK_VERSION]: "
|
|
2998
|
+
[ANALYTICS_HEADERS.SDK_VERSION]: "2.0.0-beta.0"
|
|
3359
2999
|
// Replaced at build time
|
|
3360
3000
|
}
|
|
3361
3001
|
};
|
|
@@ -3374,7 +3014,12 @@ var EmbeddedProvider = class extends CoreEmbeddedProvider {
|
|
|
3374
3014
|
import {
|
|
3375
3015
|
EMBEDDED_PROVIDER_AUTH_TYPES
|
|
3376
3016
|
} from "@phantom/embedded-provider-core";
|
|
3377
|
-
import {
|
|
3017
|
+
import {
|
|
3018
|
+
DEFAULT_WALLET_API_URL,
|
|
3019
|
+
DEFAULT_EMBEDDED_WALLET_TYPE,
|
|
3020
|
+
DEFAULT_AUTH_URL as DEFAULT_AUTH_URL2,
|
|
3021
|
+
DEFAULT_AUTH_API_BASE_URL
|
|
3022
|
+
} from "@phantom/constants";
|
|
3378
3023
|
|
|
3379
3024
|
// src/utils/auth-callback.ts
|
|
3380
3025
|
function isAuthFailureCallback(searchParams) {
|
|
@@ -3767,15 +3412,16 @@ var ProviderManager = class {
|
|
|
3767
3412
|
}
|
|
3768
3413
|
const apiBaseUrl = this.config.apiBaseUrl || DEFAULT_WALLET_API_URL;
|
|
3769
3414
|
const authUrl = this.config.authOptions?.authUrl || DEFAULT_AUTH_URL2;
|
|
3415
|
+
const authApiBaseUrl = this.config.authOptions?.authApiBaseUrl || DEFAULT_AUTH_API_BASE_URL;
|
|
3770
3416
|
provider = new EmbeddedProvider({
|
|
3771
3417
|
apiBaseUrl,
|
|
3772
3418
|
appId: this.config.appId,
|
|
3773
3419
|
authOptions: {
|
|
3774
3420
|
...this.config.authOptions || {},
|
|
3775
3421
|
authUrl,
|
|
3776
|
-
redirectUrl: this.config.authOptions?.redirectUrl || this.getValidatedCurrentUrl()
|
|
3422
|
+
redirectUrl: this.config.authOptions?.redirectUrl || this.getValidatedCurrentUrl(),
|
|
3423
|
+
authApiBaseUrl
|
|
3777
3424
|
},
|
|
3778
|
-
unstable__auth2Options: this.config.unstable__auth2Options,
|
|
3779
3425
|
embeddedWalletType: embeddedWalletType || DEFAULT_EMBEDDED_WALLET_TYPE,
|
|
3780
3426
|
addressTypes: this.config.addressTypes || [AddressType.solana]
|
|
3781
3427
|
});
|
|
@@ -4125,6 +3771,7 @@ var BrowserSDK = class {
|
|
|
4125
3771
|
// src/index.ts
|
|
4126
3772
|
import { NetworkId } from "@phantom/constants";
|
|
4127
3773
|
import { AddressType as AddressType4 } from "@phantom/client";
|
|
3774
|
+
import { base64urlEncode, base64urlDecode } from "@phantom/base64url";
|
|
4128
3775
|
import { PHANTOM_ICON as PHANTOM_ICON3 } from "@phantom/constants";
|
|
4129
3776
|
export {
|
|
4130
3777
|
AddressType4 as AddressType,
|
|
@@ -4133,6 +3780,8 @@ export {
|
|
|
4133
3780
|
DebugLevel,
|
|
4134
3781
|
NetworkId,
|
|
4135
3782
|
PHANTOM_ICON3 as PHANTOM_ICON,
|
|
3783
|
+
base64urlDecode,
|
|
3784
|
+
base64urlEncode,
|
|
4136
3785
|
debug,
|
|
4137
3786
|
detectBrowser,
|
|
4138
3787
|
getBrowserDisplayName,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phantom/browser-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.0",
|
|
4
4
|
"description": "Browser SDK for Phantom Wallet",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -33,16 +33,16 @@
|
|
|
33
33
|
"prettier": "prettier --write \"src/**/*.{ts,tsx}\""
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@phantom/auth2": "^
|
|
37
|
-
"@phantom/base64url": "^
|
|
38
|
-
"@phantom/browser-injected-sdk": "^
|
|
39
|
-
"@phantom/chain-interfaces": "^
|
|
40
|
-
"@phantom/client": "^
|
|
41
|
-
"@phantom/constants": "^
|
|
42
|
-
"@phantom/embedded-provider-core": "^
|
|
43
|
-
"@phantom/indexed-db-stamper": "^
|
|
44
|
-
"@phantom/parsers": "^
|
|
45
|
-
"@phantom/sdk-types": "^
|
|
36
|
+
"@phantom/auth2": "^2.0.0-beta.0",
|
|
37
|
+
"@phantom/base64url": "^2.0.0-beta.0",
|
|
38
|
+
"@phantom/browser-injected-sdk": "^2.0.0-beta.0",
|
|
39
|
+
"@phantom/chain-interfaces": "^2.0.0-beta.0",
|
|
40
|
+
"@phantom/client": "^2.0.0-beta.0",
|
|
41
|
+
"@phantom/constants": "^2.0.0-beta.0",
|
|
42
|
+
"@phantom/embedded-provider-core": "^2.0.0-beta.0",
|
|
43
|
+
"@phantom/indexed-db-stamper": "^2.0.0-beta.0",
|
|
44
|
+
"@phantom/parsers": "^2.0.0-beta.0",
|
|
45
|
+
"@phantom/sdk-types": "^2.0.0-beta.0",
|
|
46
46
|
"axios": "^1.10.0",
|
|
47
47
|
"bs58": "^6.0.0",
|
|
48
48
|
"buffer": "^6.0.3",
|