@rhinestone/1auth 0.7.4 → 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-IHBVEU33.mjs → chunk-6AHEN3FA.mjs} +1 -1
- package/dist/chunk-6AHEN3FA.mjs.map +1 -0
- package/dist/{chunk-UXBBB7AV.mjs → chunk-E4YZS7FZ.mjs} +68 -32
- package/dist/chunk-E4YZS7FZ.mjs.map +1 -0
- package/dist/{client-D4HVy1KF.d.mts → client-C8QSA1th.d.mts} +22 -17
- package/dist/{client-CuASptX3.d.ts → client-Dn6mL7BZ.d.ts} +22 -17
- package/dist/headless.d.mts +2 -2
- package/dist/headless.d.ts +2 -2
- package/dist/headless.js +4 -6
- package/dist/headless.js.map +1 -1
- package/dist/headless.mjs +4 -6
- package/dist/headless.mjs.map +1 -1
- package/dist/index.d.mts +163 -22
- package/dist/index.d.ts +163 -22
- package/dist/index.js +210 -116
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +143 -86
- package/dist/index.mjs.map +1 -1
- package/dist/{provider-6JoziYDq.d.ts → provider-BsVmPgkQ.d.ts} +22 -3
- package/dist/{provider-CAQT7Gqx.d.mts → provider-CJv38fIK.d.mts} +22 -3
- package/dist/react.d.mts +9 -4
- package/dist/react.d.ts +9 -4
- package/dist/react.js +4 -7
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +4 -7
- package/dist/react.mjs.map +1 -1
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +1 -1
- package/dist/{types-C0jKNT_t.d.mts → types-Dzm5lZK-.d.mts} +33 -51
- package/dist/{types-C0jKNT_t.d.ts → types-Dzm5lZK-.d.ts} +33 -51
- package/dist/{verify-CnOwPq78.d.ts → verify-0VXQpQBJ.d.mts} +4 -6
- package/dist/{verify-aWdi5O2z.d.mts → verify-9UgxLSdo.d.ts} +4 -6
- package/dist/wagmi.d.mts +3 -3
- package/dist/wagmi.d.ts +3 -3
- package/dist/wagmi.js +67 -31
- package/dist/wagmi.js.map +1 -1
- package/dist/wagmi.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-IHBVEU33.mjs.map +0 -1
- package/dist/chunk-UXBBB7AV.mjs.map +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Address } from 'viem';
|
|
2
|
+
import { O as OneAuthClient } from './client-Dn6mL7BZ.js';
|
|
3
|
+
import { C as CloseOnStatus, h as SignerType } from './types-Dzm5lZK-.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* EIP-1193 JSON-RPC provider factory for the 1auth passkey authentication system.
|
|
@@ -30,6 +31,24 @@ type OneAuthProvider = {
|
|
|
30
31
|
on: (event: string, listener: Listener) => void;
|
|
31
32
|
removeListener: (event: string, listener: Listener) => void;
|
|
32
33
|
disconnect: () => Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Bind the active session in-memory and persist it to `localStorage`.
|
|
36
|
+
*
|
|
37
|
+
* Used by {@link createOneAuthConnection} to hand a freshly connected session
|
|
38
|
+
* to the provider the instant `connect()` resolves — before the app has
|
|
39
|
+
* written `1auth-user` itself. This closes a race where a provider built right
|
|
40
|
+
* after connect would read stale/empty storage and default an EOA session to
|
|
41
|
+
* passkey intents. Passing `null` clears the session (same effect as
|
|
42
|
+
* `disconnect()` minus the events).
|
|
43
|
+
*
|
|
44
|
+
* Existing `createOneAuthProvider` consumers never call this, so their
|
|
45
|
+
* behavior is unchanged: `getStoredUser` falls back to the localStorage read.
|
|
46
|
+
*/
|
|
47
|
+
setSession: (session: {
|
|
48
|
+
address: Address;
|
|
49
|
+
signerType?: SignerType;
|
|
50
|
+
connectorId?: string;
|
|
51
|
+
} | null) => void;
|
|
33
52
|
};
|
|
34
53
|
type OneAuthProviderOptions = {
|
|
35
54
|
client: OneAuthClient;
|
|
@@ -46,7 +65,7 @@ type OneAuthProviderOptions = {
|
|
|
46
65
|
*
|
|
47
66
|
* The returned provider object can be used anywhere a standard Ethereum provider is
|
|
48
67
|
* accepted (e.g. passed to `createWalletClient` in viem, used as `window.ethereum`, or
|
|
49
|
-
* supplied to ethers.js `BrowserProvider`). Connection state (
|
|
68
|
+
* supplied to ethers.js `BrowserProvider`). Connection state (the account address) is
|
|
50
69
|
* persisted in `localStorage` under the configured `storageKey`.
|
|
51
70
|
*
|
|
52
71
|
* @param options - Provider configuration
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Address } from 'viem';
|
|
2
|
+
import { O as OneAuthClient } from './client-C8QSA1th.mjs';
|
|
3
|
+
import { C as CloseOnStatus, h as SignerType } from './types-Dzm5lZK-.mjs';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* EIP-1193 JSON-RPC provider factory for the 1auth passkey authentication system.
|
|
@@ -30,6 +31,24 @@ type OneAuthProvider = {
|
|
|
30
31
|
on: (event: string, listener: Listener) => void;
|
|
31
32
|
removeListener: (event: string, listener: Listener) => void;
|
|
32
33
|
disconnect: () => Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Bind the active session in-memory and persist it to `localStorage`.
|
|
36
|
+
*
|
|
37
|
+
* Used by {@link createOneAuthConnection} to hand a freshly connected session
|
|
38
|
+
* to the provider the instant `connect()` resolves — before the app has
|
|
39
|
+
* written `1auth-user` itself. This closes a race where a provider built right
|
|
40
|
+
* after connect would read stale/empty storage and default an EOA session to
|
|
41
|
+
* passkey intents. Passing `null` clears the session (same effect as
|
|
42
|
+
* `disconnect()` minus the events).
|
|
43
|
+
*
|
|
44
|
+
* Existing `createOneAuthProvider` consumers never call this, so their
|
|
45
|
+
* behavior is unchanged: `getStoredUser` falls back to the localStorage read.
|
|
46
|
+
*/
|
|
47
|
+
setSession: (session: {
|
|
48
|
+
address: Address;
|
|
49
|
+
signerType?: SignerType;
|
|
50
|
+
connectorId?: string;
|
|
51
|
+
} | null) => void;
|
|
33
52
|
};
|
|
34
53
|
type OneAuthProviderOptions = {
|
|
35
54
|
client: OneAuthClient;
|
|
@@ -46,7 +65,7 @@ type OneAuthProviderOptions = {
|
|
|
46
65
|
*
|
|
47
66
|
* The returned provider object can be used anywhere a standard Ethereum provider is
|
|
48
67
|
* accepted (e.g. passed to `createWalletClient` in viem, used as `window.ethereum`, or
|
|
49
|
-
* supplied to ethers.js `BrowserProvider`). Connection state (
|
|
68
|
+
* supplied to ethers.js `BrowserProvider`). Connection state (the account address) is
|
|
50
69
|
* persisted in `localStorage` under the configured `storageKey`.
|
|
51
70
|
*
|
|
52
71
|
* @param options - Provider configuration
|
package/dist/react.d.mts
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { O as OneAuthClient } from './client-
|
|
4
|
-
import {
|
|
3
|
+
import { O as OneAuthClient } from './client-C8QSA1th.mjs';
|
|
4
|
+
import { _ as SendIntentOptions, j as SendIntentResult, C as CloseOnStatus, aw as SponsorshipUrlConfig, av as SponsorshipCallbackConfig } from './types-Dzm5lZK-.mjs';
|
|
5
5
|
import 'viem';
|
|
6
6
|
import '@rhinestone/sdk';
|
|
7
7
|
|
|
8
8
|
interface PayButtonProps {
|
|
9
9
|
/** The OneAuthClient instance */
|
|
10
10
|
client: OneAuthClient;
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Intent parameters (calls, targetChain, etc.). `accountAddress` is omitted
|
|
13
|
+
* because PayButton resolves it internally (cached session or auth modal) and
|
|
14
|
+
* overwrites it on the sendIntent call — callers pass only calls + target
|
|
15
|
+
* chain, keeping the drop-in payment API intact.
|
|
16
|
+
*/
|
|
17
|
+
intent: Omit<SendIntentOptions, "closeOn" | "accountAddress">;
|
|
13
18
|
/** Called when payment succeeds */
|
|
14
19
|
onSuccess?: (result: SendIntentResult) => void;
|
|
15
20
|
/** Called when payment fails */
|
package/dist/react.d.ts
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { O as OneAuthClient } from './client-
|
|
4
|
-
import {
|
|
3
|
+
import { O as OneAuthClient } from './client-Dn6mL7BZ.js';
|
|
4
|
+
import { _ as SendIntentOptions, j as SendIntentResult, C as CloseOnStatus, aw as SponsorshipUrlConfig, av as SponsorshipCallbackConfig } from './types-Dzm5lZK-.js';
|
|
5
5
|
import 'viem';
|
|
6
6
|
import '@rhinestone/sdk';
|
|
7
7
|
|
|
8
8
|
interface PayButtonProps {
|
|
9
9
|
/** The OneAuthClient instance */
|
|
10
10
|
client: OneAuthClient;
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Intent parameters (calls, targetChain, etc.). `accountAddress` is omitted
|
|
13
|
+
* because PayButton resolves it internally (cached session or auth modal) and
|
|
14
|
+
* overwrites it on the sendIntent call — callers pass only calls + target
|
|
15
|
+
* chain, keeping the drop-in payment API intact.
|
|
16
|
+
*/
|
|
17
|
+
intent: Omit<SendIntentOptions, "closeOn" | "accountAddress">;
|
|
13
18
|
/** Called when payment succeeds */
|
|
14
19
|
onSuccess?: (result: SendIntentResult) => void;
|
|
15
20
|
/** Called when payment fails */
|
package/dist/react.js
CHANGED
|
@@ -160,27 +160,23 @@ function PayButton({
|
|
|
160
160
|
}
|
|
161
161
|
};
|
|
162
162
|
const executePayment = async (forceReauth = false) => {
|
|
163
|
-
let username = null;
|
|
164
163
|
let accountAddress = null;
|
|
165
164
|
if (!forceReauth) {
|
|
166
165
|
const savedUser = localStorage.getItem("1auth-user");
|
|
167
166
|
if (savedUser) {
|
|
168
167
|
try {
|
|
169
168
|
const parsed = JSON.parse(savedUser);
|
|
170
|
-
username = parsed.username || null;
|
|
171
169
|
accountAddress = parsed.address || null;
|
|
172
170
|
} catch {
|
|
173
171
|
localStorage.removeItem("1auth-user");
|
|
174
172
|
}
|
|
175
173
|
}
|
|
176
174
|
}
|
|
177
|
-
if (!
|
|
175
|
+
if (!accountAddress) {
|
|
178
176
|
const authResult = await client.authWithModal();
|
|
179
177
|
if (authResult.success && authResult.user) {
|
|
180
|
-
username = authResult.user.username || null;
|
|
181
178
|
accountAddress = authResult.user.address || null;
|
|
182
179
|
const stored = {};
|
|
183
|
-
if (username) stored.username = username;
|
|
184
180
|
if (accountAddress) stored.address = accountAddress;
|
|
185
181
|
if (authResult.signerType) stored.signerType = authResult.signerType;
|
|
186
182
|
localStorage.setItem("1auth-user", JSON.stringify(stored));
|
|
@@ -192,8 +188,9 @@ function PayButton({
|
|
|
192
188
|
try {
|
|
193
189
|
result = await client.sendIntent({
|
|
194
190
|
...intent,
|
|
195
|
-
|
|
196
|
-
|
|
191
|
+
// Guaranteed non-null: the `if (!accountAddress) { ...return }` guard
|
|
192
|
+
// above either resolved an address or bailed out before reaching here.
|
|
193
|
+
accountAddress,
|
|
197
194
|
closeOn
|
|
198
195
|
});
|
|
199
196
|
} catch (err) {
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.tsx","../src/sponsorship-fetch.ts"],"sourcesContent":["/**\n * @file React integration for the 1auth SDK.\n *\n * Exports the {@link PayButton} component — a ready-to-use payment button that\n * handles authentication, session recovery, and intent submission without\n * requiring the consuming app to manage auth state directly.\n *\n * @module @rhinestone/1auth/react\n */\n\nimport * as React from \"react\";\nimport { OneAuthClient } from \"./client\";\nimport { tokenFetchError } from \"./sponsorship-fetch\";\nimport type {\n SendIntentOptions,\n SendIntentResult,\n CloseOnStatus,\n SponsorshipCallbackConfig,\n SponsorshipUrlConfig,\n} from \"./types\";\n\n// Fingerprint icon SVG\nconst FingerprintIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M12 10a2 2 0 0 0-2 2c0 1.02-.1 2.51-.26 4\" />\n <path d=\"M14 13.12c0 2.38 0 6.38-1 8.88\" />\n <path d=\"M17.29 21.02c.12-.6.43-2.3.5-3.02\" />\n <path d=\"M2 12a10 10 0 0 1 18-6\" />\n <path d=\"M2 16h.01\" />\n <path d=\"M21.8 16c.2-2 .131-5.354 0-6\" />\n <path d=\"M5 19.5C5.5 18 6 15 6 12a6 6 0 0 1 .34-2\" />\n <path d=\"M8.65 22c.21-.66.45-1.32.57-2\" />\n <path d=\"M9 6.8a6 6 0 0 1 9 5.2v2\" />\n </svg>\n);\n\n// Default styles\nconst defaultStyles: React.CSSProperties = {\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: \"8px\",\n padding: \"16px 32px\",\n fontSize: \"16px\",\n fontWeight: 500,\n color: \"#ffffff\",\n backgroundColor: \"#18181b\",\n border: \"none\",\n borderRadius: \"9999px\",\n cursor: \"pointer\",\n transition: \"background-color 0.2s\",\n width: \"100%\",\n};\n\nconst defaultDisabledStyles: React.CSSProperties = {\n opacity: 0.5,\n cursor: \"not-allowed\",\n};\n\nconst defaultHoverStyles: React.CSSProperties = {\n backgroundColor: \"#27272a\",\n};\n\nconst defaultSuccessStyles: React.CSSProperties = {\n backgroundColor: \"#16a34a\",\n cursor: \"default\",\n};\n\n// Checkmark icon SVG\nconst CheckIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M20 6L9 17l-5-5\" />\n </svg>\n);\n\nexport interface PayButtonProps {\n /** The OneAuthClient instance */\n client: OneAuthClient;\n /** Intent parameters (calls, targetChain, etc.) - username will be filled automatically */\n intent: Omit<SendIntentOptions, \"username\" | \"closeOn\">;\n /** Called when payment succeeds */\n onSuccess?: (result: SendIntentResult) => void;\n /** Called when payment fails */\n onError?: (error: Error) => void;\n /** When to close the dialog and return success. Defaults to \"preconfirmed\" */\n closeOn?: CloseOnStatus;\n /** Button text - defaults to \"Pay with 1auth\" */\n children?: React.ReactNode;\n /** Custom class name */\n className?: string;\n /** Custom inline styles (merged with defaults) */\n style?: React.CSSProperties;\n /** Disabled state */\n disabled?: boolean;\n /** Hide the fingerprint icon */\n hideIcon?: boolean;\n}\n\n/**\n * A pre-built payment button that integrates 1auth passkey authentication\n * and intent submission into a single interactive element.\n *\n * The button manages its own loading and success states. On first click it\n * recovers the last authenticated user from `localStorage`; if none is found\n * it opens the auth modal before submitting the intent. On subsequent clicks\n * the stored session is reused so the user only needs to re-authenticate when\n * their session has been invalidated server-side.\n *\n * @param props - See {@link PayButtonProps} for the full set of options.\n *\n * @example\n * ```tsx\n * import { PayButton } from \"@rhinestone/1auth/react\";\n * import { client } from \"./auth\"; // your OneAuthClient instance\n *\n * <PayButton\n * client={client}\n * intent={{\n * targetChain: 8453, // Base\n * calls: [{ to: \"0xRecipient\", data: \"0x\", value: \"1000000\" }],\n * }}\n * onSuccess={(result) => console.log(\"tx:\", result.transactionHash)}\n * onError={(err) => console.error(err)}\n * >\n * Pay $5 USDC\n * </PayButton>\n * ```\n */\nexport function PayButton({\n client,\n intent,\n onSuccess,\n onError,\n closeOn = \"preconfirmed\",\n children = \"Pay with 1auth\",\n className,\n style,\n disabled,\n hideIcon,\n}: PayButtonProps) {\n const [isProcessing, setIsProcessing] = React.useState(false);\n const [isSuccess, setIsSuccess] = React.useState(false);\n const [isHovered, setIsHovered] = React.useState(false);\n\n const handleClick = async () => {\n if (disabled || isProcessing || isSuccess) return;\n\n setIsProcessing(true);\n\n try {\n await executePayment();\n } catch (err) {\n if (err instanceof Error && !err.message.includes(\"rejected\")) {\n onError?.(err);\n }\n } finally {\n setIsProcessing(false);\n }\n };\n\n /**\n * Core payment execution with built-in session recovery and re-auth retry.\n *\n * Flow:\n * 1. On first call (`forceReauth = false`), attempt to load a cached user\n * from `localStorage` under the key `\"1auth-user\"`. If the JSON is\n * malformed the cache entry is removed so the next call starts clean.\n * 2. If no cached user exists (or `forceReauth` is `true`), open the auth\n * modal. On success, persist `{ username, address }` to `localStorage`\n * so subsequent clicks skip authentication.\n * 3. Submit the intent. If the server responds with \"User not found\" — which\n * happens when a cached username/address no longer exists on the server —\n * clear `localStorage` and call `executePayment(true)` to force a fresh\n * authentication before retrying once.\n *\n * @param forceReauth - When `true`, skip the localStorage lookup and go\n * straight to the auth modal. Used internally on \"User not found\" errors\n * to recover from a stale cache without requiring a second user gesture.\n */\n const executePayment = async (forceReauth = false) => {\n // Try to get existing user from localStorage\n let username: string | null = null;\n let accountAddress: string | null = null;\n if (!forceReauth) {\n const savedUser = localStorage.getItem(\"1auth-user\");\n if (savedUser) {\n try {\n const parsed = JSON.parse(savedUser);\n username = parsed.username || null;\n accountAddress = parsed.address || null;\n } catch {\n localStorage.removeItem(\"1auth-user\");\n }\n }\n }\n\n // If no user (or forced reauth), authenticate first\n if (!username && !accountAddress) {\n const authResult = await client.authWithModal();\n if (authResult.success && authResult.user) {\n username = authResult.user.username || null;\n accountAddress = authResult.user.address || null;\n const stored: Record<string, string> = {};\n if (username) stored.username = username;\n if (accountAddress) stored.address = accountAddress;\n if (authResult.signerType) stored.signerType = authResult.signerType;\n localStorage.setItem(\"1auth-user\", JSON.stringify(stored));\n } else {\n // Auth cancelled or failed\n return;\n }\n }\n\n // Send the intent\n let result: SendIntentResult;\n try {\n result = await client.sendIntent({\n ...intent,\n username: username || undefined,\n accountAddress: accountAddress || undefined,\n closeOn,\n });\n } catch (err) {\n // If user not found, clear localStorage and force re-authentication\n if (err instanceof Error && err.message.includes(\"User not found\")) {\n localStorage.removeItem(\"1auth-user\");\n return executePayment(true);\n }\n throw err;\n }\n\n if (result.success) {\n setIsSuccess(true);\n onSuccess?.(result);\n } else {\n // If user not found error in result, clear localStorage and retry\n if (result.error?.message?.includes(\"User not found\")) {\n localStorage.removeItem(\"1auth-user\");\n return executePayment(true);\n }\n onError?.(new Error(result.error?.message || \"Payment failed\"));\n }\n };\n\n const combinedStyles: React.CSSProperties = {\n ...defaultStyles,\n ...(isSuccess ? defaultSuccessStyles : {}),\n ...(isHovered && !disabled && !isProcessing && !isSuccess ? defaultHoverStyles : {}),\n ...(disabled || isProcessing ? defaultDisabledStyles : {}),\n ...style,\n };\n\n return (\n <button\n type=\"button\"\n className={className}\n style={combinedStyles}\n onClick={handleClick}\n disabled={disabled || isProcessing || isSuccess}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n {isSuccess ? (\n <>\n <CheckIcon className=\"pay-button-icon\" />\n Paid\n </>\n ) : isProcessing ? (\n \"Processing...\"\n ) : (\n <>\n {!hideIcon && <FingerprintIcon className=\"pay-button-icon\" />}\n {children}\n </>\n )}\n </button>\n );\n}\n\n/**\n * Margin (ms) before a JWT's `exp` at which the cached access token is\n * treated as stale. Covers clock skew between the minting app server and\n * the orchestrator's verifier plus in-flight time of the prepare call —\n * a token that verifies fine locally can still be rejected downstream if\n * it expires mid-request.\n */\nconst ACCESS_TOKEN_EXPIRY_MARGIN_MS = 60_000;\n\n/**\n * Check whether a cached JWT access token is still safely usable.\n *\n * Decodes the payload WITHOUT verifying the signature — the client only\n * needs the `exp` claim to know when to re-mint; verification happens\n * server-side. Returns `false` (unusable) when the token is expired or\n * within {@link ACCESS_TOKEN_EXPIRY_MARGIN_MS} of expiry. Tokens that\n * can't be decoded or carry no numeric `exp` are treated as unusable so\n * a malformed cache entry never gets served indefinitely.\n */\nexport function isAccessTokenFresh(token: string): boolean {\n try {\n const payload = token.split(\".\")[1];\n if (!payload) return false;\n // base64url → base64 before atob; JWTs use the url-safe alphabet.\n const json = atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n const claims = JSON.parse(json) as { exp?: unknown };\n if (typeof claims.exp !== \"number\") return false;\n return Date.now() < claims.exp * 1000 - ACCESS_TOKEN_EXPIRY_MARGIN_MS;\n } catch {\n return false;\n }\n}\n\n/**\n * Build a {@link SponsorshipCallbackConfig} from token endpoint URLs with an\n * expiry-aware access-token cache held in a closure.\n *\n * Exported as the non-React core of {@link useSponsorship} so the caching\n * behaviour is unit-testable (and usable outside React). The access token is\n * cached and re-used only while its `exp` claim is comfortably in the\n * future — a long-lived tab must re-mint rather than serve a token the\n * orchestrator will reject with a 401 (`\"exp\" claim timestamp check\n * failed`), which previously surfaced to users as \"Failed to get quote from\n * orchestrator\". The extension token is always fetched fresh per intent\n * because it binds to a specific `intentOp`.\n *\n * Both fetches use `credentials: \"include\"` so the app's own session cookies\n * authenticate the user — no separate auth channel needed.\n */\nexport function createCachedSponsorship(\n config: SponsorshipUrlConfig,\n): SponsorshipCallbackConfig {\n const { accessTokenUrl, extensionTokenUrl } = config;\n let cachedToken: string | null = null;\n\n const fetchAccessToken = async (): Promise<string> => {\n const res = await fetch(accessTokenUrl, {\n method: \"GET\",\n credentials: \"include\",\n });\n if (!res.ok) {\n // Folds the server's JSON error reason into the message so\n // misconfigurations aren't reduced to an opaque HTTP status.\n throw await tokenFetchError(\"sponsorship access token\", res);\n }\n const body = (await res.json()) as { token?: string };\n if (!body.token) throw new Error(\"Missing `token` in access-token response\");\n cachedToken = body.token;\n return body.token;\n };\n\n return {\n accessToken: async () =>\n cachedToken && isAccessTokenFresh(cachedToken)\n ? cachedToken\n : await fetchAccessToken(),\n getExtensionToken: async (intentOp: string) => {\n const res = await fetch(extensionTokenUrl, {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ intentOp }),\n });\n if (res.status === 401) {\n // Access token likely expired — drop cached copy so the next\n // accessToken() call re-mints, then surface the error for the\n // integrator to retry.\n cachedToken = null;\n }\n if (!res.ok) {\n throw await tokenFetchError(\"sponsorship extension token\", res);\n }\n const body = (await res.json()) as { token?: string };\n if (!body.token) throw new Error(\"Missing `token` in extension-token response\");\n return body.token;\n },\n };\n}\n\n/**\n * React hook wrapper around {@link createCachedSponsorship}. The returned\n * config is stable across renders (memoized by URL), so passing it to\n * `new OneAuthClient({ sponsorship })` or `client.setSponsorship(...)` does\n * not thrash the client. Re-creating on URL change naturally resets the\n * cached token so we never serve a token minted against the previous\n * endpoint.\n */\nexport function useSponsorship(\n config: SponsorshipUrlConfig,\n): SponsorshipCallbackConfig {\n const { accessTokenUrl, extensionTokenUrl } = config;\n\n return React.useMemo<SponsorshipCallbackConfig>(\n () => createCachedSponsorship({ accessTokenUrl, extensionTokenUrl }),\n [accessTokenUrl, extensionTokenUrl],\n );\n}\n\n// Re-export types for convenience\nexport type { SendIntentOptions, SendIntentResult, CloseOnStatus } from \"./types\";\n","/**\n * Shared helpers for fetching sponsorship tokens (access + extension) from an\n * integrator's backend endpoints.\n *\n * The integrator's token routes return their failure reason in the JSON body\n * (`{ error: \"createSponsorshipSigner: missing env var(s): ...\" }`). Surfacing\n * only the HTTP status — \"Failed to fetch access token (500 ...)\" — hides that\n * reason and turns every misconfiguration into the same opaque dialog error.\n * These helpers read the body and fold the server's message into the thrown\n * Error so the actual cause (missing env var, bad JWK, expired session) is\n * visible at the call site and in the dialog.\n */\n\n/**\n * Pull a human-readable reason out of a failed token response. Tries the JSON\n * `error`/`message` fields first, then falls back to a short text snippet, then\n * to the HTTP status text. Never throws — diagnostics must not mask the original\n * failure with a parse error.\n */\nasync function extractServerReason(response: Response): Promise<string> {\n try {\n // Clone so a JSON parse failure can still fall back to reading text.\n const text = await response.clone().text();\n if (!text) return response.statusText;\n try {\n const data = JSON.parse(text) as { error?: unknown; message?: unknown };\n const reason = data.error ?? data.message;\n if (typeof reason === \"string\" && reason.trim()) return reason;\n } catch {\n // Body wasn't JSON — fall through to the raw text snippet.\n }\n // Cap the snippet so an HTML error page doesn't flood the message.\n return text.slice(0, 200);\n } catch {\n return response.statusText;\n }\n}\n\n/**\n * Build an Error for a failed token fetch that includes the server's reason.\n * `label` names the token kind, e.g. \"access token\" or \"extension token\".\n */\nexport async function tokenFetchError(\n label: string,\n response: Response,\n): Promise<Error> {\n const reason = await extractServerReason(response);\n const suffix = reason ? `: ${reason}` : \"\";\n return new Error(\n `Failed to fetch ${label} (${response.status} ${response.statusText})${suffix}`,\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx","../src/sponsorship-fetch.ts"],"sourcesContent":["/**\n * @file React integration for the 1auth SDK.\n *\n * Exports the {@link PayButton} component — a ready-to-use payment button that\n * handles authentication, session recovery, and intent submission without\n * requiring the consuming app to manage auth state directly.\n *\n * @module @rhinestone/1auth/react\n */\n\nimport * as React from \"react\";\nimport { OneAuthClient } from \"./client\";\nimport { tokenFetchError } from \"./sponsorship-fetch\";\nimport type {\n SendIntentOptions,\n SendIntentResult,\n CloseOnStatus,\n SponsorshipCallbackConfig,\n SponsorshipUrlConfig,\n} from \"./types\";\n\n// Fingerprint icon SVG\nconst FingerprintIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M12 10a2 2 0 0 0-2 2c0 1.02-.1 2.51-.26 4\" />\n <path d=\"M14 13.12c0 2.38 0 6.38-1 8.88\" />\n <path d=\"M17.29 21.02c.12-.6.43-2.3.5-3.02\" />\n <path d=\"M2 12a10 10 0 0 1 18-6\" />\n <path d=\"M2 16h.01\" />\n <path d=\"M21.8 16c.2-2 .131-5.354 0-6\" />\n <path d=\"M5 19.5C5.5 18 6 15 6 12a6 6 0 0 1 .34-2\" />\n <path d=\"M8.65 22c.21-.66.45-1.32.57-2\" />\n <path d=\"M9 6.8a6 6 0 0 1 9 5.2v2\" />\n </svg>\n);\n\n// Default styles\nconst defaultStyles: React.CSSProperties = {\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: \"8px\",\n padding: \"16px 32px\",\n fontSize: \"16px\",\n fontWeight: 500,\n color: \"#ffffff\",\n backgroundColor: \"#18181b\",\n border: \"none\",\n borderRadius: \"9999px\",\n cursor: \"pointer\",\n transition: \"background-color 0.2s\",\n width: \"100%\",\n};\n\nconst defaultDisabledStyles: React.CSSProperties = {\n opacity: 0.5,\n cursor: \"not-allowed\",\n};\n\nconst defaultHoverStyles: React.CSSProperties = {\n backgroundColor: \"#27272a\",\n};\n\nconst defaultSuccessStyles: React.CSSProperties = {\n backgroundColor: \"#16a34a\",\n cursor: \"default\",\n};\n\n// Checkmark icon SVG\nconst CheckIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M20 6L9 17l-5-5\" />\n </svg>\n);\n\nexport interface PayButtonProps {\n /** The OneAuthClient instance */\n client: OneAuthClient;\n /**\n * Intent parameters (calls, targetChain, etc.). `accountAddress` is omitted\n * because PayButton resolves it internally (cached session or auth modal) and\n * overwrites it on the sendIntent call — callers pass only calls + target\n * chain, keeping the drop-in payment API intact.\n */\n intent: Omit<SendIntentOptions, \"closeOn\" | \"accountAddress\">;\n /** Called when payment succeeds */\n onSuccess?: (result: SendIntentResult) => void;\n /** Called when payment fails */\n onError?: (error: Error) => void;\n /** When to close the dialog and return success. Defaults to \"preconfirmed\" */\n closeOn?: CloseOnStatus;\n /** Button text - defaults to \"Pay with 1auth\" */\n children?: React.ReactNode;\n /** Custom class name */\n className?: string;\n /** Custom inline styles (merged with defaults) */\n style?: React.CSSProperties;\n /** Disabled state */\n disabled?: boolean;\n /** Hide the fingerprint icon */\n hideIcon?: boolean;\n}\n\n/**\n * A pre-built payment button that integrates 1auth passkey authentication\n * and intent submission into a single interactive element.\n *\n * The button manages its own loading and success states. On first click it\n * recovers the last authenticated user from `localStorage`; if none is found\n * it opens the auth modal before submitting the intent. On subsequent clicks\n * the stored session is reused so the user only needs to re-authenticate when\n * their session has been invalidated server-side.\n *\n * @param props - See {@link PayButtonProps} for the full set of options.\n *\n * @example\n * ```tsx\n * import { PayButton } from \"@rhinestone/1auth/react\";\n * import { client } from \"./auth\"; // your OneAuthClient instance\n *\n * <PayButton\n * client={client}\n * intent={{\n * targetChain: 8453, // Base\n * calls: [{ to: \"0xRecipient\", data: \"0x\", value: \"1000000\" }],\n * }}\n * onSuccess={(result) => console.log(\"tx:\", result.transactionHash)}\n * onError={(err) => console.error(err)}\n * >\n * Pay $5 USDC\n * </PayButton>\n * ```\n */\nexport function PayButton({\n client,\n intent,\n onSuccess,\n onError,\n closeOn = \"preconfirmed\",\n children = \"Pay with 1auth\",\n className,\n style,\n disabled,\n hideIcon,\n}: PayButtonProps) {\n const [isProcessing, setIsProcessing] = React.useState(false);\n const [isSuccess, setIsSuccess] = React.useState(false);\n const [isHovered, setIsHovered] = React.useState(false);\n\n const handleClick = async () => {\n if (disabled || isProcessing || isSuccess) return;\n\n setIsProcessing(true);\n\n try {\n await executePayment();\n } catch (err) {\n if (err instanceof Error && !err.message.includes(\"rejected\")) {\n onError?.(err);\n }\n } finally {\n setIsProcessing(false);\n }\n };\n\n /**\n * Core payment execution with built-in session recovery and re-auth retry.\n *\n * Flow:\n * 1. On first call (`forceReauth = false`), attempt to load a cached user\n * from `localStorage` under the key `\"1auth-user\"`. If the JSON is\n * malformed the cache entry is removed so the next call starts clean.\n * 2. If no cached user exists (or `forceReauth` is `true`), open the auth\n * modal. On success, persist `{ address }` to `localStorage`\n * so subsequent clicks skip authentication.\n * 3. Submit the intent. If the server responds with \"User not found\" — which\n * happens when a cached account address no longer exists on the server —\n * clear `localStorage` and call `executePayment(true)` to force a fresh\n * authentication before retrying once.\n *\n * @param forceReauth - When `true`, skip the localStorage lookup and go\n * straight to the auth modal. Used internally on \"User not found\" errors\n * to recover from a stale cache without requiring a second user gesture.\n */\n const executePayment = async (forceReauth = false) => {\n // Try to get existing account address from localStorage\n let accountAddress: string | null = null;\n if (!forceReauth) {\n const savedUser = localStorage.getItem(\"1auth-user\");\n if (savedUser) {\n try {\n const parsed = JSON.parse(savedUser);\n accountAddress = parsed.address || null;\n } catch {\n localStorage.removeItem(\"1auth-user\");\n }\n }\n }\n\n // If no account address (or forced reauth), authenticate first\n if (!accountAddress) {\n const authResult = await client.authWithModal();\n if (authResult.success && authResult.user) {\n accountAddress = authResult.user.address || null;\n const stored: Record<string, string> = {};\n if (accountAddress) stored.address = accountAddress;\n if (authResult.signerType) stored.signerType = authResult.signerType;\n localStorage.setItem(\"1auth-user\", JSON.stringify(stored));\n } else {\n // Auth cancelled or failed\n return;\n }\n }\n\n // Send the intent\n let result: SendIntentResult;\n try {\n result = await client.sendIntent({\n ...intent,\n // Guaranteed non-null: the `if (!accountAddress) { ...return }` guard\n // above either resolved an address or bailed out before reaching here.\n accountAddress,\n closeOn,\n });\n } catch (err) {\n // If user not found, clear localStorage and force re-authentication\n if (err instanceof Error && err.message.includes(\"User not found\")) {\n localStorage.removeItem(\"1auth-user\");\n return executePayment(true);\n }\n throw err;\n }\n\n if (result.success) {\n setIsSuccess(true);\n onSuccess?.(result);\n } else {\n // If user not found error in result, clear localStorage and retry\n if (result.error?.message?.includes(\"User not found\")) {\n localStorage.removeItem(\"1auth-user\");\n return executePayment(true);\n }\n onError?.(new Error(result.error?.message || \"Payment failed\"));\n }\n };\n\n const combinedStyles: React.CSSProperties = {\n ...defaultStyles,\n ...(isSuccess ? defaultSuccessStyles : {}),\n ...(isHovered && !disabled && !isProcessing && !isSuccess ? defaultHoverStyles : {}),\n ...(disabled || isProcessing ? defaultDisabledStyles : {}),\n ...style,\n };\n\n return (\n <button\n type=\"button\"\n className={className}\n style={combinedStyles}\n onClick={handleClick}\n disabled={disabled || isProcessing || isSuccess}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n {isSuccess ? (\n <>\n <CheckIcon className=\"pay-button-icon\" />\n Paid\n </>\n ) : isProcessing ? (\n \"Processing...\"\n ) : (\n <>\n {!hideIcon && <FingerprintIcon className=\"pay-button-icon\" />}\n {children}\n </>\n )}\n </button>\n );\n}\n\n/**\n * Margin (ms) before a JWT's `exp` at which the cached access token is\n * treated as stale. Covers clock skew between the minting app server and\n * the orchestrator's verifier plus in-flight time of the prepare call —\n * a token that verifies fine locally can still be rejected downstream if\n * it expires mid-request.\n */\nconst ACCESS_TOKEN_EXPIRY_MARGIN_MS = 60_000;\n\n/**\n * Check whether a cached JWT access token is still safely usable.\n *\n * Decodes the payload WITHOUT verifying the signature — the client only\n * needs the `exp` claim to know when to re-mint; verification happens\n * server-side. Returns `false` (unusable) when the token is expired or\n * within {@link ACCESS_TOKEN_EXPIRY_MARGIN_MS} of expiry. Tokens that\n * can't be decoded or carry no numeric `exp` are treated as unusable so\n * a malformed cache entry never gets served indefinitely.\n */\nexport function isAccessTokenFresh(token: string): boolean {\n try {\n const payload = token.split(\".\")[1];\n if (!payload) return false;\n // base64url → base64 before atob; JWTs use the url-safe alphabet.\n const json = atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n const claims = JSON.parse(json) as { exp?: unknown };\n if (typeof claims.exp !== \"number\") return false;\n return Date.now() < claims.exp * 1000 - ACCESS_TOKEN_EXPIRY_MARGIN_MS;\n } catch {\n return false;\n }\n}\n\n/**\n * Build a {@link SponsorshipCallbackConfig} from token endpoint URLs with an\n * expiry-aware access-token cache held in a closure.\n *\n * Exported as the non-React core of {@link useSponsorship} so the caching\n * behaviour is unit-testable (and usable outside React). The access token is\n * cached and re-used only while its `exp` claim is comfortably in the\n * future — a long-lived tab must re-mint rather than serve a token the\n * orchestrator will reject with a 401 (`\"exp\" claim timestamp check\n * failed`), which previously surfaced to users as \"Failed to get quote from\n * orchestrator\". The extension token is always fetched fresh per intent\n * because it binds to a specific `intentOp`.\n *\n * Both fetches use `credentials: \"include\"` so the app's own session cookies\n * authenticate the user — no separate auth channel needed.\n */\nexport function createCachedSponsorship(\n config: SponsorshipUrlConfig,\n): SponsorshipCallbackConfig {\n const { accessTokenUrl, extensionTokenUrl } = config;\n let cachedToken: string | null = null;\n\n const fetchAccessToken = async (): Promise<string> => {\n const res = await fetch(accessTokenUrl, {\n method: \"GET\",\n credentials: \"include\",\n });\n if (!res.ok) {\n // Folds the server's JSON error reason into the message so\n // misconfigurations aren't reduced to an opaque HTTP status.\n throw await tokenFetchError(\"sponsorship access token\", res);\n }\n const body = (await res.json()) as { token?: string };\n if (!body.token) throw new Error(\"Missing `token` in access-token response\");\n cachedToken = body.token;\n return body.token;\n };\n\n return {\n accessToken: async () =>\n cachedToken && isAccessTokenFresh(cachedToken)\n ? cachedToken\n : await fetchAccessToken(),\n getExtensionToken: async (intentOp: string) => {\n const res = await fetch(extensionTokenUrl, {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ intentOp }),\n });\n if (res.status === 401) {\n // Access token likely expired — drop cached copy so the next\n // accessToken() call re-mints, then surface the error for the\n // integrator to retry.\n cachedToken = null;\n }\n if (!res.ok) {\n throw await tokenFetchError(\"sponsorship extension token\", res);\n }\n const body = (await res.json()) as { token?: string };\n if (!body.token) throw new Error(\"Missing `token` in extension-token response\");\n return body.token;\n },\n };\n}\n\n/**\n * React hook wrapper around {@link createCachedSponsorship}. The returned\n * config is stable across renders (memoized by URL), so passing it to\n * `new OneAuthClient({ sponsorship })` or `client.setSponsorship(...)` does\n * not thrash the client. Re-creating on URL change naturally resets the\n * cached token so we never serve a token minted against the previous\n * endpoint.\n */\nexport function useSponsorship(\n config: SponsorshipUrlConfig,\n): SponsorshipCallbackConfig {\n const { accessTokenUrl, extensionTokenUrl } = config;\n\n return React.useMemo<SponsorshipCallbackConfig>(\n () => createCachedSponsorship({ accessTokenUrl, extensionTokenUrl }),\n [accessTokenUrl, extensionTokenUrl],\n );\n}\n\n// Re-export types for convenience\nexport type { SendIntentOptions, SendIntentResult, CloseOnStatus } from \"./types\";\n","/**\n * Shared helpers for fetching sponsorship tokens (access + extension) from an\n * integrator's backend endpoints.\n *\n * The integrator's token routes return their failure reason in the JSON body\n * (`{ error: \"createSponsorshipSigner: missing env var(s): ...\" }`). Surfacing\n * only the HTTP status — \"Failed to fetch access token (500 ...)\" — hides that\n * reason and turns every misconfiguration into the same opaque dialog error.\n * These helpers read the body and fold the server's message into the thrown\n * Error so the actual cause (missing env var, bad JWK, expired session) is\n * visible at the call site and in the dialog.\n */\n\n/**\n * Pull a human-readable reason out of a failed token response. Tries the JSON\n * `error`/`message` fields first, then falls back to a short text snippet, then\n * to the HTTP status text. Never throws — diagnostics must not mask the original\n * failure with a parse error.\n */\nasync function extractServerReason(response: Response): Promise<string> {\n try {\n // Clone so a JSON parse failure can still fall back to reading text.\n const text = await response.clone().text();\n if (!text) return response.statusText;\n try {\n const data = JSON.parse(text) as { error?: unknown; message?: unknown };\n const reason = data.error ?? data.message;\n if (typeof reason === \"string\" && reason.trim()) return reason;\n } catch {\n // Body wasn't JSON — fall through to the raw text snippet.\n }\n // Cap the snippet so an HTML error page doesn't flood the message.\n return text.slice(0, 200);\n } catch {\n return response.statusText;\n }\n}\n\n/**\n * Build an Error for a failed token fetch that includes the server's reason.\n * `label` names the token kind, e.g. \"access token\" or \"extension token\".\n */\nexport async function tokenFetchError(\n label: string,\n response: Response,\n): Promise<Error> {\n const reason = await extractServerReason(response);\n const suffix = reason ? `: ${reason}` : \"\";\n return new Error(\n `Failed to fetch ${label} (${response.status} ${response.statusText})${suffix}`,\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/react.mjs
CHANGED
|
@@ -101,27 +101,23 @@ function PayButton({
|
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
103
|
const executePayment = async (forceReauth = false) => {
|
|
104
|
-
let username = null;
|
|
105
104
|
let accountAddress = null;
|
|
106
105
|
if (!forceReauth) {
|
|
107
106
|
const savedUser = localStorage.getItem("1auth-user");
|
|
108
107
|
if (savedUser) {
|
|
109
108
|
try {
|
|
110
109
|
const parsed = JSON.parse(savedUser);
|
|
111
|
-
username = parsed.username || null;
|
|
112
110
|
accountAddress = parsed.address || null;
|
|
113
111
|
} catch {
|
|
114
112
|
localStorage.removeItem("1auth-user");
|
|
115
113
|
}
|
|
116
114
|
}
|
|
117
115
|
}
|
|
118
|
-
if (!
|
|
116
|
+
if (!accountAddress) {
|
|
119
117
|
const authResult = await client.authWithModal();
|
|
120
118
|
if (authResult.success && authResult.user) {
|
|
121
|
-
username = authResult.user.username || null;
|
|
122
119
|
accountAddress = authResult.user.address || null;
|
|
123
120
|
const stored = {};
|
|
124
|
-
if (username) stored.username = username;
|
|
125
121
|
if (accountAddress) stored.address = accountAddress;
|
|
126
122
|
if (authResult.signerType) stored.signerType = authResult.signerType;
|
|
127
123
|
localStorage.setItem("1auth-user", JSON.stringify(stored));
|
|
@@ -133,8 +129,9 @@ function PayButton({
|
|
|
133
129
|
try {
|
|
134
130
|
result = await client.sendIntent({
|
|
135
131
|
...intent,
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
// Guaranteed non-null: the `if (!accountAddress) { ...return }` guard
|
|
133
|
+
// above either resolved an address or bailed out before reaching here.
|
|
134
|
+
accountAddress,
|
|
138
135
|
closeOn
|
|
139
136
|
});
|
|
140
137
|
} catch (err) {
|
package/dist/react.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react.tsx"],"sourcesContent":["/**\n * @file React integration for the 1auth SDK.\n *\n * Exports the {@link PayButton} component — a ready-to-use payment button that\n * handles authentication, session recovery, and intent submission without\n * requiring the consuming app to manage auth state directly.\n *\n * @module @rhinestone/1auth/react\n */\n\nimport * as React from \"react\";\nimport { OneAuthClient } from \"./client\";\nimport { tokenFetchError } from \"./sponsorship-fetch\";\nimport type {\n SendIntentOptions,\n SendIntentResult,\n CloseOnStatus,\n SponsorshipCallbackConfig,\n SponsorshipUrlConfig,\n} from \"./types\";\n\n// Fingerprint icon SVG\nconst FingerprintIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M12 10a2 2 0 0 0-2 2c0 1.02-.1 2.51-.26 4\" />\n <path d=\"M14 13.12c0 2.38 0 6.38-1 8.88\" />\n <path d=\"M17.29 21.02c.12-.6.43-2.3.5-3.02\" />\n <path d=\"M2 12a10 10 0 0 1 18-6\" />\n <path d=\"M2 16h.01\" />\n <path d=\"M21.8 16c.2-2 .131-5.354 0-6\" />\n <path d=\"M5 19.5C5.5 18 6 15 6 12a6 6 0 0 1 .34-2\" />\n <path d=\"M8.65 22c.21-.66.45-1.32.57-2\" />\n <path d=\"M9 6.8a6 6 0 0 1 9 5.2v2\" />\n </svg>\n);\n\n// Default styles\nconst defaultStyles: React.CSSProperties = {\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: \"8px\",\n padding: \"16px 32px\",\n fontSize: \"16px\",\n fontWeight: 500,\n color: \"#ffffff\",\n backgroundColor: \"#18181b\",\n border: \"none\",\n borderRadius: \"9999px\",\n cursor: \"pointer\",\n transition: \"background-color 0.2s\",\n width: \"100%\",\n};\n\nconst defaultDisabledStyles: React.CSSProperties = {\n opacity: 0.5,\n cursor: \"not-allowed\",\n};\n\nconst defaultHoverStyles: React.CSSProperties = {\n backgroundColor: \"#27272a\",\n};\n\nconst defaultSuccessStyles: React.CSSProperties = {\n backgroundColor: \"#16a34a\",\n cursor: \"default\",\n};\n\n// Checkmark icon SVG\nconst CheckIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M20 6L9 17l-5-5\" />\n </svg>\n);\n\nexport interface PayButtonProps {\n /** The OneAuthClient instance */\n client: OneAuthClient;\n /** Intent parameters (calls, targetChain, etc.) - username will be filled automatically */\n intent: Omit<SendIntentOptions, \"username\" | \"closeOn\">;\n /** Called when payment succeeds */\n onSuccess?: (result: SendIntentResult) => void;\n /** Called when payment fails */\n onError?: (error: Error) => void;\n /** When to close the dialog and return success. Defaults to \"preconfirmed\" */\n closeOn?: CloseOnStatus;\n /** Button text - defaults to \"Pay with 1auth\" */\n children?: React.ReactNode;\n /** Custom class name */\n className?: string;\n /** Custom inline styles (merged with defaults) */\n style?: React.CSSProperties;\n /** Disabled state */\n disabled?: boolean;\n /** Hide the fingerprint icon */\n hideIcon?: boolean;\n}\n\n/**\n * A pre-built payment button that integrates 1auth passkey authentication\n * and intent submission into a single interactive element.\n *\n * The button manages its own loading and success states. On first click it\n * recovers the last authenticated user from `localStorage`; if none is found\n * it opens the auth modal before submitting the intent. On subsequent clicks\n * the stored session is reused so the user only needs to re-authenticate when\n * their session has been invalidated server-side.\n *\n * @param props - See {@link PayButtonProps} for the full set of options.\n *\n * @example\n * ```tsx\n * import { PayButton } from \"@rhinestone/1auth/react\";\n * import { client } from \"./auth\"; // your OneAuthClient instance\n *\n * <PayButton\n * client={client}\n * intent={{\n * targetChain: 8453, // Base\n * calls: [{ to: \"0xRecipient\", data: \"0x\", value: \"1000000\" }],\n * }}\n * onSuccess={(result) => console.log(\"tx:\", result.transactionHash)}\n * onError={(err) => console.error(err)}\n * >\n * Pay $5 USDC\n * </PayButton>\n * ```\n */\nexport function PayButton({\n client,\n intent,\n onSuccess,\n onError,\n closeOn = \"preconfirmed\",\n children = \"Pay with 1auth\",\n className,\n style,\n disabled,\n hideIcon,\n}: PayButtonProps) {\n const [isProcessing, setIsProcessing] = React.useState(false);\n const [isSuccess, setIsSuccess] = React.useState(false);\n const [isHovered, setIsHovered] = React.useState(false);\n\n const handleClick = async () => {\n if (disabled || isProcessing || isSuccess) return;\n\n setIsProcessing(true);\n\n try {\n await executePayment();\n } catch (err) {\n if (err instanceof Error && !err.message.includes(\"rejected\")) {\n onError?.(err);\n }\n } finally {\n setIsProcessing(false);\n }\n };\n\n /**\n * Core payment execution with built-in session recovery and re-auth retry.\n *\n * Flow:\n * 1. On first call (`forceReauth = false`), attempt to load a cached user\n * from `localStorage` under the key `\"1auth-user\"`. If the JSON is\n * malformed the cache entry is removed so the next call starts clean.\n * 2. If no cached user exists (or `forceReauth` is `true`), open the auth\n * modal. On success, persist `{ username, address }` to `localStorage`\n * so subsequent clicks skip authentication.\n * 3. Submit the intent. If the server responds with \"User not found\" — which\n * happens when a cached username/address no longer exists on the server —\n * clear `localStorage` and call `executePayment(true)` to force a fresh\n * authentication before retrying once.\n *\n * @param forceReauth - When `true`, skip the localStorage lookup and go\n * straight to the auth modal. Used internally on \"User not found\" errors\n * to recover from a stale cache without requiring a second user gesture.\n */\n const executePayment = async (forceReauth = false) => {\n // Try to get existing user from localStorage\n let username: string | null = null;\n let accountAddress: string | null = null;\n if (!forceReauth) {\n const savedUser = localStorage.getItem(\"1auth-user\");\n if (savedUser) {\n try {\n const parsed = JSON.parse(savedUser);\n username = parsed.username || null;\n accountAddress = parsed.address || null;\n } catch {\n localStorage.removeItem(\"1auth-user\");\n }\n }\n }\n\n // If no user (or forced reauth), authenticate first\n if (!username && !accountAddress) {\n const authResult = await client.authWithModal();\n if (authResult.success && authResult.user) {\n username = authResult.user.username || null;\n accountAddress = authResult.user.address || null;\n const stored: Record<string, string> = {};\n if (username) stored.username = username;\n if (accountAddress) stored.address = accountAddress;\n if (authResult.signerType) stored.signerType = authResult.signerType;\n localStorage.setItem(\"1auth-user\", JSON.stringify(stored));\n } else {\n // Auth cancelled or failed\n return;\n }\n }\n\n // Send the intent\n let result: SendIntentResult;\n try {\n result = await client.sendIntent({\n ...intent,\n username: username || undefined,\n accountAddress: accountAddress || undefined,\n closeOn,\n });\n } catch (err) {\n // If user not found, clear localStorage and force re-authentication\n if (err instanceof Error && err.message.includes(\"User not found\")) {\n localStorage.removeItem(\"1auth-user\");\n return executePayment(true);\n }\n throw err;\n }\n\n if (result.success) {\n setIsSuccess(true);\n onSuccess?.(result);\n } else {\n // If user not found error in result, clear localStorage and retry\n if (result.error?.message?.includes(\"User not found\")) {\n localStorage.removeItem(\"1auth-user\");\n return executePayment(true);\n }\n onError?.(new Error(result.error?.message || \"Payment failed\"));\n }\n };\n\n const combinedStyles: React.CSSProperties = {\n ...defaultStyles,\n ...(isSuccess ? defaultSuccessStyles : {}),\n ...(isHovered && !disabled && !isProcessing && !isSuccess ? defaultHoverStyles : {}),\n ...(disabled || isProcessing ? defaultDisabledStyles : {}),\n ...style,\n };\n\n return (\n <button\n type=\"button\"\n className={className}\n style={combinedStyles}\n onClick={handleClick}\n disabled={disabled || isProcessing || isSuccess}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n {isSuccess ? (\n <>\n <CheckIcon className=\"pay-button-icon\" />\n Paid\n </>\n ) : isProcessing ? (\n \"Processing...\"\n ) : (\n <>\n {!hideIcon && <FingerprintIcon className=\"pay-button-icon\" />}\n {children}\n </>\n )}\n </button>\n );\n}\n\n/**\n * Margin (ms) before a JWT's `exp` at which the cached access token is\n * treated as stale. Covers clock skew between the minting app server and\n * the orchestrator's verifier plus in-flight time of the prepare call —\n * a token that verifies fine locally can still be rejected downstream if\n * it expires mid-request.\n */\nconst ACCESS_TOKEN_EXPIRY_MARGIN_MS = 60_000;\n\n/**\n * Check whether a cached JWT access token is still safely usable.\n *\n * Decodes the payload WITHOUT verifying the signature — the client only\n * needs the `exp` claim to know when to re-mint; verification happens\n * server-side. Returns `false` (unusable) when the token is expired or\n * within {@link ACCESS_TOKEN_EXPIRY_MARGIN_MS} of expiry. Tokens that\n * can't be decoded or carry no numeric `exp` are treated as unusable so\n * a malformed cache entry never gets served indefinitely.\n */\nexport function isAccessTokenFresh(token: string): boolean {\n try {\n const payload = token.split(\".\")[1];\n if (!payload) return false;\n // base64url → base64 before atob; JWTs use the url-safe alphabet.\n const json = atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n const claims = JSON.parse(json) as { exp?: unknown };\n if (typeof claims.exp !== \"number\") return false;\n return Date.now() < claims.exp * 1000 - ACCESS_TOKEN_EXPIRY_MARGIN_MS;\n } catch {\n return false;\n }\n}\n\n/**\n * Build a {@link SponsorshipCallbackConfig} from token endpoint URLs with an\n * expiry-aware access-token cache held in a closure.\n *\n * Exported as the non-React core of {@link useSponsorship} so the caching\n * behaviour is unit-testable (and usable outside React). The access token is\n * cached and re-used only while its `exp` claim is comfortably in the\n * future — a long-lived tab must re-mint rather than serve a token the\n * orchestrator will reject with a 401 (`\"exp\" claim timestamp check\n * failed`), which previously surfaced to users as \"Failed to get quote from\n * orchestrator\". The extension token is always fetched fresh per intent\n * because it binds to a specific `intentOp`.\n *\n * Both fetches use `credentials: \"include\"` so the app's own session cookies\n * authenticate the user — no separate auth channel needed.\n */\nexport function createCachedSponsorship(\n config: SponsorshipUrlConfig,\n): SponsorshipCallbackConfig {\n const { accessTokenUrl, extensionTokenUrl } = config;\n let cachedToken: string | null = null;\n\n const fetchAccessToken = async (): Promise<string> => {\n const res = await fetch(accessTokenUrl, {\n method: \"GET\",\n credentials: \"include\",\n });\n if (!res.ok) {\n // Folds the server's JSON error reason into the message so\n // misconfigurations aren't reduced to an opaque HTTP status.\n throw await tokenFetchError(\"sponsorship access token\", res);\n }\n const body = (await res.json()) as { token?: string };\n if (!body.token) throw new Error(\"Missing `token` in access-token response\");\n cachedToken = body.token;\n return body.token;\n };\n\n return {\n accessToken: async () =>\n cachedToken && isAccessTokenFresh(cachedToken)\n ? cachedToken\n : await fetchAccessToken(),\n getExtensionToken: async (intentOp: string) => {\n const res = await fetch(extensionTokenUrl, {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ intentOp }),\n });\n if (res.status === 401) {\n // Access token likely expired — drop cached copy so the next\n // accessToken() call re-mints, then surface the error for the\n // integrator to retry.\n cachedToken = null;\n }\n if (!res.ok) {\n throw await tokenFetchError(\"sponsorship extension token\", res);\n }\n const body = (await res.json()) as { token?: string };\n if (!body.token) throw new Error(\"Missing `token` in extension-token response\");\n return body.token;\n },\n };\n}\n\n/**\n * React hook wrapper around {@link createCachedSponsorship}. The returned\n * config is stable across renders (memoized by URL), so passing it to\n * `new OneAuthClient({ sponsorship })` or `client.setSponsorship(...)` does\n * not thrash the client. Re-creating on URL change naturally resets the\n * cached token so we never serve a token minted against the previous\n * endpoint.\n */\nexport function useSponsorship(\n config: SponsorshipUrlConfig,\n): SponsorshipCallbackConfig {\n const { accessTokenUrl, extensionTokenUrl } = config;\n\n return React.useMemo<SponsorshipCallbackConfig>(\n () => createCachedSponsorship({ accessTokenUrl, extensionTokenUrl }),\n [accessTokenUrl, extensionTokenUrl],\n );\n}\n\n// Re-export types for convenience\nexport type { SendIntentOptions, SendIntentResult, CloseOnStatus } from \"./types\";\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/react.tsx"],"sourcesContent":["/**\n * @file React integration for the 1auth SDK.\n *\n * Exports the {@link PayButton} component — a ready-to-use payment button that\n * handles authentication, session recovery, and intent submission without\n * requiring the consuming app to manage auth state directly.\n *\n * @module @rhinestone/1auth/react\n */\n\nimport * as React from \"react\";\nimport { OneAuthClient } from \"./client\";\nimport { tokenFetchError } from \"./sponsorship-fetch\";\nimport type {\n SendIntentOptions,\n SendIntentResult,\n CloseOnStatus,\n SponsorshipCallbackConfig,\n SponsorshipUrlConfig,\n} from \"./types\";\n\n// Fingerprint icon SVG\nconst FingerprintIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M12 10a2 2 0 0 0-2 2c0 1.02-.1 2.51-.26 4\" />\n <path d=\"M14 13.12c0 2.38 0 6.38-1 8.88\" />\n <path d=\"M17.29 21.02c.12-.6.43-2.3.5-3.02\" />\n <path d=\"M2 12a10 10 0 0 1 18-6\" />\n <path d=\"M2 16h.01\" />\n <path d=\"M21.8 16c.2-2 .131-5.354 0-6\" />\n <path d=\"M5 19.5C5.5 18 6 15 6 12a6 6 0 0 1 .34-2\" />\n <path d=\"M8.65 22c.21-.66.45-1.32.57-2\" />\n <path d=\"M9 6.8a6 6 0 0 1 9 5.2v2\" />\n </svg>\n);\n\n// Default styles\nconst defaultStyles: React.CSSProperties = {\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: \"8px\",\n padding: \"16px 32px\",\n fontSize: \"16px\",\n fontWeight: 500,\n color: \"#ffffff\",\n backgroundColor: \"#18181b\",\n border: \"none\",\n borderRadius: \"9999px\",\n cursor: \"pointer\",\n transition: \"background-color 0.2s\",\n width: \"100%\",\n};\n\nconst defaultDisabledStyles: React.CSSProperties = {\n opacity: 0.5,\n cursor: \"not-allowed\",\n};\n\nconst defaultHoverStyles: React.CSSProperties = {\n backgroundColor: \"#27272a\",\n};\n\nconst defaultSuccessStyles: React.CSSProperties = {\n backgroundColor: \"#16a34a\",\n cursor: \"default\",\n};\n\n// Checkmark icon SVG\nconst CheckIcon = ({ className }: { className?: string }) => (\n <svg\n className={className}\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M20 6L9 17l-5-5\" />\n </svg>\n);\n\nexport interface PayButtonProps {\n /** The OneAuthClient instance */\n client: OneAuthClient;\n /**\n * Intent parameters (calls, targetChain, etc.). `accountAddress` is omitted\n * because PayButton resolves it internally (cached session or auth modal) and\n * overwrites it on the sendIntent call — callers pass only calls + target\n * chain, keeping the drop-in payment API intact.\n */\n intent: Omit<SendIntentOptions, \"closeOn\" | \"accountAddress\">;\n /** Called when payment succeeds */\n onSuccess?: (result: SendIntentResult) => void;\n /** Called when payment fails */\n onError?: (error: Error) => void;\n /** When to close the dialog and return success. Defaults to \"preconfirmed\" */\n closeOn?: CloseOnStatus;\n /** Button text - defaults to \"Pay with 1auth\" */\n children?: React.ReactNode;\n /** Custom class name */\n className?: string;\n /** Custom inline styles (merged with defaults) */\n style?: React.CSSProperties;\n /** Disabled state */\n disabled?: boolean;\n /** Hide the fingerprint icon */\n hideIcon?: boolean;\n}\n\n/**\n * A pre-built payment button that integrates 1auth passkey authentication\n * and intent submission into a single interactive element.\n *\n * The button manages its own loading and success states. On first click it\n * recovers the last authenticated user from `localStorage`; if none is found\n * it opens the auth modal before submitting the intent. On subsequent clicks\n * the stored session is reused so the user only needs to re-authenticate when\n * their session has been invalidated server-side.\n *\n * @param props - See {@link PayButtonProps} for the full set of options.\n *\n * @example\n * ```tsx\n * import { PayButton } from \"@rhinestone/1auth/react\";\n * import { client } from \"./auth\"; // your OneAuthClient instance\n *\n * <PayButton\n * client={client}\n * intent={{\n * targetChain: 8453, // Base\n * calls: [{ to: \"0xRecipient\", data: \"0x\", value: \"1000000\" }],\n * }}\n * onSuccess={(result) => console.log(\"tx:\", result.transactionHash)}\n * onError={(err) => console.error(err)}\n * >\n * Pay $5 USDC\n * </PayButton>\n * ```\n */\nexport function PayButton({\n client,\n intent,\n onSuccess,\n onError,\n closeOn = \"preconfirmed\",\n children = \"Pay with 1auth\",\n className,\n style,\n disabled,\n hideIcon,\n}: PayButtonProps) {\n const [isProcessing, setIsProcessing] = React.useState(false);\n const [isSuccess, setIsSuccess] = React.useState(false);\n const [isHovered, setIsHovered] = React.useState(false);\n\n const handleClick = async () => {\n if (disabled || isProcessing || isSuccess) return;\n\n setIsProcessing(true);\n\n try {\n await executePayment();\n } catch (err) {\n if (err instanceof Error && !err.message.includes(\"rejected\")) {\n onError?.(err);\n }\n } finally {\n setIsProcessing(false);\n }\n };\n\n /**\n * Core payment execution with built-in session recovery and re-auth retry.\n *\n * Flow:\n * 1. On first call (`forceReauth = false`), attempt to load a cached user\n * from `localStorage` under the key `\"1auth-user\"`. If the JSON is\n * malformed the cache entry is removed so the next call starts clean.\n * 2. If no cached user exists (or `forceReauth` is `true`), open the auth\n * modal. On success, persist `{ address }` to `localStorage`\n * so subsequent clicks skip authentication.\n * 3. Submit the intent. If the server responds with \"User not found\" — which\n * happens when a cached account address no longer exists on the server —\n * clear `localStorage` and call `executePayment(true)` to force a fresh\n * authentication before retrying once.\n *\n * @param forceReauth - When `true`, skip the localStorage lookup and go\n * straight to the auth modal. Used internally on \"User not found\" errors\n * to recover from a stale cache without requiring a second user gesture.\n */\n const executePayment = async (forceReauth = false) => {\n // Try to get existing account address from localStorage\n let accountAddress: string | null = null;\n if (!forceReauth) {\n const savedUser = localStorage.getItem(\"1auth-user\");\n if (savedUser) {\n try {\n const parsed = JSON.parse(savedUser);\n accountAddress = parsed.address || null;\n } catch {\n localStorage.removeItem(\"1auth-user\");\n }\n }\n }\n\n // If no account address (or forced reauth), authenticate first\n if (!accountAddress) {\n const authResult = await client.authWithModal();\n if (authResult.success && authResult.user) {\n accountAddress = authResult.user.address || null;\n const stored: Record<string, string> = {};\n if (accountAddress) stored.address = accountAddress;\n if (authResult.signerType) stored.signerType = authResult.signerType;\n localStorage.setItem(\"1auth-user\", JSON.stringify(stored));\n } else {\n // Auth cancelled or failed\n return;\n }\n }\n\n // Send the intent\n let result: SendIntentResult;\n try {\n result = await client.sendIntent({\n ...intent,\n // Guaranteed non-null: the `if (!accountAddress) { ...return }` guard\n // above either resolved an address or bailed out before reaching here.\n accountAddress,\n closeOn,\n });\n } catch (err) {\n // If user not found, clear localStorage and force re-authentication\n if (err instanceof Error && err.message.includes(\"User not found\")) {\n localStorage.removeItem(\"1auth-user\");\n return executePayment(true);\n }\n throw err;\n }\n\n if (result.success) {\n setIsSuccess(true);\n onSuccess?.(result);\n } else {\n // If user not found error in result, clear localStorage and retry\n if (result.error?.message?.includes(\"User not found\")) {\n localStorage.removeItem(\"1auth-user\");\n return executePayment(true);\n }\n onError?.(new Error(result.error?.message || \"Payment failed\"));\n }\n };\n\n const combinedStyles: React.CSSProperties = {\n ...defaultStyles,\n ...(isSuccess ? defaultSuccessStyles : {}),\n ...(isHovered && !disabled && !isProcessing && !isSuccess ? defaultHoverStyles : {}),\n ...(disabled || isProcessing ? defaultDisabledStyles : {}),\n ...style,\n };\n\n return (\n <button\n type=\"button\"\n className={className}\n style={combinedStyles}\n onClick={handleClick}\n disabled={disabled || isProcessing || isSuccess}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n >\n {isSuccess ? (\n <>\n <CheckIcon className=\"pay-button-icon\" />\n Paid\n </>\n ) : isProcessing ? (\n \"Processing...\"\n ) : (\n <>\n {!hideIcon && <FingerprintIcon className=\"pay-button-icon\" />}\n {children}\n </>\n )}\n </button>\n );\n}\n\n/**\n * Margin (ms) before a JWT's `exp` at which the cached access token is\n * treated as stale. Covers clock skew between the minting app server and\n * the orchestrator's verifier plus in-flight time of the prepare call —\n * a token that verifies fine locally can still be rejected downstream if\n * it expires mid-request.\n */\nconst ACCESS_TOKEN_EXPIRY_MARGIN_MS = 60_000;\n\n/**\n * Check whether a cached JWT access token is still safely usable.\n *\n * Decodes the payload WITHOUT verifying the signature — the client only\n * needs the `exp` claim to know when to re-mint; verification happens\n * server-side. Returns `false` (unusable) when the token is expired or\n * within {@link ACCESS_TOKEN_EXPIRY_MARGIN_MS} of expiry. Tokens that\n * can't be decoded or carry no numeric `exp` are treated as unusable so\n * a malformed cache entry never gets served indefinitely.\n */\nexport function isAccessTokenFresh(token: string): boolean {\n try {\n const payload = token.split(\".\")[1];\n if (!payload) return false;\n // base64url → base64 before atob; JWTs use the url-safe alphabet.\n const json = atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n const claims = JSON.parse(json) as { exp?: unknown };\n if (typeof claims.exp !== \"number\") return false;\n return Date.now() < claims.exp * 1000 - ACCESS_TOKEN_EXPIRY_MARGIN_MS;\n } catch {\n return false;\n }\n}\n\n/**\n * Build a {@link SponsorshipCallbackConfig} from token endpoint URLs with an\n * expiry-aware access-token cache held in a closure.\n *\n * Exported as the non-React core of {@link useSponsorship} so the caching\n * behaviour is unit-testable (and usable outside React). The access token is\n * cached and re-used only while its `exp` claim is comfortably in the\n * future — a long-lived tab must re-mint rather than serve a token the\n * orchestrator will reject with a 401 (`\"exp\" claim timestamp check\n * failed`), which previously surfaced to users as \"Failed to get quote from\n * orchestrator\". The extension token is always fetched fresh per intent\n * because it binds to a specific `intentOp`.\n *\n * Both fetches use `credentials: \"include\"` so the app's own session cookies\n * authenticate the user — no separate auth channel needed.\n */\nexport function createCachedSponsorship(\n config: SponsorshipUrlConfig,\n): SponsorshipCallbackConfig {\n const { accessTokenUrl, extensionTokenUrl } = config;\n let cachedToken: string | null = null;\n\n const fetchAccessToken = async (): Promise<string> => {\n const res = await fetch(accessTokenUrl, {\n method: \"GET\",\n credentials: \"include\",\n });\n if (!res.ok) {\n // Folds the server's JSON error reason into the message so\n // misconfigurations aren't reduced to an opaque HTTP status.\n throw await tokenFetchError(\"sponsorship access token\", res);\n }\n const body = (await res.json()) as { token?: string };\n if (!body.token) throw new Error(\"Missing `token` in access-token response\");\n cachedToken = body.token;\n return body.token;\n };\n\n return {\n accessToken: async () =>\n cachedToken && isAccessTokenFresh(cachedToken)\n ? cachedToken\n : await fetchAccessToken(),\n getExtensionToken: async (intentOp: string) => {\n const res = await fetch(extensionTokenUrl, {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ intentOp }),\n });\n if (res.status === 401) {\n // Access token likely expired — drop cached copy so the next\n // accessToken() call re-mints, then surface the error for the\n // integrator to retry.\n cachedToken = null;\n }\n if (!res.ok) {\n throw await tokenFetchError(\"sponsorship extension token\", res);\n }\n const body = (await res.json()) as { token?: string };\n if (!body.token) throw new Error(\"Missing `token` in extension-token response\");\n return body.token;\n },\n };\n}\n\n/**\n * React hook wrapper around {@link createCachedSponsorship}. The returned\n * config is stable across renders (memoized by URL), so passing it to\n * `new OneAuthClient({ sponsorship })` or `client.setSponsorship(...)` does\n * not thrash the client. Re-creating on URL change naturally resets the\n * cached token so we never serve a token minted against the previous\n * endpoint.\n */\nexport function useSponsorship(\n config: SponsorshipUrlConfig,\n): SponsorshipCallbackConfig {\n const { accessTokenUrl, extensionTokenUrl } = config;\n\n return React.useMemo<SponsorshipCallbackConfig>(\n () => createCachedSponsorship({ accessTokenUrl, extensionTokenUrl }),\n [accessTokenUrl, extensionTokenUrl],\n );\n}\n\n// Re-export types for convenience\nexport type { SendIntentOptions, SendIntentResult, CloseOnStatus } from \"./types\";\n"],"mappings":"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","names":[]}
|
package/dist/server.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Address, Hex } from 'viem';
|
|
2
|
-
export { e as encodeWebAuthnSignature, q as hashMessage, k as isTestnet, v as verifyMessageHash } from './verify-
|
|
3
|
-
export { W as WebAuthnSignature } from './types-
|
|
2
|
+
export { e as encodeWebAuthnSignature, q as hashMessage, k as isTestnet, v as verifyMessageHash } from './verify-0VXQpQBJ.mjs';
|
|
3
|
+
export { W as WebAuthnSignature } from './types-Dzm5lZK-.mjs';
|
|
4
4
|
import '@rhinestone/sdk';
|
|
5
5
|
|
|
6
6
|
/**
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Address, Hex } from 'viem';
|
|
2
|
-
export { e as encodeWebAuthnSignature, q as hashMessage, k as isTestnet, v as verifyMessageHash } from './verify-
|
|
3
|
-
export { W as WebAuthnSignature } from './types-
|
|
2
|
+
export { e as encodeWebAuthnSignature, q as hashMessage, k as isTestnet, v as verifyMessageHash } from './verify-9UgxLSdo.js';
|
|
3
|
+
export { W as WebAuthnSignature } from './types-Dzm5lZK-.js';
|
|
4
4
|
import '@rhinestone/sdk';
|
|
5
5
|
|
|
6
6
|
/**
|