@volr/react 0.1.1 → 0.1.3

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 CHANGED
@@ -88,6 +88,38 @@ function AdvancedComponent() {
88
88
  - **SSR Safe**: Works with Next.js and other SSR frameworks
89
89
  - **Call Builders**: `buildCall()` and `buildCalls()` utilities for easy transaction building
90
90
 
91
+ ## OAuth Handling
92
+
93
+ The SDK provides a dedicated hook `useVolrAuthCallback` to simplify handling OAuth redirects. This is useful for custom login flows where you need to process the callback from social providers (Google, Twitter, Apple).
94
+
95
+ ```tsx
96
+ import { useVolrAuthCallback } from '@volr/react';
97
+
98
+ function AuthCallback() {
99
+ const { isLoading, error, isNewUser, user } = useVolrAuthCallback({
100
+ onSuccess: (user) => {
101
+ console.log('Logged in:', user);
102
+ // Navigate to dashboard or home
103
+ },
104
+ onError: (err) => {
105
+ console.error('Login failed:', err);
106
+ // Show error message or redirect to login
107
+ },
108
+ });
109
+
110
+ if (isLoading) return <div>Processing authentication...</div>;
111
+ if (error) return <div>Error: {error}</div>;
112
+
113
+ return <div>Redirecting...</div>;
114
+ }
115
+ ```
116
+
117
+ This hook automatically:
118
+ 1. Parses URL parameters (`success`, `userId`, `isNewUser`, `error`)
119
+ 2. Refreshes the session to establish the authentication cookie
120
+ 3. Fetches the full user profile
121
+ 4. Updates the global `VolrProvider` context state
122
+
91
123
  ## Failure Semantics
92
124
 
93
125
  - Relay enforces strict failure-by-default:
package/dist/index.cjs CHANGED
@@ -74,7 +74,7 @@ var safeStorage = {
74
74
  }
75
75
  };
76
76
 
77
- // node_modules/@volr/shared/src/constants/storage.ts
77
+ // ../shared/src/constants/storage.ts
78
78
  var STORAGE_KEYS = {
79
79
  accessToken: "volr:accessToken",
80
80
  user: "volr:user",
@@ -350,6 +350,17 @@ var SessionSync = class {
350
350
  }
351
351
  };
352
352
 
353
+ // src/config/backend.ts
354
+ var DEFAULT_API_BASE_URL = "https://api.volr.io";
355
+ function resolveApiBaseUrl(config) {
356
+ const anyConfig = config;
357
+ const override = anyConfig.apiBaseUrl;
358
+ if (override && typeof override === "string") {
359
+ return override.replace(/\/+$/, "");
360
+ }
361
+ return DEFAULT_API_BASE_URL;
362
+ }
363
+
353
364
  // src/config/webauthn.ts
354
365
  var AUTHENTICATOR_SELECTION = {
355
366
  authenticatorAttachment: "platform",
@@ -649,18 +660,18 @@ function VolrProvider({ config, children }) {
649
660
  providerCountRef.current--;
650
661
  };
651
662
  }, []);
663
+ const apiBaseUrl = resolveApiBaseUrl(config);
652
664
  const client = react.useMemo(() => {
653
665
  if (!config.projectApiKey) {
654
666
  throw new Error(
655
667
  "VolrProvider requires config.projectApiKey. Please set VITE_PROJECT_API_KEY environment variable or provide projectApiKey in config."
656
668
  );
657
669
  }
658
- const baseUrl = config.__devApiBaseUrl || config.apiBaseUrl;
659
670
  return new APIClient({
660
- baseUrl,
671
+ baseUrl: apiBaseUrl,
661
672
  apiKey: config.projectApiKey
662
673
  });
663
- }, [config.apiBaseUrl, config.projectApiKey]);
674
+ }, [apiBaseUrl, config.projectApiKey]);
664
675
  const [user, setUser] = react.useState(() => {
665
676
  if (typeof window === "undefined") return null;
666
677
  const userStr = safeStorage.getItem(STORAGE_KEYS.user);
@@ -1865,9 +1876,10 @@ function useVolrLogin() {
1865
1876
  credentialId: u.credentialId ?? void 0
1866
1877
  };
1867
1878
  }, []);
