@b3dotfun/sdk 0.1.70-alpha.3 → 0.1.70-alpha.4
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/cjs/app.shared.d.ts +8 -0
- package/dist/cjs/app.shared.js +19 -0
- package/dist/cjs/global-account/better-auth-client.d.ts +8 -0
- package/dist/cjs/global-account/better-auth-client.js +26 -3
- package/dist/cjs/global-account/client-manager.d.ts +2 -0
- package/dist/cjs/global-account/client-manager.js +19 -4
- package/dist/cjs/global-account/react/components/B3Provider/B3Provider.d.ts +10 -1
- package/dist/cjs/global-account/react/components/B3Provider/B3Provider.js +15 -1
- package/dist/cjs/global-account/react/components/B3Provider/BetterAuthProvider.js +45 -4
- package/dist/cjs/global-account/react/hooks/useBetterAuth.js +7 -7
- package/dist/esm/app.shared.d.ts +8 -0
- package/dist/esm/app.shared.js +17 -0
- package/dist/esm/global-account/better-auth-client.d.ts +8 -0
- package/dist/esm/global-account/better-auth-client.js +25 -4
- package/dist/esm/global-account/client-manager.d.ts +2 -0
- package/dist/esm/global-account/client-manager.js +19 -5
- package/dist/esm/global-account/react/components/B3Provider/B3Provider.d.ts +10 -1
- package/dist/esm/global-account/react/components/B3Provider/B3Provider.js +16 -2
- package/dist/esm/global-account/react/components/B3Provider/BetterAuthProvider.js +46 -5
- package/dist/esm/global-account/react/hooks/useBetterAuth.js +8 -8
- package/dist/types/app.shared.d.ts +8 -0
- package/dist/types/global-account/better-auth-client.d.ts +8 -0
- package/dist/types/global-account/client-manager.d.ts +2 -0
- package/dist/types/global-account/react/components/B3Provider/B3Provider.d.ts +10 -1
- package/package.json +1 -1
- package/src/app.shared.ts +20 -0
- package/src/global-account/better-auth-client.ts +28 -4
- package/src/global-account/client-manager.ts +20 -5
- package/src/global-account/react/components/B3Provider/B3Provider.tsx +26 -1
- package/src/global-account/react/components/B3Provider/BetterAuthProvider.tsx +51 -5
- package/src/global-account/react/hooks/useBetterAuth.ts +8 -8
package/dist/cjs/app.shared.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { ClientApplication } from "@b3dotfun/b3-api";
|
|
2
2
|
import { AuthenticationClient } from "@feathersjs/authentication-client";
|
|
3
|
+
/** Default API URL derived from environment variables. */
|
|
3
4
|
export declare const B3_API_URL: string;
|
|
5
|
+
/**
|
|
6
|
+
* Override the B3 API URL at runtime. Called by `B3Provider` when an
|
|
7
|
+
* `apiUrl` prop is provided. Pass `null` to revert to the default.
|
|
8
|
+
*/
|
|
9
|
+
export declare function setB3ApiUrl(url: string | null): void;
|
|
10
|
+
/** Get the current B3 API URL (prefers runtime override, then env var). */
|
|
11
|
+
export declare function getB3ApiUrl(): string;
|
|
4
12
|
export declare const authenticate: (app: ClientApplication, accessToken: string, identityToken: string, params?: Record<string, any>) => Promise<import("@feathersjs/authentication").AuthenticationResult | null>;
|
|
5
13
|
export declare class MyAuthenticationClient extends AuthenticationClient {
|
|
6
14
|
getFromLocation(location: any): Promise<any>;
|
package/dist/cjs/app.shared.js
CHANGED
|
@@ -4,11 +4,30 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.clientOptions = exports.MyAuthenticationClient = exports.authenticate = exports.B3_API_URL = void 0;
|
|
7
|
+
exports.setB3ApiUrl = setB3ApiUrl;
|
|
8
|
+
exports.getB3ApiUrl = getB3ApiUrl;
|
|
7
9
|
const authentication_client_1 = require("@feathersjs/authentication-client");
|
|
8
10
|
const js_cookie_1 = __importDefault(require("js-cookie"));
|
|
9
11
|
const constants_1 = require("./shared/constants");
|
|
10
12
|
const session_duration_1 = require("./shared/utils/session-duration");
|
|
13
|
+
/** Default API URL derived from environment variables. */
|
|
11
14
|
exports.B3_API_URL = process.env.EXPO_PUBLIC_B3_API || process.env.NEXT_PUBLIC_B3_API || process.env.PUBLIC_B3_API || "https://api.b3.fun";
|
|
15
|
+
/**
|
|
16
|
+
* Runtime-overridable API URL. Set by `B3Provider` when an `apiUrl` prop
|
|
17
|
+
* is provided. Falls back to the env-var-derived `B3_API_URL`.
|
|
18
|
+
*/
|
|
19
|
+
let _apiUrlOverride = null;
|
|
20
|
+
/**
|
|
21
|
+
* Override the B3 API URL at runtime. Called by `B3Provider` when an
|
|
22
|
+
* `apiUrl` prop is provided. Pass `null` to revert to the default.
|
|
23
|
+
*/
|
|
24
|
+
function setB3ApiUrl(url) {
|
|
25
|
+
_apiUrlOverride = url;
|
|
26
|
+
}
|
|
27
|
+
/** Get the current B3 API URL (prefers runtime override, then env var). */
|
|
28
|
+
function getB3ApiUrl() {
|
|
29
|
+
return _apiUrlOverride || exports.B3_API_URL;
|
|
30
|
+
}
|
|
12
31
|
const authenticate = async (app, accessToken, identityToken, params) => {
|
|
13
32
|
const fullToken = `${accessToken}+${identityToken}`;
|
|
14
33
|
// Do not authenticate if there is no token
|
|
@@ -940,6 +940,14 @@ export declare function createB3BetterAuthClient(baseURL?: string): {
|
|
|
940
940
|
};
|
|
941
941
|
};
|
|
942
942
|
};
|
|
943
|
+
/** Get the shared BetterAuth client, creating it if needed. */
|
|
944
|
+
export declare function getBetterAuthClient(): B3BetterAuthClient;
|
|
945
|
+
/** Force-recreate the client on next access (e.g. after API URL change). */
|
|
946
|
+
export declare function resetBetterAuthClient(): void;
|
|
947
|
+
/**
|
|
948
|
+
* @deprecated Use `getBetterAuthClient()` instead. This static singleton
|
|
949
|
+
* does not reflect runtime API URL changes via `B3Provider apiUrl` prop.
|
|
950
|
+
*/
|
|
943
951
|
export declare const betterAuthClient: {
|
|
944
952
|
signIn: {
|
|
945
953
|
social: <FetchOptions extends import("@better-auth/core").ClientFetchOption<Partial<{
|
|
@@ -2,16 +2,39 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.betterAuthClient = void 0;
|
|
4
4
|
exports.createB3BetterAuthClient = createB3BetterAuthClient;
|
|
5
|
+
exports.getBetterAuthClient = getBetterAuthClient;
|
|
6
|
+
exports.resetBetterAuthClient = resetBetterAuthClient;
|
|
5
7
|
const client_1 = require("better-auth/client");
|
|
6
8
|
const app_shared_1 = require("../app.shared");
|
|
7
|
-
function createB3BetterAuthClient(baseURL
|
|
9
|
+
function createB3BetterAuthClient(baseURL) {
|
|
8
10
|
return (0, client_1.createAuthClient)({
|
|
9
|
-
baseURL,
|
|
11
|
+
baseURL: baseURL ?? (0, app_shared_1.getB3ApiUrl)(),
|
|
10
12
|
basePath: "/auth",
|
|
11
13
|
fetchOptions: {
|
|
12
14
|
credentials: "include",
|
|
13
15
|
},
|
|
14
16
|
});
|
|
15
17
|
}
|
|
16
|
-
//
|
|
18
|
+
// Lazily-initialized singleton. Recreated when the API URL changes via
|
|
19
|
+
// `resetBetterAuthClient()` (called by B3Provider when `apiUrl` changes).
|
|
20
|
+
let _client = null;
|
|
21
|
+
let _clientUrl = null;
|
|
22
|
+
/** Get the shared BetterAuth client, creating it if needed. */
|
|
23
|
+
function getBetterAuthClient() {
|
|
24
|
+
const url = (0, app_shared_1.getB3ApiUrl)();
|
|
25
|
+
if (!_client || _clientUrl !== url) {
|
|
26
|
+
_client = createB3BetterAuthClient(url);
|
|
27
|
+
_clientUrl = url;
|
|
28
|
+
}
|
|
29
|
+
return _client;
|
|
30
|
+
}
|
|
31
|
+
/** Force-recreate the client on next access (e.g. after API URL change). */
|
|
32
|
+
function resetBetterAuthClient() {
|
|
33
|
+
_client = null;
|
|
34
|
+
_clientUrl = null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* @deprecated Use `getBetterAuthClient()` instead. This static singleton
|
|
38
|
+
* does not reflect runtime API URL changes via `B3Provider apiUrl` prop.
|
|
39
|
+
*/
|
|
17
40
|
exports.betterAuthClient = createB3BetterAuthClient();
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ClientApplication } from "@b3dotfun/b3-api";
|
|
2
2
|
export type ClientType = "socket" | "rest";
|
|
3
|
+
/** Reset all cached clients (called when API URL changes). */
|
|
4
|
+
export declare function resetClients(): void;
|
|
3
5
|
/**
|
|
4
6
|
* Sets the active client type and creates the appropriate client
|
|
5
7
|
*
|
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.authenticateBoth = exports.authenticateWithClient = exports.authenticate = void 0;
|
|
7
|
+
exports.resetClients = resetClients;
|
|
7
8
|
exports.setClientType = setClientType;
|
|
8
9
|
exports.getClient = getClient;
|
|
9
10
|
exports.getClientType = getClientType;
|
|
@@ -21,15 +22,19 @@ let currentClient = null;
|
|
|
21
22
|
// Socket client instance
|
|
22
23
|
let socketClient = null;
|
|
23
24
|
let socketInstance = null;
|
|
25
|
+
let socketClientUrl = null;
|
|
24
26
|
// REST client instance
|
|
25
27
|
let restClient = null;
|
|
28
|
+
let restClientUrl = null;
|
|
26
29
|
/**
|
|
27
30
|
* Creates a socket client
|
|
28
31
|
*/
|
|
29
32
|
function createSocketClient() {
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
const url = (0, app_shared_1.getB3ApiUrl)();
|
|
34
|
+
if (!socketClient || socketClientUrl !== url) {
|
|
35
|
+
socketInstance = (0, socket_io_client_1.default)(url, { transports: ["websocket"] });
|
|
32
36
|
socketClient = (0, b3_api_1.createClient)((0, socketio_client_1.default)(socketInstance), app_shared_1.clientOptions);
|
|
37
|
+
socketClientUrl = url;
|
|
33
38
|
}
|
|
34
39
|
return socketClient;
|
|
35
40
|
}
|
|
@@ -45,12 +50,22 @@ function resolveFetch() {
|
|
|
45
50
|
return require("cross-fetch").fetch;
|
|
46
51
|
}
|
|
47
52
|
function createRestClient() {
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
const url = (0, app_shared_1.getB3ApiUrl)();
|
|
54
|
+
if (!restClient || restClientUrl !== url) {
|
|
55
|
+
const connection = (0, rest_client_1.default)(url).fetch(resolveFetch());
|
|
50
56
|
restClient = (0, b3_api_1.createClient)(connection, app_shared_1.clientOptions);
|
|
57
|
+
restClientUrl = url;
|
|
51
58
|
}
|
|
52
59
|
return restClient;
|
|
53
60
|
}
|
|
61
|
+
/** Reset all cached clients (called when API URL changes). */
|
|
62
|
+
function resetClients() {
|
|
63
|
+
socketClient = null;
|
|
64
|
+
socketClientUrl = null;
|
|
65
|
+
restClient = null;
|
|
66
|
+
restClientUrl = null;
|
|
67
|
+
currentClient = null;
|
|
68
|
+
}
|
|
54
69
|
/**
|
|
55
70
|
* Sets the active client type and creates the appropriate client
|
|
56
71
|
*
|
|
@@ -10,7 +10,7 @@ import { ClientType } from "../../../client-manager";
|
|
|
10
10
|
/**
|
|
11
11
|
* Main B3Provider component
|
|
12
12
|
*/
|
|
13
|
-
export declare function B3Provider({ theme, children, accountOverride, environment, automaticallySetFirstEoa, defaultEoaProvider, simDuneApiKey, toaster: _toaster, clientType, rpcUrls, partnerId, stripePublishableKey, onConnect, onLogout, connectors, overrideDefaultConnectors, createClientReferenceId, defaultPermissions, disableBSMNTAuthentication, queryClient, authStrategy, }: {
|
|
13
|
+
export declare function B3Provider({ theme, children, accountOverride, environment, automaticallySetFirstEoa, defaultEoaProvider, simDuneApiKey, toaster: _toaster, clientType, rpcUrls, partnerId, stripePublishableKey, onConnect, onLogout, connectors, overrideDefaultConnectors, createClientReferenceId, defaultPermissions, disableBSMNTAuthentication, queryClient, authStrategy, apiUrl, }: {
|
|
14
14
|
theme: "light" | "dark";
|
|
15
15
|
children: React.ReactNode;
|
|
16
16
|
accountOverride?: Account;
|
|
@@ -39,4 +39,13 @@ export declare function B3Provider({ theme, children, accountOverride, environme
|
|
|
39
39
|
queryClient?: QueryClient;
|
|
40
40
|
/** Auth strategy: "thirdweb" (default, ecosystem wallet) or "better-auth" (email/password via Better Auth) */
|
|
41
41
|
authStrategy?: AuthStrategy;
|
|
42
|
+
/**
|
|
43
|
+
* Override the B3 API URL. When provided, all SDK requests (auth, Feathers,
|
|
44
|
+
* BetterAuth) will use this URL instead of the `NEXT_PUBLIC_B3_API` env var.
|
|
45
|
+
*
|
|
46
|
+
* Useful for local development: pass `http://localhost:3031` to route
|
|
47
|
+
* OAuth callbacks and session checks through your local B3 API.
|
|
48
|
+
* If omitted, defaults to `NEXT_PUBLIC_B3_API` or `https://api.b3.fun`.
|
|
49
|
+
*/
|
|
50
|
+
apiUrl?: string;
|
|
42
51
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -12,6 +12,8 @@ const react_2 = require("../../../../wallet/react");
|
|
|
12
12
|
require("@relayprotocol/relay-kit-ui/styles.css");
|
|
13
13
|
const react_3 = require("react");
|
|
14
14
|
const client_manager_1 = require("../../../client-manager");
|
|
15
|
+
const app_shared_1 = require("../../../../app.shared");
|
|
16
|
+
const better_auth_client_1 = require("../../../better-auth-client");
|
|
15
17
|
const StyleRoot_1 = require("../StyleRoot");
|
|
16
18
|
const index_1 = require("../Toast/index");
|
|
17
19
|
const AuthenticationProvider_1 = __importDefault(require("./AuthenticationProvider"));
|
|
@@ -23,7 +25,19 @@ const LocalSDKProvider_1 = require("./LocalSDKProvider");
|
|
|
23
25
|
*/
|
|
24
26
|
function B3Provider({ theme = "light", children, accountOverride, environment, automaticallySetFirstEoa, defaultEoaProvider, simDuneApiKey,
|
|
25
27
|
// deprecated since v0.0.87
|
|
26
|
-
toaster: _toaster, clientType = "rest", rpcUrls, partnerId, stripePublishableKey, onConnect, onLogout, connectors, overrideDefaultConnectors = false, createClientReferenceId, defaultPermissions, disableBSMNTAuthentication = false, queryClient, authStrategy = "thirdweb", }) {
|
|
28
|
+
toaster: _toaster, clientType = "rest", rpcUrls, partnerId, stripePublishableKey, onConnect, onLogout, connectors, overrideDefaultConnectors = false, createClientReferenceId, defaultPermissions, disableBSMNTAuthentication = false, queryClient, authStrategy = "thirdweb", apiUrl, }) {
|
|
29
|
+
// Override the B3 API URL when the `apiUrl` prop is provided.
|
|
30
|
+
// Must run before client initialization and auth providers mount.
|
|
31
|
+
(0, react_3.useEffect)(() => {
|
|
32
|
+
(0, app_shared_1.setB3ApiUrl)(apiUrl ?? null);
|
|
33
|
+
if (apiUrl) {
|
|
34
|
+
(0, better_auth_client_1.resetBetterAuthClient)();
|
|
35
|
+
(0, client_manager_1.resetClients)();
|
|
36
|
+
}
|
|
37
|
+
return () => {
|
|
38
|
+
(0, app_shared_1.setB3ApiUrl)(null);
|
|
39
|
+
};
|
|
40
|
+
}, [apiUrl]);
|
|
27
41
|
// Initialize Google Analytics on mount
|
|
28
42
|
(0, react_3.useEffect)(() => {
|
|
29
43
|
(0, analytics_1.loadGA4Script)();
|
|
@@ -45,7 +45,7 @@ const BetterAuthProvider = ({ partnerId }) => {
|
|
|
45
45
|
app_1.default.logout = async () => {
|
|
46
46
|
debug("Patched logout: clearing Better Auth session");
|
|
47
47
|
try {
|
|
48
|
-
await better_auth_client_1.
|
|
48
|
+
await (0, better_auth_client_1.getBetterAuthClient)().signOut();
|
|
49
49
|
}
|
|
50
50
|
catch {
|
|
51
51
|
debug("Better Auth signOut failed (non-critical)");
|
|
@@ -79,9 +79,50 @@ const BetterAuthProvider = ({ partnerId }) => {
|
|
|
79
79
|
catch {
|
|
80
80
|
debug("No existing Feathers JWT");
|
|
81
81
|
}
|
|
82
|
-
// 2. Check for
|
|
82
|
+
// 2. Check for _ba_token in URL (OAuth callback with third-party cookie bypass).
|
|
83
|
+
// When the API and frontend are on different domains, browsers with third-party
|
|
84
|
+
// cookie blocking (Firefox TCP, Safari ITP, Chrome Privacy Sandbox) prevent the
|
|
85
|
+
// better-auth.session_token cookie from being sent on cross-origin requests.
|
|
86
|
+
// The B3 API injects the session token into the redirect URL as _ba_token,
|
|
87
|
+
// allowing direct exchange without relying on cookies.
|
|
88
|
+
const params = new URLSearchParams(window.location.search);
|
|
89
|
+
const baToken = params.get("_ba_token");
|
|
90
|
+
if (baToken) {
|
|
91
|
+
debug("Found _ba_token in URL, exchanging for Feathers JWT");
|
|
92
|
+
// Clean the token from the URL immediately (security)
|
|
93
|
+
params.delete("_ba_token");
|
|
94
|
+
const cleanSearch = params.toString();
|
|
95
|
+
const cleanUrl = window.location.pathname + (cleanSearch ? `?${cleanSearch}` : "") + window.location.hash;
|
|
96
|
+
window.history.replaceState({}, "", cleanUrl);
|
|
97
|
+
try {
|
|
98
|
+
const response = await app_1.default.authenticate({
|
|
99
|
+
strategy: "better-auth",
|
|
100
|
+
accessToken: baToken,
|
|
101
|
+
partnerId,
|
|
102
|
+
});
|
|
103
|
+
if (response.accessToken) {
|
|
104
|
+
js_cookie_1.default.set(constants_1.B3_AUTH_COOKIE_NAME, response.accessToken, {
|
|
105
|
+
secure: true,
|
|
106
|
+
sameSite: "Lax",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (response.user) {
|
|
110
|
+
setUser(response.user);
|
|
111
|
+
setIsAuthenticated(true);
|
|
112
|
+
setIsConnected(true);
|
|
113
|
+
}
|
|
114
|
+
debug("_ba_token exchanged for Feathers JWT", { userId: response.user?.userId });
|
|
115
|
+
setIsAuthenticating(false);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
debug("_ba_token exchange failed", err);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// 3. Check for a Better Auth session via cookie (works when API and frontend
|
|
123
|
+
// share a domain or when cookies aren't partitioned)
|
|
83
124
|
try {
|
|
84
|
-
const session = await better_auth_client_1.
|
|
125
|
+
const session = await (0, better_auth_client_1.getBetterAuthClient)().getSession();
|
|
85
126
|
if (session.data?.session?.token) {
|
|
86
127
|
debug("Better Auth session found, exchanging for Feathers JWT", {
|
|
87
128
|
betterAuthUserId: session.data.user?.id,
|
|
@@ -110,7 +151,7 @@ const BetterAuthProvider = ({ partnerId }) => {
|
|
|
110
151
|
catch {
|
|
111
152
|
debug("No Better Auth session to restore");
|
|
112
153
|
}
|
|
113
|
-
//
|
|
154
|
+
// 4. Nothing found — show login UI
|
|
114
155
|
setIsAuthenticating(false);
|
|
115
156
|
};
|
|
116
157
|
restoreSession();
|
|
@@ -57,7 +57,7 @@ function useBetterAuth() {
|
|
|
57
57
|
setHasStartedConnecting(true);
|
|
58
58
|
setIsAuthenticating(true);
|
|
59
59
|
try {
|
|
60
|
-
const result = await better_auth_client_1.
|
|
60
|
+
const result = await (0, better_auth_client_1.getBetterAuthClient)().signIn.email({ email, password });
|
|
61
61
|
if (result.error) {
|
|
62
62
|
throw new Error(result.error.message || "Sign in failed");
|
|
63
63
|
}
|
|
@@ -78,7 +78,7 @@ function useBetterAuth() {
|
|
|
78
78
|
setHasStartedConnecting(true);
|
|
79
79
|
setIsAuthenticating(true);
|
|
80
80
|
try {
|
|
81
|
-
const result = await better_auth_client_1.
|
|
81
|
+
const result = await (0, better_auth_client_1.getBetterAuthClient)().signUp.email({ email, password, name });
|
|
82
82
|
if (result.error) {
|
|
83
83
|
throw new Error(result.error.message || "Sign up failed");
|
|
84
84
|
}
|
|
@@ -87,7 +87,7 @@ function useBetterAuth() {
|
|
|
87
87
|
// requireEmailVerification is enabled — send verification email with
|
|
88
88
|
// a callbackURL Better Auth redirects to after server-side verify.
|
|
89
89
|
// Pass verifyCallbackURL to land on a dedicated confirmation page.
|
|
90
|
-
await better_auth_client_1.
|
|
90
|
+
await (0, better_auth_client_1.getBetterAuthClient)().sendVerificationEmail({
|
|
91
91
|
email,
|
|
92
92
|
callbackURL: verifyCallbackURL || `${window.location.origin}?authStrategy=better-auth`,
|
|
93
93
|
});
|
|
@@ -106,7 +106,7 @@ function useBetterAuth() {
|
|
|
106
106
|
setHasStartedConnecting(true);
|
|
107
107
|
setIsAuthenticating(true);
|
|
108
108
|
try {
|
|
109
|
-
const result = await better_auth_client_1.
|
|
109
|
+
const result = await (0, better_auth_client_1.getBetterAuthClient)().signIn.social({
|
|
110
110
|
provider,
|
|
111
111
|
callbackURL: window.location.href,
|
|
112
112
|
});
|
|
@@ -124,7 +124,7 @@ function useBetterAuth() {
|
|
|
124
124
|
}, [setIsAuthenticating, setHasStartedConnecting]);
|
|
125
125
|
const requestPasswordReset = (0, react_2.useCallback)(async (email, redirectTo) => {
|
|
126
126
|
debug("Requesting password reset", { email });
|
|
127
|
-
const result = await better_auth_client_1.
|
|
127
|
+
const result = await (0, better_auth_client_1.getBetterAuthClient)().requestPasswordReset({
|
|
128
128
|
email,
|
|
129
129
|
redirectTo,
|
|
130
130
|
});
|
|
@@ -136,7 +136,7 @@ function useBetterAuth() {
|
|
|
136
136
|
}, []);
|
|
137
137
|
const resetPassword = (0, react_2.useCallback)(async (newPassword, token) => {
|
|
138
138
|
debug("Resetting password");
|
|
139
|
-
const result = await better_auth_client_1.
|
|
139
|
+
const result = await (0, better_auth_client_1.getBetterAuthClient)().resetPassword({
|
|
140
140
|
newPassword,
|
|
141
141
|
token,
|
|
142
142
|
});
|
|
@@ -153,6 +153,6 @@ function useBetterAuth() {
|
|
|
153
153
|
requestPasswordReset,
|
|
154
154
|
resetPassword,
|
|
155
155
|
exchangeForFeathersJWT,
|
|
156
|
-
betterAuthClient: better_auth_client_1.
|
|
156
|
+
betterAuthClient: (0, better_auth_client_1.getBetterAuthClient)(),
|
|
157
157
|
};
|
|
158
158
|
}
|
package/dist/esm/app.shared.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { ClientApplication } from "@b3dotfun/b3-api";
|
|
2
2
|
import { AuthenticationClient } from "@feathersjs/authentication-client";
|
|
3
|
+
/** Default API URL derived from environment variables. */
|
|
3
4
|
export declare const B3_API_URL: string;
|
|
5
|
+
/**
|
|
6
|
+
* Override the B3 API URL at runtime. Called by `B3Provider` when an
|
|
7
|
+
* `apiUrl` prop is provided. Pass `null` to revert to the default.
|
|
8
|
+
*/
|
|
9
|
+
export declare function setB3ApiUrl(url: string | null): void;
|
|
10
|
+
/** Get the current B3 API URL (prefers runtime override, then env var). */
|
|
11
|
+
export declare function getB3ApiUrl(): string;
|
|
4
12
|
export declare const authenticate: (app: ClientApplication, accessToken: string, identityToken: string, params?: Record<string, any>) => Promise<import("@feathersjs/authentication").AuthenticationResult | null>;
|
|
5
13
|
export declare class MyAuthenticationClient extends AuthenticationClient {
|
|
6
14
|
getFromLocation(location: any): Promise<any>;
|
package/dist/esm/app.shared.js
CHANGED
|
@@ -2,7 +2,24 @@ import { AuthenticationClient } from "@feathersjs/authentication-client";
|
|
|
2
2
|
import Cookies from "js-cookie";
|
|
3
3
|
import { B3_AUTH_COOKIE_NAME } from "./shared/constants/index.js";
|
|
4
4
|
import { getSessionDurationDays } from "./shared/utils/session-duration.js";
|
|
5
|
+
/** Default API URL derived from environment variables. */
|
|
5
6
|
export const B3_API_URL = process.env.EXPO_PUBLIC_B3_API || process.env.NEXT_PUBLIC_B3_API || process.env.PUBLIC_B3_API || "https://api.b3.fun";
|
|
7
|
+
/**
|
|
8
|
+
* Runtime-overridable API URL. Set by `B3Provider` when an `apiUrl` prop
|
|
9
|
+
* is provided. Falls back to the env-var-derived `B3_API_URL`.
|
|
10
|
+
*/
|
|
11
|
+
let _apiUrlOverride = null;
|
|
12
|
+
/**
|
|
13
|
+
* Override the B3 API URL at runtime. Called by `B3Provider` when an
|
|
14
|
+
* `apiUrl` prop is provided. Pass `null` to revert to the default.
|
|
15
|
+
*/
|
|
16
|
+
export function setB3ApiUrl(url) {
|
|
17
|
+
_apiUrlOverride = url;
|
|
18
|
+
}
|
|
19
|
+
/** Get the current B3 API URL (prefers runtime override, then env var). */
|
|
20
|
+
export function getB3ApiUrl() {
|
|
21
|
+
return _apiUrlOverride || B3_API_URL;
|
|
22
|
+
}
|
|
6
23
|
export const authenticate = async (app, accessToken, identityToken, params) => {
|
|
7
24
|
const fullToken = `${accessToken}+${identityToken}`;
|
|
8
25
|
// Do not authenticate if there is no token
|
|
@@ -940,6 +940,14 @@ export declare function createB3BetterAuthClient(baseURL?: string): {
|
|
|
940
940
|
};
|
|
941
941
|
};
|
|
942
942
|
};
|
|
943
|
+
/** Get the shared BetterAuth client, creating it if needed. */
|
|
944
|
+
export declare function getBetterAuthClient(): B3BetterAuthClient;
|
|
945
|
+
/** Force-recreate the client on next access (e.g. after API URL change). */
|
|
946
|
+
export declare function resetBetterAuthClient(): void;
|
|
947
|
+
/**
|
|
948
|
+
* @deprecated Use `getBetterAuthClient()` instead. This static singleton
|
|
949
|
+
* does not reflect runtime API URL changes via `B3Provider apiUrl` prop.
|
|
950
|
+
*/
|
|
943
951
|
export declare const betterAuthClient: {
|
|
944
952
|
signIn: {
|
|
945
953
|
social: <FetchOptions extends import("@better-auth/core").ClientFetchOption<Partial<{
|
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
import { createAuthClient } from "better-auth/client";
|
|
2
|
-
import {
|
|
3
|
-
export function createB3BetterAuthClient(baseURL
|
|
2
|
+
import { getB3ApiUrl } from "../app.shared.js";
|
|
3
|
+
export function createB3BetterAuthClient(baseURL) {
|
|
4
4
|
return createAuthClient({
|
|
5
|
-
baseURL,
|
|
5
|
+
baseURL: baseURL ?? getB3ApiUrl(),
|
|
6
6
|
basePath: "/auth",
|
|
7
7
|
fetchOptions: {
|
|
8
8
|
credentials: "include",
|
|
9
9
|
},
|
|
10
10
|
});
|
|
11
11
|
}
|
|
12
|
-
//
|
|
12
|
+
// Lazily-initialized singleton. Recreated when the API URL changes via
|
|
13
|
+
// `resetBetterAuthClient()` (called by B3Provider when `apiUrl` changes).
|
|
14
|
+
let _client = null;
|
|
15
|
+
let _clientUrl = null;
|
|
16
|
+
/** Get the shared BetterAuth client, creating it if needed. */
|
|
17
|
+
export function getBetterAuthClient() {
|
|
18
|
+
const url = getB3ApiUrl();
|
|
19
|
+
if (!_client || _clientUrl !== url) {
|
|
20
|
+
_client = createB3BetterAuthClient(url);
|
|
21
|
+
_clientUrl = url;
|
|
22
|
+
}
|
|
23
|
+
return _client;
|
|
24
|
+
}
|
|
25
|
+
/** Force-recreate the client on next access (e.g. after API URL change). */
|
|
26
|
+
export function resetBetterAuthClient() {
|
|
27
|
+
_client = null;
|
|
28
|
+
_clientUrl = null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @deprecated Use `getBetterAuthClient()` instead. This static singleton
|
|
32
|
+
* does not reflect runtime API URL changes via `B3Provider apiUrl` prop.
|
|
33
|
+
*/
|
|
13
34
|
export const betterAuthClient = createB3BetterAuthClient();
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ClientApplication } from "@b3dotfun/b3-api";
|
|
2
2
|
export type ClientType = "socket" | "rest";
|
|
3
|
+
/** Reset all cached clients (called when API URL changes). */
|
|
4
|
+
export declare function resetClients(): void;
|
|
3
5
|
/**
|
|
4
6
|
* Sets the active client type and creates the appropriate client
|
|
5
7
|
*
|
|
@@ -2,22 +2,26 @@ import { createClient } from "@b3dotfun/b3-api";
|
|
|
2
2
|
import rest from "@feathersjs/rest-client";
|
|
3
3
|
import socketio from "@feathersjs/socketio-client";
|
|
4
4
|
import io from "socket.io-client";
|
|
5
|
-
import { authenticate as authenticateB3,
|
|
5
|
+
import { authenticate as authenticateB3, getB3ApiUrl, clientOptions } from "../app.shared.js";
|
|
6
6
|
// Global state to track which client type is active
|
|
7
7
|
let currentClientType = "rest";
|
|
8
8
|
let currentClient = null;
|
|
9
9
|
// Socket client instance
|
|
10
10
|
let socketClient = null;
|
|
11
11
|
let socketInstance = null;
|
|
12
|
+
let socketClientUrl = null;
|
|
12
13
|
// REST client instance
|
|
13
14
|
let restClient = null;
|
|
15
|
+
let restClientUrl = null;
|
|
14
16
|
/**
|
|
15
17
|
* Creates a socket client
|
|
16
18
|
*/
|
|
17
19
|
function createSocketClient() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
const url = getB3ApiUrl();
|
|
21
|
+
if (!socketClient || socketClientUrl !== url) {
|
|
22
|
+
socketInstance = io(url, { transports: ["websocket"] });
|
|
20
23
|
socketClient = createClient(socketio(socketInstance), clientOptions);
|
|
24
|
+
socketClientUrl = url;
|
|
21
25
|
}
|
|
22
26
|
return socketClient;
|
|
23
27
|
}
|
|
@@ -33,12 +37,22 @@ function resolveFetch() {
|
|
|
33
37
|
return require("cross-fetch").fetch;
|
|
34
38
|
}
|
|
35
39
|
function createRestClient() {
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
const url = getB3ApiUrl();
|
|
41
|
+
if (!restClient || restClientUrl !== url) {
|
|
42
|
+
const connection = rest(url).fetch(resolveFetch());
|
|
38
43
|
restClient = createClient(connection, clientOptions);
|
|
44
|
+
restClientUrl = url;
|
|
39
45
|
}
|
|
40
46
|
return restClient;
|
|
41
47
|
}
|
|
48
|
+
/** Reset all cached clients (called when API URL changes). */
|
|
49
|
+
export function resetClients() {
|
|
50
|
+
socketClient = null;
|
|
51
|
+
socketClientUrl = null;
|
|
52
|
+
restClient = null;
|
|
53
|
+
restClientUrl = null;
|
|
54
|
+
currentClient = null;
|
|
55
|
+
}
|
|
42
56
|
/**
|
|
43
57
|
* Sets the active client type and creates the appropriate client
|
|
44
58
|
*
|
|
@@ -10,7 +10,7 @@ import { ClientType } from "../../../client-manager";
|
|
|
10
10
|
/**
|
|
11
11
|
* Main B3Provider component
|
|
12
12
|
*/
|
|
13
|
-
export declare function B3Provider({ theme, children, accountOverride, environment, automaticallySetFirstEoa, defaultEoaProvider, simDuneApiKey, toaster: _toaster, clientType, rpcUrls, partnerId, stripePublishableKey, onConnect, onLogout, connectors, overrideDefaultConnectors, createClientReferenceId, defaultPermissions, disableBSMNTAuthentication, queryClient, authStrategy, }: {
|
|
13
|
+
export declare function B3Provider({ theme, children, accountOverride, environment, automaticallySetFirstEoa, defaultEoaProvider, simDuneApiKey, toaster: _toaster, clientType, rpcUrls, partnerId, stripePublishableKey, onConnect, onLogout, connectors, overrideDefaultConnectors, createClientReferenceId, defaultPermissions, disableBSMNTAuthentication, queryClient, authStrategy, apiUrl, }: {
|
|
14
14
|
theme: "light" | "dark";
|
|
15
15
|
children: React.ReactNode;
|
|
16
16
|
accountOverride?: Account;
|
|
@@ -39,4 +39,13 @@ export declare function B3Provider({ theme, children, accountOverride, environme
|
|
|
39
39
|
queryClient?: QueryClient;
|
|
40
40
|
/** Auth strategy: "thirdweb" (default, ecosystem wallet) or "better-auth" (email/password via Better Auth) */
|
|
41
41
|
authStrategy?: AuthStrategy;
|
|
42
|
+
/**
|
|
43
|
+
* Override the B3 API URL. When provided, all SDK requests (auth, Feathers,
|
|
44
|
+
* BetterAuth) will use this URL instead of the `NEXT_PUBLIC_B3_API` env var.
|
|
45
|
+
*
|
|
46
|
+
* Useful for local development: pass `http://localhost:3031` to route
|
|
47
|
+
* OAuth callbacks and session checks through your local B3 API.
|
|
48
|
+
* If omitted, defaults to `NEXT_PUBLIC_B3_API` or `https://api.b3.fun`.
|
|
49
|
+
*/
|
|
50
|
+
apiUrl?: string;
|
|
42
51
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -5,7 +5,9 @@ import { loadGA4Script } from "../../../../global-account/utils/analytics.js";
|
|
|
5
5
|
import { WalletProvider } from "../../../../wallet/react/index.js";
|
|
6
6
|
import "@relayprotocol/relay-kit-ui/styles.css";
|
|
7
7
|
import { useEffect, useMemo } from "react";
|
|
8
|
-
import { setClientType } from "../../../client-manager.js";
|
|
8
|
+
import { resetClients, setClientType } from "../../../client-manager.js";
|
|
9
|
+
import { setB3ApiUrl } from "../../../../app.shared.js";
|
|
10
|
+
import { resetBetterAuthClient } from "../../../better-auth-client.js";
|
|
9
11
|
import { StyleRoot } from "../StyleRoot.js";
|
|
10
12
|
import { setToastContext, ToastProvider, useToastContext } from "../Toast/index.js";
|
|
11
13
|
import AuthenticationProvider from "./AuthenticationProvider.js";
|
|
@@ -17,7 +19,19 @@ import { LocalSDKProvider } from "./LocalSDKProvider.js";
|
|
|
17
19
|
*/
|
|
18
20
|
export function B3Provider({ theme = "light", children, accountOverride, environment, automaticallySetFirstEoa, defaultEoaProvider, simDuneApiKey,
|
|
19
21
|
// deprecated since v0.0.87
|
|
20
|
-
toaster: _toaster, clientType = "rest", rpcUrls, partnerId, stripePublishableKey, onConnect, onLogout, connectors, overrideDefaultConnectors = false, createClientReferenceId, defaultPermissions, disableBSMNTAuthentication = false, queryClient, authStrategy = "thirdweb", }) {
|
|
22
|
+
toaster: _toaster, clientType = "rest", rpcUrls, partnerId, stripePublishableKey, onConnect, onLogout, connectors, overrideDefaultConnectors = false, createClientReferenceId, defaultPermissions, disableBSMNTAuthentication = false, queryClient, authStrategy = "thirdweb", apiUrl, }) {
|
|
23
|
+
// Override the B3 API URL when the `apiUrl` prop is provided.
|
|
24
|
+
// Must run before client initialization and auth providers mount.
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
setB3ApiUrl(apiUrl ?? null);
|
|
27
|
+
if (apiUrl) {
|
|
28
|
+
resetBetterAuthClient();
|
|
29
|
+
resetClients();
|
|
30
|
+
}
|
|
31
|
+
return () => {
|
|
32
|
+
setB3ApiUrl(null);
|
|
33
|
+
};
|
|
34
|
+
}, [apiUrl]);
|
|
21
35
|
// Initialize Google Analytics on mount
|
|
22
36
|
useEffect(() => {
|
|
23
37
|
loadGA4Script();
|
|
@@ -4,7 +4,7 @@ import { B3_AUTH_COOKIE_NAME } from "../../../../shared/constants/index.js";
|
|
|
4
4
|
import { debugB3React } from "../../../../shared/utils/debug.js";
|
|
5
5
|
import Cookies from "js-cookie";
|
|
6
6
|
import { useEffect, useRef } from "react";
|
|
7
|
-
import {
|
|
7
|
+
import { getBetterAuthClient } from "../../../better-auth-client.js";
|
|
8
8
|
import { useUserQuery } from "../../hooks/useUserQuery.js";
|
|
9
9
|
const debug = debugB3React("BetterAuthProvider");
|
|
10
10
|
/**
|
|
@@ -40,7 +40,7 @@ const BetterAuthProvider = ({ partnerId }) => {
|
|
|
40
40
|
app.logout = async () => {
|
|
41
41
|
debug("Patched logout: clearing Better Auth session");
|
|
42
42
|
try {
|
|
43
|
-
await
|
|
43
|
+
await getBetterAuthClient().signOut();
|
|
44
44
|
}
|
|
45
45
|
catch {
|
|
46
46
|
debug("Better Auth signOut failed (non-critical)");
|
|
@@ -74,9 +74,50 @@ const BetterAuthProvider = ({ partnerId }) => {
|
|
|
74
74
|
catch {
|
|
75
75
|
debug("No existing Feathers JWT");
|
|
76
76
|
}
|
|
77
|
-
// 2. Check for
|
|
77
|
+
// 2. Check for _ba_token in URL (OAuth callback with third-party cookie bypass).
|
|
78
|
+
// When the API and frontend are on different domains, browsers with third-party
|
|
79
|
+
// cookie blocking (Firefox TCP, Safari ITP, Chrome Privacy Sandbox) prevent the
|
|
80
|
+
// better-auth.session_token cookie from being sent on cross-origin requests.
|
|
81
|
+
// The B3 API injects the session token into the redirect URL as _ba_token,
|
|
82
|
+
// allowing direct exchange without relying on cookies.
|
|
83
|
+
const params = new URLSearchParams(window.location.search);
|
|
84
|
+
const baToken = params.get("_ba_token");
|
|
85
|
+
if (baToken) {
|
|
86
|
+
debug("Found _ba_token in URL, exchanging for Feathers JWT");
|
|
87
|
+
// Clean the token from the URL immediately (security)
|
|
88
|
+
params.delete("_ba_token");
|
|
89
|
+
const cleanSearch = params.toString();
|
|
90
|
+
const cleanUrl = window.location.pathname + (cleanSearch ? `?${cleanSearch}` : "") + window.location.hash;
|
|
91
|
+
window.history.replaceState({}, "", cleanUrl);
|
|
92
|
+
try {
|
|
93
|
+
const response = await app.authenticate({
|
|
94
|
+
strategy: "better-auth",
|
|
95
|
+
accessToken: baToken,
|
|
96
|
+
partnerId,
|
|
97
|
+
});
|
|
98
|
+
if (response.accessToken) {
|
|
99
|
+
Cookies.set(B3_AUTH_COOKIE_NAME, response.accessToken, {
|
|
100
|
+
secure: true,
|
|
101
|
+
sameSite: "Lax",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (response.user) {
|
|
105
|
+
setUser(response.user);
|
|
106
|
+
setIsAuthenticated(true);
|
|
107
|
+
setIsConnected(true);
|
|
108
|
+
}
|
|
109
|
+
debug("_ba_token exchanged for Feathers JWT", { userId: response.user?.userId });
|
|
110
|
+
setIsAuthenticating(false);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
debug("_ba_token exchange failed", err);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// 3. Check for a Better Auth session via cookie (works when API and frontend
|
|
118
|
+
// share a domain or when cookies aren't partitioned)
|
|
78
119
|
try {
|
|
79
|
-
const session = await
|
|
120
|
+
const session = await getBetterAuthClient().getSession();
|
|
80
121
|
if (session.data?.session?.token) {
|
|
81
122
|
debug("Better Auth session found, exchanging for Feathers JWT", {
|
|
82
123
|
betterAuthUserId: session.data.user?.id,
|
|
@@ -105,7 +146,7 @@ const BetterAuthProvider = ({ partnerId }) => {
|
|
|
105
146
|
catch {
|
|
106
147
|
debug("No Better Auth session to restore");
|
|
107
148
|
}
|
|
108
|
-
//
|
|
149
|
+
// 4. Nothing found — show login UI
|
|
109
150
|
setIsAuthenticating(false);
|
|
110
151
|
};
|
|
111
152
|
restoreSession();
|
|
@@ -2,7 +2,7 @@ import app from "../../../global-account/app.js";
|
|
|
2
2
|
import { useAuthStore, useB3Config } from "../../../global-account/react/index.js";
|
|
3
3
|
import { debugB3React } from "../../../shared/utils/debug.js";
|
|
4
4
|
import { useCallback } from "react";
|
|
5
|
-
import {
|
|
5
|
+
import { getBetterAuthClient } from "../../better-auth-client.js";
|
|
6
6
|
import { useUserQuery } from "./useUserQuery.js";
|
|
7
7
|
const debug = debugB3React("useBetterAuth");
|
|
8
8
|
/** Thrown when email verification is required before the user can sign in. */
|
|
@@ -49,7 +49,7 @@ export function useBetterAuth() {
|
|
|
49
49
|
setHasStartedConnecting(true);
|
|
50
50
|
setIsAuthenticating(true);
|
|
51
51
|
try {
|
|
52
|
-
const result = await
|
|
52
|
+
const result = await getBetterAuthClient().signIn.email({ email, password });
|
|
53
53
|
if (result.error) {
|
|
54
54
|
throw new Error(result.error.message || "Sign in failed");
|
|
55
55
|
}
|
|
@@ -70,7 +70,7 @@ export function useBetterAuth() {
|
|
|
70
70
|
setHasStartedConnecting(true);
|
|
71
71
|
setIsAuthenticating(true);
|
|
72
72
|
try {
|
|
73
|
-
const result = await
|
|
73
|
+
const result = await getBetterAuthClient().signUp.email({ email, password, name });
|
|
74
74
|
if (result.error) {
|
|
75
75
|
throw new Error(result.error.message || "Sign up failed");
|
|
76
76
|
}
|
|
@@ -79,7 +79,7 @@ export function useBetterAuth() {
|
|
|
79
79
|
// requireEmailVerification is enabled — send verification email with
|
|
80
80
|
// a callbackURL Better Auth redirects to after server-side verify.
|
|
81
81
|
// Pass verifyCallbackURL to land on a dedicated confirmation page.
|
|
82
|
-
await
|
|
82
|
+
await getBetterAuthClient().sendVerificationEmail({
|
|
83
83
|
email,
|
|
84
84
|
callbackURL: verifyCallbackURL || `${window.location.origin}?authStrategy=better-auth`,
|
|
85
85
|
});
|
|
@@ -98,7 +98,7 @@ export function useBetterAuth() {
|
|
|
98
98
|
setHasStartedConnecting(true);
|
|
99
99
|
setIsAuthenticating(true);
|
|
100
100
|
try {
|
|
101
|
-
const result = await
|
|
101
|
+
const result = await getBetterAuthClient().signIn.social({
|
|
102
102
|
provider,
|
|
103
103
|
callbackURL: window.location.href,
|
|
104
104
|
});
|
|
@@ -116,7 +116,7 @@ export function useBetterAuth() {
|
|
|
116
116
|
}, [setIsAuthenticating, setHasStartedConnecting]);
|
|
117
117
|
const requestPasswordReset = useCallback(async (email, redirectTo) => {
|
|
118
118
|
debug("Requesting password reset", { email });
|
|
119
|
-
const result = await
|
|
119
|
+
const result = await getBetterAuthClient().requestPasswordReset({
|
|
120
120
|
email,
|
|
121
121
|
redirectTo,
|
|
122
122
|
});
|
|
@@ -128,7 +128,7 @@ export function useBetterAuth() {
|
|
|
128
128
|
}, []);
|
|
129
129
|
const resetPassword = useCallback(async (newPassword, token) => {
|
|
130
130
|
debug("Resetting password");
|
|
131
|
-
const result = await
|
|
131
|
+
const result = await getBetterAuthClient().resetPassword({
|
|
132
132
|
newPassword,
|
|
133
133
|
token,
|
|
134
134
|
});
|
|
@@ -145,6 +145,6 @@ export function useBetterAuth() {
|
|
|
145
145
|
requestPasswordReset,
|
|
146
146
|
resetPassword,
|
|
147
147
|
exchangeForFeathersJWT,
|
|
148
|
-
betterAuthClient,
|
|
148
|
+
betterAuthClient: getBetterAuthClient(),
|
|
149
149
|
};
|
|
150
150
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { ClientApplication } from "@b3dotfun/b3-api";
|
|
2
2
|
import { AuthenticationClient } from "@feathersjs/authentication-client";
|
|
3
|
+
/** Default API URL derived from environment variables. */
|
|
3
4
|
export declare const B3_API_URL: string;
|
|
5
|
+
/**
|
|
6
|
+
* Override the B3 API URL at runtime. Called by `B3Provider` when an
|
|
7
|
+
* `apiUrl` prop is provided. Pass `null` to revert to the default.
|
|
8
|
+
*/
|
|
9
|
+
export declare function setB3ApiUrl(url: string | null): void;
|
|
10
|
+
/** Get the current B3 API URL (prefers runtime override, then env var). */
|
|
11
|
+
export declare function getB3ApiUrl(): string;
|
|
4
12
|
export declare const authenticate: (app: ClientApplication, accessToken: string, identityToken: string, params?: Record<string, any>) => Promise<import("@feathersjs/authentication").AuthenticationResult | null>;
|
|
5
13
|
export declare class MyAuthenticationClient extends AuthenticationClient {
|
|
6
14
|
getFromLocation(location: any): Promise<any>;
|
|
@@ -940,6 +940,14 @@ export declare function createB3BetterAuthClient(baseURL?: string): {
|
|
|
940
940
|
};
|
|
941
941
|
};
|
|
942
942
|
};
|
|
943
|
+
/** Get the shared BetterAuth client, creating it if needed. */
|
|
944
|
+
export declare function getBetterAuthClient(): B3BetterAuthClient;
|
|
945
|
+
/** Force-recreate the client on next access (e.g. after API URL change). */
|
|
946
|
+
export declare function resetBetterAuthClient(): void;
|
|
947
|
+
/**
|
|
948
|
+
* @deprecated Use `getBetterAuthClient()` instead. This static singleton
|
|
949
|
+
* does not reflect runtime API URL changes via `B3Provider apiUrl` prop.
|
|
950
|
+
*/
|
|
943
951
|
export declare const betterAuthClient: {
|
|
944
952
|
signIn: {
|
|
945
953
|
social: <FetchOptions extends import("@better-auth/core").ClientFetchOption<Partial<{
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ClientApplication } from "@b3dotfun/b3-api";
|
|
2
2
|
export type ClientType = "socket" | "rest";
|
|
3
|
+
/** Reset all cached clients (called when API URL changes). */
|
|
4
|
+
export declare function resetClients(): void;
|
|
3
5
|
/**
|
|
4
6
|
* Sets the active client type and creates the appropriate client
|
|
5
7
|
*
|
|
@@ -10,7 +10,7 @@ import { ClientType } from "../../../client-manager";
|
|
|
10
10
|
/**
|
|
11
11
|
* Main B3Provider component
|
|
12
12
|
*/
|
|
13
|
-
export declare function B3Provider({ theme, children, accountOverride, environment, automaticallySetFirstEoa, defaultEoaProvider, simDuneApiKey, toaster: _toaster, clientType, rpcUrls, partnerId, stripePublishableKey, onConnect, onLogout, connectors, overrideDefaultConnectors, createClientReferenceId, defaultPermissions, disableBSMNTAuthentication, queryClient, authStrategy, }: {
|
|
13
|
+
export declare function B3Provider({ theme, children, accountOverride, environment, automaticallySetFirstEoa, defaultEoaProvider, simDuneApiKey, toaster: _toaster, clientType, rpcUrls, partnerId, stripePublishableKey, onConnect, onLogout, connectors, overrideDefaultConnectors, createClientReferenceId, defaultPermissions, disableBSMNTAuthentication, queryClient, authStrategy, apiUrl, }: {
|
|
14
14
|
theme: "light" | "dark";
|
|
15
15
|
children: React.ReactNode;
|
|
16
16
|
accountOverride?: Account;
|
|
@@ -39,4 +39,13 @@ export declare function B3Provider({ theme, children, accountOverride, environme
|
|
|
39
39
|
queryClient?: QueryClient;
|
|
40
40
|
/** Auth strategy: "thirdweb" (default, ecosystem wallet) or "better-auth" (email/password via Better Auth) */
|
|
41
41
|
authStrategy?: AuthStrategy;
|
|
42
|
+
/**
|
|
43
|
+
* Override the B3 API URL. When provided, all SDK requests (auth, Feathers,
|
|
44
|
+
* BetterAuth) will use this URL instead of the `NEXT_PUBLIC_B3_API` env var.
|
|
45
|
+
*
|
|
46
|
+
* Useful for local development: pass `http://localhost:3031` to route
|
|
47
|
+
* OAuth callbacks and session checks through your local B3 API.
|
|
48
|
+
* If omitted, defaults to `NEXT_PUBLIC_B3_API` or `https://api.b3.fun`.
|
|
49
|
+
*/
|
|
50
|
+
apiUrl?: string;
|
|
42
51
|
}): import("react/jsx-runtime").JSX.Element;
|
package/package.json
CHANGED
package/src/app.shared.ts
CHANGED
|
@@ -4,9 +4,29 @@ import Cookies from "js-cookie";
|
|
|
4
4
|
import { B3_AUTH_COOKIE_NAME } from "./shared/constants";
|
|
5
5
|
import { getSessionDurationDays } from "./shared/utils/session-duration";
|
|
6
6
|
|
|
7
|
+
/** Default API URL derived from environment variables. */
|
|
7
8
|
export const B3_API_URL =
|
|
8
9
|
process.env.EXPO_PUBLIC_B3_API || process.env.NEXT_PUBLIC_B3_API || process.env.PUBLIC_B3_API || "https://api.b3.fun";
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Runtime-overridable API URL. Set by `B3Provider` when an `apiUrl` prop
|
|
13
|
+
* is provided. Falls back to the env-var-derived `B3_API_URL`.
|
|
14
|
+
*/
|
|
15
|
+
let _apiUrlOverride: string | null = null;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Override the B3 API URL at runtime. Called by `B3Provider` when an
|
|
19
|
+
* `apiUrl` prop is provided. Pass `null` to revert to the default.
|
|
20
|
+
*/
|
|
21
|
+
export function setB3ApiUrl(url: string | null): void {
|
|
22
|
+
_apiUrlOverride = url;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Get the current B3 API URL (prefers runtime override, then env var). */
|
|
26
|
+
export function getB3ApiUrl(): string {
|
|
27
|
+
return _apiUrlOverride || B3_API_URL;
|
|
28
|
+
}
|
|
29
|
+
|
|
10
30
|
export const authenticate = async (
|
|
11
31
|
app: ClientApplication,
|
|
12
32
|
accessToken: string,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { createAuthClient } from "better-auth/client";
|
|
2
|
-
import {
|
|
2
|
+
import { getB3ApiUrl } from "../app.shared";
|
|
3
3
|
|
|
4
4
|
export type B3BetterAuthClient = ReturnType<typeof createB3BetterAuthClient>;
|
|
5
5
|
|
|
6
|
-
export function createB3BetterAuthClient(baseURL
|
|
6
|
+
export function createB3BetterAuthClient(baseURL?: string) {
|
|
7
7
|
return createAuthClient({
|
|
8
|
-
baseURL,
|
|
8
|
+
baseURL: baseURL ?? getB3ApiUrl(),
|
|
9
9
|
basePath: "/auth",
|
|
10
10
|
fetchOptions: {
|
|
11
11
|
credentials: "include",
|
|
@@ -13,5 +13,29 @@ export function createB3BetterAuthClient(baseURL: string = B3_API_URL) {
|
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
//
|
|
16
|
+
// Lazily-initialized singleton. Recreated when the API URL changes via
|
|
17
|
+
// `resetBetterAuthClient()` (called by B3Provider when `apiUrl` changes).
|
|
18
|
+
let _client: B3BetterAuthClient | null = null;
|
|
19
|
+
let _clientUrl: string | null = null;
|
|
20
|
+
|
|
21
|
+
/** Get the shared BetterAuth client, creating it if needed. */
|
|
22
|
+
export function getBetterAuthClient(): B3BetterAuthClient {
|
|
23
|
+
const url = getB3ApiUrl();
|
|
24
|
+
if (!_client || _clientUrl !== url) {
|
|
25
|
+
_client = createB3BetterAuthClient(url);
|
|
26
|
+
_clientUrl = url;
|
|
27
|
+
}
|
|
28
|
+
return _client;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Force-recreate the client on next access (e.g. after API URL change). */
|
|
32
|
+
export function resetBetterAuthClient(): void {
|
|
33
|
+
_client = null;
|
|
34
|
+
_clientUrl = null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @deprecated Use `getBetterAuthClient()` instead. This static singleton
|
|
39
|
+
* does not reflect runtime API URL changes via `B3Provider apiUrl` prop.
|
|
40
|
+
*/
|
|
17
41
|
export const betterAuthClient = createB3BetterAuthClient();
|
|
@@ -2,7 +2,7 @@ import { ClientApplication, createClient } from "@b3dotfun/b3-api";
|
|
|
2
2
|
import rest from "@feathersjs/rest-client";
|
|
3
3
|
import socketio from "@feathersjs/socketio-client";
|
|
4
4
|
import io from "socket.io-client";
|
|
5
|
-
import { authenticate as authenticateB3,
|
|
5
|
+
import { authenticate as authenticateB3, getB3ApiUrl, clientOptions } from "../app.shared";
|
|
6
6
|
|
|
7
7
|
export type ClientType = "socket" | "rest";
|
|
8
8
|
|
|
@@ -13,17 +13,21 @@ let currentClient: ClientApplication | null = null;
|
|
|
13
13
|
// Socket client instance
|
|
14
14
|
let socketClient: ClientApplication | null = null;
|
|
15
15
|
let socketInstance: any = null;
|
|
16
|
+
let socketClientUrl: string | null = null;
|
|
16
17
|
|
|
17
18
|
// REST client instance
|
|
18
19
|
let restClient: ClientApplication | null = null;
|
|
20
|
+
let restClientUrl: string | null = null;
|
|
19
21
|
|
|
20
22
|
/**
|
|
21
23
|
* Creates a socket client
|
|
22
24
|
*/
|
|
23
25
|
function createSocketClient(): ClientApplication {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
const url = getB3ApiUrl();
|
|
27
|
+
if (!socketClient || socketClientUrl !== url) {
|
|
28
|
+
socketInstance = io(url, { transports: ["websocket"] });
|
|
26
29
|
socketClient = createClient(socketio(socketInstance), clientOptions);
|
|
30
|
+
socketClientUrl = url;
|
|
27
31
|
}
|
|
28
32
|
return socketClient;
|
|
29
33
|
}
|
|
@@ -40,13 +44,24 @@ function resolveFetch(): typeof fetch {
|
|
|
40
44
|
}
|
|
41
45
|
|
|
42
46
|
function createRestClient(): ClientApplication {
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
const url = getB3ApiUrl();
|
|
48
|
+
if (!restClient || restClientUrl !== url) {
|
|
49
|
+
const connection = rest(url).fetch(resolveFetch());
|
|
45
50
|
restClient = createClient(connection, clientOptions);
|
|
51
|
+
restClientUrl = url;
|
|
46
52
|
}
|
|
47
53
|
return restClient;
|
|
48
54
|
}
|
|
49
55
|
|
|
56
|
+
/** Reset all cached clients (called when API URL changes). */
|
|
57
|
+
export function resetClients(): void {
|
|
58
|
+
socketClient = null;
|
|
59
|
+
socketClientUrl = null;
|
|
60
|
+
restClient = null;
|
|
61
|
+
restClientUrl = null;
|
|
62
|
+
currentClient = null;
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
/**
|
|
51
66
|
* Sets the active client type and creates the appropriate client
|
|
52
67
|
*
|
|
@@ -11,7 +11,9 @@ import { QueryClient } from "@tanstack/react-query";
|
|
|
11
11
|
import { useEffect, useMemo } from "react";
|
|
12
12
|
import { Account, EIP1193, Wallet } from "thirdweb/wallets";
|
|
13
13
|
import { CreateConnectorFn } from "wagmi";
|
|
14
|
-
import { ClientType, setClientType } from "../../../client-manager";
|
|
14
|
+
import { ClientType, resetClients, setClientType } from "../../../client-manager";
|
|
15
|
+
import { setB3ApiUrl } from "../../../../app.shared";
|
|
16
|
+
import { resetBetterAuthClient } from "../../../better-auth-client";
|
|
15
17
|
import { StyleRoot } from "../StyleRoot";
|
|
16
18
|
import { setToastContext, ToastProvider, useToastContext } from "../Toast/index";
|
|
17
19
|
import AuthenticationProvider from "./AuthenticationProvider";
|
|
@@ -45,6 +47,7 @@ export function B3Provider({
|
|
|
45
47
|
disableBSMNTAuthentication = false,
|
|
46
48
|
queryClient,
|
|
47
49
|
authStrategy = "thirdweb",
|
|
50
|
+
apiUrl,
|
|
48
51
|
}: {
|
|
49
52
|
theme: "light" | "dark";
|
|
50
53
|
children: React.ReactNode;
|
|
@@ -74,7 +77,29 @@ export function B3Provider({
|
|
|
74
77
|
queryClient?: QueryClient;
|
|
75
78
|
/** Auth strategy: "thirdweb" (default, ecosystem wallet) or "better-auth" (email/password via Better Auth) */
|
|
76
79
|
authStrategy?: AuthStrategy;
|
|
80
|
+
/**
|
|
81
|
+
* Override the B3 API URL. When provided, all SDK requests (auth, Feathers,
|
|
82
|
+
* BetterAuth) will use this URL instead of the `NEXT_PUBLIC_B3_API` env var.
|
|
83
|
+
*
|
|
84
|
+
* Useful for local development: pass `http://localhost:3031` to route
|
|
85
|
+
* OAuth callbacks and session checks through your local B3 API.
|
|
86
|
+
* If omitted, defaults to `NEXT_PUBLIC_B3_API` or `https://api.b3.fun`.
|
|
87
|
+
*/
|
|
88
|
+
apiUrl?: string;
|
|
77
89
|
}) {
|
|
90
|
+
// Override the B3 API URL when the `apiUrl` prop is provided.
|
|
91
|
+
// Must run before client initialization and auth providers mount.
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
setB3ApiUrl(apiUrl ?? null);
|
|
94
|
+
if (apiUrl) {
|
|
95
|
+
resetBetterAuthClient();
|
|
96
|
+
resetClients();
|
|
97
|
+
}
|
|
98
|
+
return () => {
|
|
99
|
+
setB3ApiUrl(null);
|
|
100
|
+
};
|
|
101
|
+
}, [apiUrl]);
|
|
102
|
+
|
|
78
103
|
// Initialize Google Analytics on mount
|
|
79
104
|
useEffect(() => {
|
|
80
105
|
loadGA4Script();
|
|
@@ -4,7 +4,7 @@ import { B3_AUTH_COOKIE_NAME } from "@b3dotfun/sdk/shared/constants";
|
|
|
4
4
|
import { debugB3React } from "@b3dotfun/sdk/shared/utils/debug";
|
|
5
5
|
import Cookies from "js-cookie";
|
|
6
6
|
import { useEffect, useRef } from "react";
|
|
7
|
-
import {
|
|
7
|
+
import { getBetterAuthClient } from "../../../better-auth-client";
|
|
8
8
|
import { useUserQuery } from "../../hooks/useUserQuery";
|
|
9
9
|
|
|
10
10
|
const debug = debugB3React("BetterAuthProvider");
|
|
@@ -43,7 +43,7 @@ const BetterAuthProvider = ({ partnerId }: { partnerId: string }) => {
|
|
|
43
43
|
(app as any).logout = async () => {
|
|
44
44
|
debug("Patched logout: clearing Better Auth session");
|
|
45
45
|
try {
|
|
46
|
-
await
|
|
46
|
+
await getBetterAuthClient().signOut();
|
|
47
47
|
} catch {
|
|
48
48
|
debug("Better Auth signOut failed (non-critical)");
|
|
49
49
|
}
|
|
@@ -79,9 +79,55 @@ const BetterAuthProvider = ({ partnerId }: { partnerId: string }) => {
|
|
|
79
79
|
debug("No existing Feathers JWT");
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
// 2. Check for
|
|
82
|
+
// 2. Check for _ba_token in URL (OAuth callback with third-party cookie bypass).
|
|
83
|
+
// When the API and frontend are on different domains, browsers with third-party
|
|
84
|
+
// cookie blocking (Firefox TCP, Safari ITP, Chrome Privacy Sandbox) prevent the
|
|
85
|
+
// better-auth.session_token cookie from being sent on cross-origin requests.
|
|
86
|
+
// The B3 API injects the session token into the redirect URL as _ba_token,
|
|
87
|
+
// allowing direct exchange without relying on cookies.
|
|
88
|
+
const params = new URLSearchParams(window.location.search);
|
|
89
|
+
const baToken = params.get("_ba_token");
|
|
90
|
+
if (baToken) {
|
|
91
|
+
debug("Found _ba_token in URL, exchanging for Feathers JWT");
|
|
92
|
+
|
|
93
|
+
// Clean the token from the URL immediately (security)
|
|
94
|
+
params.delete("_ba_token");
|
|
95
|
+
const cleanSearch = params.toString();
|
|
96
|
+
const cleanUrl = window.location.pathname + (cleanSearch ? `?${cleanSearch}` : "") + window.location.hash;
|
|
97
|
+
window.history.replaceState({}, "", cleanUrl);
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const response = await app.authenticate({
|
|
101
|
+
strategy: "better-auth",
|
|
102
|
+
accessToken: baToken,
|
|
103
|
+
partnerId,
|
|
104
|
+
} as any);
|
|
105
|
+
|
|
106
|
+
if (response.accessToken) {
|
|
107
|
+
Cookies.set(B3_AUTH_COOKIE_NAME, response.accessToken, {
|
|
108
|
+
secure: true,
|
|
109
|
+
sameSite: "Lax",
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (response.user) {
|
|
114
|
+
setUser(response.user);
|
|
115
|
+
setIsAuthenticated(true);
|
|
116
|
+
setIsConnected(true);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
debug("_ba_token exchanged for Feathers JWT", { userId: response.user?.userId });
|
|
120
|
+
setIsAuthenticating(false);
|
|
121
|
+
return;
|
|
122
|
+
} catch (err) {
|
|
123
|
+
debug("_ba_token exchange failed", err);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 3. Check for a Better Auth session via cookie (works when API and frontend
|
|
128
|
+
// share a domain or when cookies aren't partitioned)
|
|
83
129
|
try {
|
|
84
|
-
const session = await
|
|
130
|
+
const session = await getBetterAuthClient().getSession();
|
|
85
131
|
if (session.data?.session?.token) {
|
|
86
132
|
debug("Better Auth session found, exchanging for Feathers JWT", {
|
|
87
133
|
betterAuthUserId: session.data.user?.id,
|
|
@@ -114,7 +160,7 @@ const BetterAuthProvider = ({ partnerId }: { partnerId: string }) => {
|
|
|
114
160
|
debug("No Better Auth session to restore");
|
|
115
161
|
}
|
|
116
162
|
|
|
117
|
-
//
|
|
163
|
+
// 4. Nothing found — show login UI
|
|
118
164
|
setIsAuthenticating(false);
|
|
119
165
|
};
|
|
120
166
|
|
|
@@ -2,7 +2,7 @@ import app from "@b3dotfun/sdk/global-account/app";
|
|
|
2
2
|
import { useAuthStore, useB3Config } from "@b3dotfun/sdk/global-account/react";
|
|
3
3
|
import { debugB3React } from "@b3dotfun/sdk/shared/utils/debug";
|
|
4
4
|
import { useCallback } from "react";
|
|
5
|
-
import {
|
|
5
|
+
import { getBetterAuthClient } from "../../better-auth-client";
|
|
6
6
|
import { useUserQuery } from "./useUserQuery";
|
|
7
7
|
|
|
8
8
|
const debug = debugB3React("useBetterAuth");
|
|
@@ -65,7 +65,7 @@ export function useBetterAuth() {
|
|
|
65
65
|
setIsAuthenticating(true);
|
|
66
66
|
|
|
67
67
|
try {
|
|
68
|
-
const result = await
|
|
68
|
+
const result = await getBetterAuthClient().signIn.email({ email, password });
|
|
69
69
|
|
|
70
70
|
if (result.error) {
|
|
71
71
|
throw new Error(result.error.message || "Sign in failed");
|
|
@@ -93,7 +93,7 @@ export function useBetterAuth() {
|
|
|
93
93
|
setIsAuthenticating(true);
|
|
94
94
|
|
|
95
95
|
try {
|
|
96
|
-
const result = await
|
|
96
|
+
const result = await getBetterAuthClient().signUp.email({ email, password, name });
|
|
97
97
|
|
|
98
98
|
if (result.error) {
|
|
99
99
|
throw new Error(result.error.message || "Sign up failed");
|
|
@@ -104,7 +104,7 @@ export function useBetterAuth() {
|
|
|
104
104
|
// requireEmailVerification is enabled — send verification email with
|
|
105
105
|
// a callbackURL Better Auth redirects to after server-side verify.
|
|
106
106
|
// Pass verifyCallbackURL to land on a dedicated confirmation page.
|
|
107
|
-
await
|
|
107
|
+
await getBetterAuthClient().sendVerificationEmail({
|
|
108
108
|
email,
|
|
109
109
|
callbackURL: verifyCallbackURL || `${window.location.origin}?authStrategy=better-auth`,
|
|
110
110
|
});
|
|
@@ -128,7 +128,7 @@ export function useBetterAuth() {
|
|
|
128
128
|
setIsAuthenticating(true);
|
|
129
129
|
|
|
130
130
|
try {
|
|
131
|
-
const result = await
|
|
131
|
+
const result = await getBetterAuthClient().signIn.social({
|
|
132
132
|
provider,
|
|
133
133
|
callbackURL: window.location.href,
|
|
134
134
|
});
|
|
@@ -151,7 +151,7 @@ export function useBetterAuth() {
|
|
|
151
151
|
const requestPasswordReset = useCallback(async (email: string, redirectTo?: string) => {
|
|
152
152
|
debug("Requesting password reset", { email });
|
|
153
153
|
|
|
154
|
-
const result = await
|
|
154
|
+
const result = await getBetterAuthClient().requestPasswordReset({
|
|
155
155
|
email,
|
|
156
156
|
redirectTo,
|
|
157
157
|
});
|
|
@@ -167,7 +167,7 @@ export function useBetterAuth() {
|
|
|
167
167
|
const resetPassword = useCallback(async (newPassword: string, token: string) => {
|
|
168
168
|
debug("Resetting password");
|
|
169
169
|
|
|
170
|
-
const result = await
|
|
170
|
+
const result = await getBetterAuthClient().resetPassword({
|
|
171
171
|
newPassword,
|
|
172
172
|
token,
|
|
173
173
|
});
|
|
@@ -187,6 +187,6 @@ export function useBetterAuth() {
|
|
|
187
187
|
requestPasswordReset,
|
|
188
188
|
resetPassword,
|
|
189
189
|
exchangeForFeathersJWT,
|
|
190
|
-
betterAuthClient,
|
|
190
|
+
betterAuthClient: getBetterAuthClient(),
|
|
191
191
|
};
|
|
192
192
|
}
|