1879
+ const apiBaseUrl = resolveApiBaseUrl(config);
1868
1880
  const api = react.useMemo(
1869
- () => createAxiosInstance(config.apiBaseUrl, config.projectApiKey),
1870
- [config.apiBaseUrl, config.projectApiKey]
1881
+ () => createAxiosInstance(apiBaseUrl, config.projectApiKey),
1882
+ [apiBaseUrl, config.projectApiKey]
1871
1883
  );
1872
1884
  const requestEmailCode = react.useCallback(
1873
1885
  async (email) => {
@@ -1935,11 +1947,11 @@ function useVolrLogin() {
1935
1947
  const handleSocialLogin = react.useCallback(
1936
1948
  async (provider) => {
1937
1949
  if (typeof window !== "undefined") {
1938
- const baseUrl = config.apiBaseUrl.replace(/\/+$/, "");
1950
+ const baseUrl = apiBaseUrl.replace(/\/+$/, "");
1939
1951
  window.location.href = `${baseUrl}/auth/${provider}`;
1940
1952
  }
1941
1953
  },
1942
- [config.apiBaseUrl]
1954
+ [apiBaseUrl]
1943
1955
  );
1944
1956
  const requestSiweNonce = react.useCallback(async () => {
1945
1957
  const response = await api.get("/auth/siwe/nonce");
@@ -2011,6 +2023,91 @@ function useVolrLogin() {
2011
2023
  handlePasskeyComplete
2012
2024
  };
2013
2025
  }
2026
+ function useVolrAuthCallback(options = {}) {
2027
+ const { config, setUser } = useVolr();
2028
+ const { refreshAccessToken, setAccessToken } = useInternalAuth();
2029
+ const [isLoading, setIsLoading] = react.useState(true);
2030
+ const [error, setError] = react.useState(null);
2031
+ const [isNewUser, setIsNewUser] = react.useState(false);
2032
+ const [user, setLocalUser] = react.useState(null);
2033
+ const toVolrUser = react.useCallback((u) => {
2034
+ return {
2035
+ id: u.id,
2036
+ email: u.email,
2037
+ accountId: u.accountId ?? void 0,
2038
+ evmAddress: u.evmAddress,
2039
+ keyStorageType: u.keyStorageType ?? void 0,
2040
+ signerType: u.signerType ?? void 0,
2041
+ walletConnector: u.walletConnector ?? void 0,
2042
+ lastWalletChainId: u.lastWalletChainId ?? void 0,
2043
+ blobUrl: u.blobUrl ?? void 0,
2044
+ prfInput: u.prfInput ?? void 0,
2045
+ credentialId: u.credentialId ?? void 0
2046
+ };
2047
+ }, []);
2048
+ const apiBaseUrl = resolveApiBaseUrl(config);
2049
+ react.useEffect(() => {
2050
+ const handleCallback = async () => {
2051
+ if (typeof window === "undefined") return;
2052
+ const searchParams = new URLSearchParams(window.location.search);
2053
+ const success = searchParams.get("success");
2054
+ const errorParam = searchParams.get("error");
2055
+ const userId = searchParams.get("userId");
2056
+ const isNew = searchParams.get("isNewUser") === "true";
2057
+ if (errorParam) {
2058
+ const errorMsg = `Authentication failed: ${errorParam}`;
2059
+ setError(errorMsg);
2060
+ setIsLoading(false);
2061
+ options.onError?.(errorMsg);
2062
+ return;
2063
+ }
2064
+ if (success !== "true" || !userId) {
2065
+ const errorMsg = "Invalid callback parameters";
2066
+ setError(errorMsg);
2067
+ setIsLoading(false);
2068
+ options.onError?.(errorMsg);
2069
+ return;
2070
+ }
2071
+ try {
2072
+ setIsNewUser(isNew);
2073
+ await refreshAccessToken();
2074
+ const api = createAxiosInstance(
2075
+ apiBaseUrl,
2076
+ config.projectApiKey
2077
+ );
2078
+ const userRes = await api.get(`/auth/onboarding/${userId}`);
2079
+ if (!userRes.data?.ok) {
2080
+ throw new Error("Failed to fetch user details");
2081
+ }
2082
+ const refreshRes = await api.post("/auth/refresh");
2083
+ if (!refreshRes.data?.ok) {
2084
+ throw new Error("Failed to refresh session");
2085
+ }
2086
+ const userData = refreshRes.data.data.user;
2087
+ const accessToken = refreshRes.data.data.accessToken;
2088
+ setAccessToken(accessToken);
2089
+ const volrUser = toVolrUser(userData);
2090
+ setUser(volrUser);
2091
+ setLocalUser(volrUser);
2092
+ setIsLoading(false);
2093
+ options.onSuccess?.(volrUser);
2094
+ } catch (err) {
2095
+ console.error("Callback error:", err);
2096
+ const errorMsg = err.message || "Failed to complete authentication";
2097
+ setError(errorMsg);
2098
+ setIsLoading(false);
2099
+ options.onError?.(errorMsg);
2100
+ }
2101
+ };
2102
+ handleCallback();
2103
+ }, [apiBaseUrl, config.projectApiKey, refreshAccessToken, setAccessToken, setUser, toVolrUser]);
2104
+ return {
2105
+ isLoading,
2106
+ error,
2107
+ isNewUser,
2108
+ user
2109
+ };
2110
+ }
2014
2111
  async function jsonRpc(rpcUrl, method, params) {
2015
2112
  const payload = {
2016
2113
  jsonrpc: "2.0",
@@ -2304,6 +2401,7 @@ function usePasskeyEnrollment() {
2304
2401
  const [isEnrolling, setIsEnrolling] = react.useState(false);
2305
2402
  const [error, setError] = react.useState(null);
2306
2403
  const isEnrollingRef = react.useRef(false);
2404
+ const apiBaseUrl = resolveApiBaseUrl(config);
2307
2405
  const enroll = react.useCallback(async () => {
2308
2406
  if (isEnrollingRef.current || isEnrolling) return;
2309
2407
  isEnrollingRef.current = true;
@@ -2319,13 +2417,15 @@ function usePasskeyEnrollment() {
2319
2417
  }
2320
2418
  const accessToken = client.getAccessToken();
2321
2419
  if (!accessToken) {
2322
- throw new Error("Access token is required for passkey enrollment. Please login first.");
2420
+ throw new Error(
2421
+ "Access token is required for passkey enrollment. Please login first."
2422
+ );
2323
2423
  }
2324
2424
  const projectId = user.id;
2325
2425
  setStep("encrypting");
2326
2426
  const result = await enrollPasskey({
2327
2427
  client,
2328
- baseUrl: config.apiBaseUrl,
2428
+ baseUrl: apiBaseUrl,
2329
2429
  apiKey: config.projectApiKey,
2330
2430
  userId: user.id,
2331
2431
  userEmail: user.email,
@@ -2370,15 +2470,7 @@ function usePasskeyEnrollment() {
2370
2470
  setIsEnrolling(false);
2371
2471
  isEnrollingRef.current = false;
2372
2472
  }
2373
- }, [
2374
- client,
2375
- config.apiBaseUrl,
2376
- config.projectApiKey,
2377
- user,
2378
- setProvider,
2379
- setUser,
2380
- isEnrolling
2381
- ]);
2473
+ }, [apiBaseUrl, client, config.projectApiKey, user, setProvider, setUser, isEnrolling]);
2382
2474
  return {
2383
2475
  enroll,
2384
2476
  step,
@@ -2605,6 +2697,7 @@ exports.usePasskeyEnrollment = usePasskeyEnrollment;
2605
2697
  exports.usePrecheck = usePrecheck;
2606
2698
  exports.useRelay = useRelay;
2607
2699
  exports.useVolr = useVolr;
2700
+ exports.useVolrAuthCallback = useVolrAuthCallback;
2608
2701
  exports.useVolrLogin = useVolrLogin;
2609
2702
  exports.useVolrWallet = useVolrWallet;
2610
2703
  //# sourceMappingURL=index.cjs.map