@cedros/login-react 0.0.17 → 0.0.18
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/{GoogleLoginButton-CXwp4LsQ.js → GoogleLoginButton-CwKEUISj.js} +19 -19
- package/dist/{GoogleLoginButton-CXwp4LsQ.js.map → GoogleLoginButton-CwKEUISj.js.map} +1 -1
- package/dist/{GoogleLoginButton-zS_69-KV.cjs → GoogleLoginButton-oM5fMYPB.cjs} +1 -1
- package/dist/{GoogleLoginButton-zS_69-KV.cjs.map → GoogleLoginButton-oM5fMYPB.cjs.map} +1 -1
- package/dist/google-only.cjs +1 -1
- package/dist/google-only.js +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/login-react.css +1 -1
- package/dist/{mobileWalletAdapter-coZRD4Yx.js → mobileWalletAdapter-BulleEbi.js} +2 -2
- package/dist/{mobileWalletAdapter-coZRD4Yx.js.map → mobileWalletAdapter-BulleEbi.js.map} +1 -1
- package/dist/{mobileWalletAdapter-Dp4yFxCm.cjs → mobileWalletAdapter-DOdmFAk0.cjs} +1 -1
- package/dist/{mobileWalletAdapter-Dp4yFxCm.cjs.map → mobileWalletAdapter-DOdmFAk0.cjs.map} +1 -1
- package/dist/solana-only.cjs +1 -1
- package/dist/solana-only.js +1 -1
- package/package.json +1 -1
|
@@ -168,8 +168,8 @@ function le({
|
|
|
168
168
|
md: "cedros-button-md",
|
|
169
169
|
lg: "cedros-button-lg"
|
|
170
170
|
}, R = {
|
|
171
|
-
default: "cedros-button-
|
|
172
|
-
outline: "cedros-button-
|
|
171
|
+
default: "cedros-button-social",
|
|
172
|
+
outline: "cedros-button-social-outline"
|
|
173
173
|
}, P = f || k || o && !g;
|
|
174
174
|
return /* @__PURE__ */ w($, { children: [
|
|
175
175
|
/* @__PURE__ */ w(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobileWalletAdapter-coZRD4Yx.js","sources":["../src/hooks/useSolanaAuth.ts","../src/utils/walletDetection.ts","../src/components/solana/SolanaLoginButton.tsx","../src/utils/mobileWalletAdapter.ts"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport { validateSolanaPublicKey } from '../utils/validation';\nimport type { AuthResponse, AuthError, ChallengeResponse } from '../types';\n\nexport interface UseSolanaAuthReturn {\n requestChallenge: (publicKey: string) => Promise<ChallengeResponse>;\n signIn: (publicKey: string, signature: string, message: string) => Promise<AuthResponse>;\n isLoading: boolean;\n error: AuthError | null;\n clearError: () => void;\n}\n\n/**\n * Hook for Solana wallet authentication.\n *\n * @example\n * ```tsx\n * function SolanaLogin() {\n * const { requestChallenge, signIn, isLoading } = useSolanaAuth();\n * const { publicKey, signMessage } = useWallet();\n *\n * const handleLogin = async () => {\n * const challenge = await requestChallenge(publicKey.toBase58());\n * const signature = await signMessage(new TextEncoder().encode(challenge.message));\n * const result = await signIn(\n * publicKey.toBase58(),\n * Buffer.from(signature).toString('base64'),\n * challenge.message\n * );\n * };\n * }\n * ```\n */\nexport function useSolanaAuth(): UseSolanaAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n const requestChallenge = useCallback(\n async (publicKey: string): Promise<ChallengeResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<ChallengeResponse>(\n '/solana/challenge',\n { publicKey },\n { credentials: 'omit' }\n );\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Failed to get challenge');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient]\n );\n\n const signIn = useCallback(\n async (publicKey: string, signature: string, message: string): Promise<AuthResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<AuthResponse>('/solana', {\n publicKey,\n signature,\n message,\n });\n config.callbacks?.onLoginSuccess?.(data.user, 'solana');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Solana sign-in failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, config.callbacks, _internal]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n return {\n requestChallenge,\n signIn,\n isLoading,\n error,\n clearError,\n };\n}\n","/**\n * Wallet detection utilities for Solana browser wallets\n */\n\n/**\n * Type for window with potential Solana wallet extensions\n */\ninterface WindowWithWallets extends Window {\n phantom?: { solana?: unknown };\n solflare?: { solana?: unknown };\n backpack?: { solana?: unknown };\n glow?: { solana?: unknown };\n slope?: { solana?: unknown };\n sollet?: { solana?: unknown };\n coin98?: { solana?: unknown };\n clover?: { solana?: unknown };\n mathWallet?: { solana?: unknown };\n ledger?: { solana?: unknown };\n torus?: { solana?: unknown };\n walletconnect?: { solana?: unknown };\n solana?: unknown;\n}\n\n/**\n * Known Solana wallet provider names to check for on the window object\n */\ntype WalletProviderName = Exclude<keyof WindowWithWallets, keyof Window>;\n\nconst WALLET_PROVIDERS: WalletProviderName[] = [\n 'phantom',\n 'solflare',\n 'backpack',\n 'glow',\n 'slope',\n 'sollet',\n 'coin98',\n 'clover',\n 'mathWallet',\n 'ledger',\n 'torus',\n 'walletconnect',\n];\n\n/**\n * UI-9 FIX: Validates that a wallet provider object has expected Solana wallet methods.\n * Prevents spoofed wallet objects from being accepted.\n */\nfunction isValidSolanaProvider(provider: unknown): boolean {\n if (!provider || typeof provider !== 'object') return false;\n const wallet = provider as Record<string, unknown>;\n // Check for at least one expected wallet method/property\n // Real wallets have connect, signMessage, signTransaction, etc.\n return (\n typeof wallet.connect === 'function' ||\n typeof wallet.signMessage === 'function' ||\n typeof wallet.signTransaction === 'function' ||\n 'isConnected' in wallet\n );\n}\n\n/**\n * Check for wallets registered via the Wallet Standard protocol.\n * MWA (via @solana-mobile/wallet-standard-mobile) registers this way.\n */\nfunction hasWalletStandardWallets(): boolean {\n try {\n // The wallet-standard protocol exposes registered wallets via a global\n const walletStandard = (window as unknown as Record<string, unknown>)['__wallet_standard__'];\n if (\n walletStandard &&\n typeof walletStandard === 'object' &&\n 'get' in walletStandard &&\n typeof (walletStandard as Record<string, unknown>).get === 'function'\n ) {\n const wallets = (walletStandard as { get: () => unknown[] }).get();\n return Array.isArray(wallets) && wallets.length > 0;\n }\n } catch {\n // Not available\n }\n return false;\n}\n\n/**\n * Detects if any Solana wallet extensions are installed in the browser.\n * Checks for common wallet adapters like Phantom, Solflare, Backpack, etc.\n *\n * @returns true if at least one Solana wallet is detected\n *\n * @example\n * ```tsx\n * if (detectSolanaWallets()) {\n * // Show Solana login button\n * }\n * ```\n */\nexport function detectSolanaWallets(): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n const win = window as WindowWithWallets;\n\n // Check window object for wallet injections\n // UI-9: Validate wallet has expected methods to reject spoofed providers\n for (const provider of WALLET_PROVIDERS) {\n const walletObj = win[provider];\n if (\n walletObj &&\n typeof walletObj === 'object' &&\n 'solana' in walletObj &&\n isValidSolanaProvider(walletObj.solana)\n ) {\n return true;\n }\n }\n\n // Check for generic Solana provider (e.g., from some mobile wallet browsers)\n // UI-9: Also validate generic provider\n if (isValidSolanaProvider(win.solana)) {\n return true;\n }\n\n // Check for wallet-standard registered wallets (e.g., MWA via registerMwa())\n if (hasWalletStandardWallets()) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Returns list of detected Solana wallet names (for debugging/display)\n *\n * @returns Array of detected wallet provider names\n */\nexport function getDetectedWalletNames(): string[] {\n if (typeof window === 'undefined') {\n return [];\n }\n\n const win = window as WindowWithWallets;\n const detected: string[] = [];\n\n // UI-9: Use validation to reject spoofed providers\n for (const provider of WALLET_PROVIDERS) {\n const walletObj = win[provider];\n if (\n walletObj &&\n typeof walletObj === 'object' &&\n 'solana' in walletObj &&\n isValidSolanaProvider(walletObj.solana)\n ) {\n detected.push(provider);\n }\n }\n\n if (isValidSolanaProvider(win.solana) && detected.length === 0) {\n detected.push('solana');\n }\n\n return detected;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { useSolanaAuth } from '../../hooks/useSolanaAuth';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\nimport { detectSolanaWallets } from '../../utils/walletDetection';\n\nexport interface SolanaLoginButtonProps {\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n className?: string;\n variant?: 'default' | 'outline';\n size?: 'sm' | 'md' | 'lg';\n disabled?: boolean;\n /**\n * Hide the button if no Solana wallets are detected.\n * When true (default), button renders nothing if no wallets are installed.\n * When false, button always renders (useful for showing \"install wallet\" prompts).\n * @default true\n */\n hideIfNoWallet?: boolean;\n /**\n * Solana wallet adapter context. Pass this from @solana/wallet-adapter-react's useWallet().\n * The button will handle connection and signing automatically for a one-click experience.\n */\n walletContext?: {\n publicKey: { toBase58: () => string } | null;\n signMessage: ((message: Uint8Array) => Promise<Uint8Array>) | null;\n connected: boolean;\n connecting: boolean;\n connect: () => Promise<void>;\n wallet: { adapter: { name: string } } | null;\n select: (walletName: string) => void;\n wallets: Array<{\n adapter: {\n name: string;\n icon: string;\n readyState: string;\n };\n }>;\n };\n}\n\n/**\n * Solana wallet login button with one-click authentication.\n *\n * Handles wallet connection and message signing automatically.\n * If wallet is already connected, signs immediately.\n * If not connected, connects first then auto-signs.\n */\nexport function SolanaLoginButton({\n onSuccess,\n onError,\n className = '',\n variant = 'default',\n size = 'md',\n disabled = false,\n hideIfNoWallet = true,\n walletContext,\n}: SolanaLoginButtonProps) {\n const { requestChallenge, signIn, isLoading: isAuthLoading } = useSolanaAuth();\n const [showWalletSelector, setShowWalletSelector] = useState(false);\n const [pendingLogin, setPendingLogin] = useState(false);\n const [triggerConnect, setTriggerConnect] = useState(false);\n const isProcessingRef = useRef(false);\n\n // Detect wallets from browser (used when walletContext not provided)\n // Use lazy initializer to avoid re-detecting on every render\n const [browserHasWallets] = useState(() => detectSolanaWallets());\n\n const connected = walletContext?.connected ?? false;\n const connecting = walletContext?.connecting ?? false;\n const publicKey = walletContext?.publicKey;\n const signMessage = walletContext?.signMessage;\n const wallet = walletContext?.wallet;\n const wallets = walletContext?.wallets ?? [];\n\n // Get installed/ready wallets from wallet adapter context\n const installedWallets = wallets.filter(\n (w) => w.adapter.readyState === 'Installed' || w.adapter.readyState === 'Loadable'\n );\n\n // Determine if any wallets are available\n // If walletContext is provided, use its wallet list; otherwise use browser detection\n const hasWallets = walletContext ? installedWallets.length > 0 : browserHasWallets;\n\n // Execute the actual sign-in flow\n const executeSignIn = useCallback(async () => {\n if (isProcessingRef.current) return;\n if (!publicKey || !signMessage) {\n onError?.(new Error('Wallet not ready'));\n return;\n }\n\n isProcessingRef.current = true;\n try {\n const pubKeyString = publicKey.toBase58();\n\n // Request challenge from server\n const challenge = await requestChallenge(pubKeyString);\n\n // Sign the message with wallet\n const messageBytes = new TextEncoder().encode(challenge.message);\n const signatureBytes = await signMessage(messageBytes);\n\n // Validate signature\n if (!(signatureBytes instanceof Uint8Array) || signatureBytes.length === 0) {\n throw new Error('Wallet returned invalid signature');\n }\n\n // Encode signature\n let signature: string;\n try {\n signature = btoa(String.fromCharCode(...signatureBytes));\n } catch {\n throw new Error('Failed to encode signature');\n }\n\n // Authenticate with server\n await signIn(pubKeyString, signature, challenge.message);\n\n onSuccess?.();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\n } finally {\n isProcessingRef.current = false;\n setPendingLogin(false);\n }\n }, [publicKey, signMessage, requestChallenge, signIn, onSuccess, onError]);\n\n // Auto-connect when wallet is selected and triggerConnect is set\n useEffect(() => {\n if (triggerConnect && wallet && !connected && !connecting && walletContext?.connect) {\n setTriggerConnect(false);\n walletContext.connect().catch((err) => {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n setPendingLogin(false);\n });\n }\n }, [triggerConnect, wallet, connected, connecting, walletContext, onError]);\n\n // Auto-execute sign-in when connected with pending login\n // COMP-04: Catch unhandled rejections from executeSignIn (defensive - errors already\n // handled internally, but .catch() prevents unhandledrejection events)\n useEffect(() => {\n if (pendingLogin && connected && publicKey && signMessage && !isProcessingRef.current) {\n executeSignIn().catch(() => {\n /* Errors already passed to onError callback inside executeSignIn */\n });\n }\n }, [pendingLogin, connected, publicKey, signMessage, executeSignIn]);\n\n // Hide button if no wallets detected and hideIfNoWallet is true\n // Note: This must come after all hooks to satisfy React rules of hooks\n if (hideIfNoWallet && !hasWallets) {\n return null;\n }\n\n const handleClick = async () => {\n if (disabled || isAuthLoading || connecting) return;\n\n if (connected && publicKey && signMessage) {\n // Already connected - sign immediately\n setPendingLogin(true);\n await executeSignIn();\n } else if (wallet) {\n // Wallet selected but not connected - connect and queue login\n setPendingLogin(true);\n setTriggerConnect(true);\n } else if (installedWallets.length === 1) {\n // Only one wallet installed - auto-select it\n walletContext?.select(installedWallets[0].adapter.name);\n setPendingLogin(true);\n setTriggerConnect(true);\n } else if (installedWallets.length > 1) {\n // Multiple wallets - show selector\n setShowWalletSelector(true);\n } else {\n // No wallets - show error\n onError?.(\n new Error('No Solana wallet found. Please install Phantom or another Solana wallet.')\n );\n }\n };\n\n const handleSelectWallet = (walletName: string) => {\n setShowWalletSelector(false);\n walletContext?.select(walletName);\n setPendingLogin(true);\n setTriggerConnect(true);\n };\n\n const sizeClasses = {\n sm: 'cedros-button-sm',\n md: 'cedros-button-md',\n lg: 'cedros-button-lg',\n };\n\n const variantClasses = {\n default: 'cedros-button-solana',\n outline: 'cedros-button-solana-outline',\n };\n\n const isLoading = isAuthLoading || connecting || (pendingLogin && !connected);\n\n return (\n <>\n <button\n type=\"button\"\n className={`cedros-button ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}\n onClick={handleClick}\n disabled={disabled || isLoading}\n aria-label=\"Continue with Solana\"\n >\n {isLoading ? (\n <LoadingSpinner size=\"sm\" />\n ) : (\n <svg\n className=\"cedros-button-icon\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 128 128\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M25.38 96.04a4.35 4.35 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7l-17.71 17.72a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7l17.71-17.72z\" />\n <path d=\"M25.38 11.81a4.47 4.47 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7L103.96 31.96a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7L25.38 11.81z\" />\n <path d=\"M102.62 53.76a4.35 4.35 0 0 0-3.07-1.27H7.87c-1.93 0-2.9 2.34-1.54 3.7l17.71 17.72a4.35 4.35 0 0 0 3.07 1.27h91.68c1.93 0 2.9-2.34 1.54-3.7L102.62 53.76z\" />\n </svg>\n )}\n <span>Continue with Solana</span>\n </button>\n\n {/* Wallet Selector Modal */}\n {showWalletSelector && (\n <div\n className=\"cedros-modal-backdrop\"\n onClick={() => setShowWalletSelector(false)}\n role=\"presentation\"\n >\n <div\n className=\"cedros-modal cedros-wallet-selector\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"wallet-selector-title\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"cedros-modal-header\">\n <h2 id=\"wallet-selector-title\" className=\"cedros-modal-title\">\n Select Wallet\n </h2>\n <button\n type=\"button\"\n className=\"cedros-modal-close\"\n onClick={() => setShowWalletSelector(false)}\n aria-label=\"Close\"\n >\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M18 6L6 18M6 6l12 12\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n </div>\n <div className=\"cedros-modal-content\">\n <div className=\"cedros-wallet-list\">\n {installedWallets.map((w) => (\n <button\n key={w.adapter.name}\n type=\"button\"\n className=\"cedros-wallet-option\"\n onClick={() => handleSelectWallet(w.adapter.name)}\n >\n <img\n src={w.adapter.icon}\n alt=\"\"\n width=\"32\"\n height=\"32\"\n className=\"cedros-wallet-icon\"\n />\n <span>{w.adapter.name}</span>\n </button>\n ))}\n </div>\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n","/**\n * Mobile Wallet Adapter (MWA) registration for web.\n *\n * On Android Chrome, MWA lets users authenticate with their installed Solana\n * wallet app (e.g., Phantom, Solflare) via Android Intents — no browser\n * extension needed.\n *\n * Once registered, MWA appears as a wallet option in the wallet adapter's\n * wallet list (alongside browser extension wallets). Users see it as\n * \"Use Installed Wallet\" in the wallet selector.\n *\n * Requires the optional peer dependency: @solana-mobile/wallet-standard-mobile\n *\n * @see https://docs.solanamobile.com/get-started/web/installation\n */\n\nexport interface MobileWalletConfig {\n /** App name shown in the wallet's authorization dialog */\n name?: string;\n /** App URI for identity verification */\n uri?: string;\n /** App icon path/URL shown in the wallet dialog */\n icon?: string;\n /** Solana cluster(s) to support. Default: ['solana:mainnet'] */\n chains?: string[];\n}\n\n/**\n * Register Mobile Wallet Adapter as a wallet-standard wallet.\n *\n * Call this once at your application root (before rendering). After registration,\n * MWA automatically appears as \"Use Installed Wallet\" for users browsing on\n * Android Chrome with a Solana wallet app installed.\n *\n * Must be called in a non-SSR context (browser only). For Next.js, call in a\n * Client Component with `'use client'`.\n *\n * @example\n * ```tsx\n * import { registerMobileWallet, CedrosLoginProvider } from '@cedros/login-react';\n *\n * // Register before provider mounts\n * registerMobileWallet({ name: 'My App', uri: 'https://myapp.com' });\n *\n * function App() {\n * return (\n * <CedrosLoginProvider config={{ serverUrl: '...' }}>\n * <LoginForm />\n * </CedrosLoginProvider>\n * );\n * }\n * ```\n *\n * @returns true if registration succeeded, false if package not installed or SSR\n */\nexport function registerMobileWallet(config?: MobileWalletConfig): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n try {\n // Dynamic import to avoid bundling the optional peer dep\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mwa = require('@solana-mobile/wallet-standard-mobile');\n\n const chains = config?.chains ?? ['solana:mainnet'];\n\n const registrationConfig: Record<string, unknown> = {\n appIdentity: {\n name: config?.name,\n uri: config?.uri,\n icon: config?.icon,\n },\n chains,\n };\n\n // Use built-in defaults for optional config if available\n if (typeof mwa.createDefaultAuthorizationCache === 'function') {\n registrationConfig.authorizationCache = mwa.createDefaultAuthorizationCache();\n }\n if (typeof mwa.createDefaultChainSelector === 'function') {\n registrationConfig.chainSelector = mwa.createDefaultChainSelector();\n }\n if (typeof mwa.createDefaultWalletNotFoundHandler === 'function') {\n registrationConfig.onWalletNotFound = mwa.createDefaultWalletNotFoundHandler();\n }\n\n mwa.registerMwa(registrationConfig);\n return true;\n } catch {\n // @solana-mobile/wallet-standard-mobile not installed\n return false;\n }\n}\n"],"names":["useSolanaAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","useState","error","setError","apiClient","useMemo","ApiClient","requestChallenge","useCallback","publicKey","validateSolanaPublicKey","authError","err","handleApiError","signIn","signature","message","data","clearError","WALLET_PROVIDERS","isValidSolanaProvider","provider","wallet","hasWalletStandardWallets","walletStandard","wallets","detectSolanaWallets","win","walletObj","SolanaLoginButton","onSuccess","onError","className","variant","size","disabled","hideIfNoWallet","walletContext","isAuthLoading","showWalletSelector","setShowWalletSelector","pendingLogin","setPendingLogin","triggerConnect","setTriggerConnect","isProcessingRef","useRef","browserHasWallets","connected","connecting","signMessage","installedWallets","w","hasWallets","executeSignIn","pubKeyString","challenge","messageBytes","signatureBytes","useEffect","handleClick","handleSelectWallet","walletName","sizeClasses","variantClasses","jsxs","Fragment","jsx","LoadingSpinner","e","registerMobileWallet","mwa","chains","registrationConfig"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mobileWalletAdapter-BulleEbi.js","sources":["../src/hooks/useSolanaAuth.ts","../src/utils/walletDetection.ts","../src/components/solana/SolanaLoginButton.tsx","../src/utils/mobileWalletAdapter.ts"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport { validateSolanaPublicKey } from '../utils/validation';\nimport type { AuthResponse, AuthError, ChallengeResponse } from '../types';\n\nexport interface UseSolanaAuthReturn {\n requestChallenge: (publicKey: string) => Promise<ChallengeResponse>;\n signIn: (publicKey: string, signature: string, message: string) => Promise<AuthResponse>;\n isLoading: boolean;\n error: AuthError | null;\n clearError: () => void;\n}\n\n/**\n * Hook for Solana wallet authentication.\n *\n * @example\n * ```tsx\n * function SolanaLogin() {\n * const { requestChallenge, signIn, isLoading } = useSolanaAuth();\n * const { publicKey, signMessage } = useWallet();\n *\n * const handleLogin = async () => {\n * const challenge = await requestChallenge(publicKey.toBase58());\n * const signature = await signMessage(new TextEncoder().encode(challenge.message));\n * const result = await signIn(\n * publicKey.toBase58(),\n * Buffer.from(signature).toString('base64'),\n * challenge.message\n * );\n * };\n * }\n * ```\n */\nexport function useSolanaAuth(): UseSolanaAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n const requestChallenge = useCallback(\n async (publicKey: string): Promise<ChallengeResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<ChallengeResponse>(\n '/solana/challenge',\n { publicKey },\n { credentials: 'omit' }\n );\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Failed to get challenge');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient]\n );\n\n const signIn = useCallback(\n async (publicKey: string, signature: string, message: string): Promise<AuthResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<AuthResponse>('/solana', {\n publicKey,\n signature,\n message,\n });\n config.callbacks?.onLoginSuccess?.(data.user, 'solana');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Solana sign-in failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, config.callbacks, _internal]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n return {\n requestChallenge,\n signIn,\n isLoading,\n error,\n clearError,\n };\n}\n","/**\n * Wallet detection utilities for Solana browser wallets\n */\n\n/**\n * Type for window with potential Solana wallet extensions\n */\ninterface WindowWithWallets extends Window {\n phantom?: { solana?: unknown };\n solflare?: { solana?: unknown };\n backpack?: { solana?: unknown };\n glow?: { solana?: unknown };\n slope?: { solana?: unknown };\n sollet?: { solana?: unknown };\n coin98?: { solana?: unknown };\n clover?: { solana?: unknown };\n mathWallet?: { solana?: unknown };\n ledger?: { solana?: unknown };\n torus?: { solana?: unknown };\n walletconnect?: { solana?: unknown };\n solana?: unknown;\n}\n\n/**\n * Known Solana wallet provider names to check for on the window object\n */\ntype WalletProviderName = Exclude<keyof WindowWithWallets, keyof Window>;\n\nconst WALLET_PROVIDERS: WalletProviderName[] = [\n 'phantom',\n 'solflare',\n 'backpack',\n 'glow',\n 'slope',\n 'sollet',\n 'coin98',\n 'clover',\n 'mathWallet',\n 'ledger',\n 'torus',\n 'walletconnect',\n];\n\n/**\n * UI-9 FIX: Validates that a wallet provider object has expected Solana wallet methods.\n * Prevents spoofed wallet objects from being accepted.\n */\nfunction isValidSolanaProvider(provider: unknown): boolean {\n if (!provider || typeof provider !== 'object') return false;\n const wallet = provider as Record<string, unknown>;\n // Check for at least one expected wallet method/property\n // Real wallets have connect, signMessage, signTransaction, etc.\n return (\n typeof wallet.connect === 'function' ||\n typeof wallet.signMessage === 'function' ||\n typeof wallet.signTransaction === 'function' ||\n 'isConnected' in wallet\n );\n}\n\n/**\n * Check for wallets registered via the Wallet Standard protocol.\n * MWA (via @solana-mobile/wallet-standard-mobile) registers this way.\n */\nfunction hasWalletStandardWallets(): boolean {\n try {\n // The wallet-standard protocol exposes registered wallets via a global\n const walletStandard = (window as unknown as Record<string, unknown>)['__wallet_standard__'];\n if (\n walletStandard &&\n typeof walletStandard === 'object' &&\n 'get' in walletStandard &&\n typeof (walletStandard as Record<string, unknown>).get === 'function'\n ) {\n const wallets = (walletStandard as { get: () => unknown[] }).get();\n return Array.isArray(wallets) && wallets.length > 0;\n }\n } catch {\n // Not available\n }\n return false;\n}\n\n/**\n * Detects if any Solana wallet extensions are installed in the browser.\n * Checks for common wallet adapters like Phantom, Solflare, Backpack, etc.\n *\n * @returns true if at least one Solana wallet is detected\n *\n * @example\n * ```tsx\n * if (detectSolanaWallets()) {\n * // Show Solana login button\n * }\n * ```\n */\nexport function detectSolanaWallets(): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n const win = window as WindowWithWallets;\n\n // Check window object for wallet injections\n // UI-9: Validate wallet has expected methods to reject spoofed providers\n for (const provider of WALLET_PROVIDERS) {\n const walletObj = win[provider];\n if (\n walletObj &&\n typeof walletObj === 'object' &&\n 'solana' in walletObj &&\n isValidSolanaProvider(walletObj.solana)\n ) {\n return true;\n }\n }\n\n // Check for generic Solana provider (e.g., from some mobile wallet browsers)\n // UI-9: Also validate generic provider\n if (isValidSolanaProvider(win.solana)) {\n return true;\n }\n\n // Check for wallet-standard registered wallets (e.g., MWA via registerMwa())\n if (hasWalletStandardWallets()) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Returns list of detected Solana wallet names (for debugging/display)\n *\n * @returns Array of detected wallet provider names\n */\nexport function getDetectedWalletNames(): string[] {\n if (typeof window === 'undefined') {\n return [];\n }\n\n const win = window as WindowWithWallets;\n const detected: string[] = [];\n\n // UI-9: Use validation to reject spoofed providers\n for (const provider of WALLET_PROVIDERS) {\n const walletObj = win[provider];\n if (\n walletObj &&\n typeof walletObj === 'object' &&\n 'solana' in walletObj &&\n isValidSolanaProvider(walletObj.solana)\n ) {\n detected.push(provider);\n }\n }\n\n if (isValidSolanaProvider(win.solana) && detected.length === 0) {\n detected.push('solana');\n }\n\n return detected;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { useSolanaAuth } from '../../hooks/useSolanaAuth';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\nimport { detectSolanaWallets } from '../../utils/walletDetection';\n\nexport interface SolanaLoginButtonProps {\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n className?: string;\n variant?: 'default' | 'outline';\n size?: 'sm' | 'md' | 'lg';\n disabled?: boolean;\n /**\n * Hide the button if no Solana wallets are detected.\n * When true (default), button renders nothing if no wallets are installed.\n * When false, button always renders (useful for showing \"install wallet\" prompts).\n * @default true\n */\n hideIfNoWallet?: boolean;\n /**\n * Solana wallet adapter context. Pass this from @solana/wallet-adapter-react's useWallet().\n * The button will handle connection and signing automatically for a one-click experience.\n */\n walletContext?: {\n publicKey: { toBase58: () => string } | null;\n signMessage: ((message: Uint8Array) => Promise<Uint8Array>) | null;\n connected: boolean;\n connecting: boolean;\n connect: () => Promise<void>;\n wallet: { adapter: { name: string } } | null;\n select: (walletName: string) => void;\n wallets: Array<{\n adapter: {\n name: string;\n icon: string;\n readyState: string;\n };\n }>;\n };\n}\n\n/**\n * Solana wallet login button with one-click authentication.\n *\n * Handles wallet connection and message signing automatically.\n * If wallet is already connected, signs immediately.\n * If not connected, connects first then auto-signs.\n */\nexport function SolanaLoginButton({\n onSuccess,\n onError,\n className = '',\n variant = 'default',\n size = 'md',\n disabled = false,\n hideIfNoWallet = true,\n walletContext,\n}: SolanaLoginButtonProps) {\n const { requestChallenge, signIn, isLoading: isAuthLoading } = useSolanaAuth();\n const [showWalletSelector, setShowWalletSelector] = useState(false);\n const [pendingLogin, setPendingLogin] = useState(false);\n const [triggerConnect, setTriggerConnect] = useState(false);\n const isProcessingRef = useRef(false);\n\n // Detect wallets from browser (used when walletContext not provided)\n // Use lazy initializer to avoid re-detecting on every render\n const [browserHasWallets] = useState(() => detectSolanaWallets());\n\n const connected = walletContext?.connected ?? false;\n const connecting = walletContext?.connecting ?? false;\n const publicKey = walletContext?.publicKey;\n const signMessage = walletContext?.signMessage;\n const wallet = walletContext?.wallet;\n const wallets = walletContext?.wallets ?? [];\n\n // Get installed/ready wallets from wallet adapter context\n const installedWallets = wallets.filter(\n (w) => w.adapter.readyState === 'Installed' || w.adapter.readyState === 'Loadable'\n );\n\n // Determine if any wallets are available\n // If walletContext is provided, use its wallet list; otherwise use browser detection\n const hasWallets = walletContext ? installedWallets.length > 0 : browserHasWallets;\n\n // Execute the actual sign-in flow\n const executeSignIn = useCallback(async () => {\n if (isProcessingRef.current) return;\n if (!publicKey || !signMessage) {\n onError?.(new Error('Wallet not ready'));\n return;\n }\n\n isProcessingRef.current = true;\n try {\n const pubKeyString = publicKey.toBase58();\n\n // Request challenge from server\n const challenge = await requestChallenge(pubKeyString);\n\n // Sign the message with wallet\n const messageBytes = new TextEncoder().encode(challenge.message);\n const signatureBytes = await signMessage(messageBytes);\n\n // Validate signature\n if (!(signatureBytes instanceof Uint8Array) || signatureBytes.length === 0) {\n throw new Error('Wallet returned invalid signature');\n }\n\n // Encode signature\n let signature: string;\n try {\n signature = btoa(String.fromCharCode(...signatureBytes));\n } catch {\n throw new Error('Failed to encode signature');\n }\n\n // Authenticate with server\n await signIn(pubKeyString, signature, challenge.message);\n\n onSuccess?.();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\n } finally {\n isProcessingRef.current = false;\n setPendingLogin(false);\n }\n }, [publicKey, signMessage, requestChallenge, signIn, onSuccess, onError]);\n\n // Auto-connect when wallet is selected and triggerConnect is set\n useEffect(() => {\n if (triggerConnect && wallet && !connected && !connecting && walletContext?.connect) {\n setTriggerConnect(false);\n walletContext.connect().catch((err) => {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n setPendingLogin(false);\n });\n }\n }, [triggerConnect, wallet, connected, connecting, walletContext, onError]);\n\n // Auto-execute sign-in when connected with pending login\n // COMP-04: Catch unhandled rejections from executeSignIn (defensive - errors already\n // handled internally, but .catch() prevents unhandledrejection events)\n useEffect(() => {\n if (pendingLogin && connected && publicKey && signMessage && !isProcessingRef.current) {\n executeSignIn().catch(() => {\n /* Errors already passed to onError callback inside executeSignIn */\n });\n }\n }, [pendingLogin, connected, publicKey, signMessage, executeSignIn]);\n\n // Hide button if no wallets detected and hideIfNoWallet is true\n // Note: This must come after all hooks to satisfy React rules of hooks\n if (hideIfNoWallet && !hasWallets) {\n return null;\n }\n\n const handleClick = async () => {\n if (disabled || isAuthLoading || connecting) return;\n\n if (connected && publicKey && signMessage) {\n // Already connected - sign immediately\n setPendingLogin(true);\n await executeSignIn();\n } else if (wallet) {\n // Wallet selected but not connected - connect and queue login\n setPendingLogin(true);\n setTriggerConnect(true);\n } else if (installedWallets.length === 1) {\n // Only one wallet installed - auto-select it\n walletContext?.select(installedWallets[0].adapter.name);\n setPendingLogin(true);\n setTriggerConnect(true);\n } else if (installedWallets.length > 1) {\n // Multiple wallets - show selector\n setShowWalletSelector(true);\n } else {\n // No wallets - show error\n onError?.(\n new Error('No Solana wallet found. Please install Phantom or another Solana wallet.')\n );\n }\n };\n\n const handleSelectWallet = (walletName: string) => {\n setShowWalletSelector(false);\n walletContext?.select(walletName);\n setPendingLogin(true);\n setTriggerConnect(true);\n };\n\n const sizeClasses = {\n sm: 'cedros-button-sm',\n md: 'cedros-button-md',\n lg: 'cedros-button-lg',\n };\n\n const variantClasses = {\n default: 'cedros-button-social',\n outline: 'cedros-button-social-outline',\n };\n\n const isLoading = isAuthLoading || connecting || (pendingLogin && !connected);\n\n return (\n <>\n <button\n type=\"button\"\n className={`cedros-button ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}\n onClick={handleClick}\n disabled={disabled || isLoading}\n aria-label=\"Continue with Solana\"\n >\n {isLoading ? (\n <LoadingSpinner size=\"sm\" />\n ) : (\n <svg\n className=\"cedros-button-icon\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 128 128\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M25.38 96.04a4.35 4.35 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7l-17.71 17.72a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7l17.71-17.72z\" />\n <path d=\"M25.38 11.81a4.47 4.47 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7L103.96 31.96a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7L25.38 11.81z\" />\n <path d=\"M102.62 53.76a4.35 4.35 0 0 0-3.07-1.27H7.87c-1.93 0-2.9 2.34-1.54 3.7l17.71 17.72a4.35 4.35 0 0 0 3.07 1.27h91.68c1.93 0 2.9-2.34 1.54-3.7L102.62 53.76z\" />\n </svg>\n )}\n <span>Continue with Solana</span>\n </button>\n\n {/* Wallet Selector Modal */}\n {showWalletSelector && (\n <div\n className=\"cedros-modal-backdrop\"\n onClick={() => setShowWalletSelector(false)}\n role=\"presentation\"\n >\n <div\n className=\"cedros-modal cedros-wallet-selector\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"wallet-selector-title\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"cedros-modal-header\">\n <h2 id=\"wallet-selector-title\" className=\"cedros-modal-title\">\n Select Wallet\n </h2>\n <button\n type=\"button\"\n className=\"cedros-modal-close\"\n onClick={() => setShowWalletSelector(false)}\n aria-label=\"Close\"\n >\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M18 6L6 18M6 6l12 12\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n </div>\n <div className=\"cedros-modal-content\">\n <div className=\"cedros-wallet-list\">\n {installedWallets.map((w) => (\n <button\n key={w.adapter.name}\n type=\"button\"\n className=\"cedros-wallet-option\"\n onClick={() => handleSelectWallet(w.adapter.name)}\n >\n <img\n src={w.adapter.icon}\n alt=\"\"\n width=\"32\"\n height=\"32\"\n className=\"cedros-wallet-icon\"\n />\n <span>{w.adapter.name}</span>\n </button>\n ))}\n </div>\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n","/**\n * Mobile Wallet Adapter (MWA) registration for web.\n *\n * On Android Chrome, MWA lets users authenticate with their installed Solana\n * wallet app (e.g., Phantom, Solflare) via Android Intents — no browser\n * extension needed.\n *\n * Once registered, MWA appears as a wallet option in the wallet adapter's\n * wallet list (alongside browser extension wallets). Users see it as\n * \"Use Installed Wallet\" in the wallet selector.\n *\n * Requires the optional peer dependency: @solana-mobile/wallet-standard-mobile\n *\n * @see https://docs.solanamobile.com/get-started/web/installation\n */\n\nexport interface MobileWalletConfig {\n /** App name shown in the wallet's authorization dialog */\n name?: string;\n /** App URI for identity verification */\n uri?: string;\n /** App icon path/URL shown in the wallet dialog */\n icon?: string;\n /** Solana cluster(s) to support. Default: ['solana:mainnet'] */\n chains?: string[];\n}\n\n/**\n * Register Mobile Wallet Adapter as a wallet-standard wallet.\n *\n * Call this once at your application root (before rendering). After registration,\n * MWA automatically appears as \"Use Installed Wallet\" for users browsing on\n * Android Chrome with a Solana wallet app installed.\n *\n * Must be called in a non-SSR context (browser only). For Next.js, call in a\n * Client Component with `'use client'`.\n *\n * @example\n * ```tsx\n * import { registerMobileWallet, CedrosLoginProvider } from '@cedros/login-react';\n *\n * // Register before provider mounts\n * registerMobileWallet({ name: 'My App', uri: 'https://myapp.com' });\n *\n * function App() {\n * return (\n * <CedrosLoginProvider config={{ serverUrl: '...' }}>\n * <LoginForm />\n * </CedrosLoginProvider>\n * );\n * }\n * ```\n *\n * @returns true if registration succeeded, false if package not installed or SSR\n */\nexport function registerMobileWallet(config?: MobileWalletConfig): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n try {\n // Dynamic import to avoid bundling the optional peer dep\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mwa = require('@solana-mobile/wallet-standard-mobile');\n\n const chains = config?.chains ?? ['solana:mainnet'];\n\n const registrationConfig: Record<string, unknown> = {\n appIdentity: {\n name: config?.name,\n uri: config?.uri,\n icon: config?.icon,\n },\n chains,\n };\n\n // Use built-in defaults for optional config if available\n if (typeof mwa.createDefaultAuthorizationCache === 'function') {\n registrationConfig.authorizationCache = mwa.createDefaultAuthorizationCache();\n }\n if (typeof mwa.createDefaultChainSelector === 'function') {\n registrationConfig.chainSelector = mwa.createDefaultChainSelector();\n }\n if (typeof mwa.createDefaultWalletNotFoundHandler === 'function') {\n registrationConfig.onWalletNotFound = mwa.createDefaultWalletNotFoundHandler();\n }\n\n mwa.registerMwa(registrationConfig);\n return true;\n } catch {\n // @solana-mobile/wallet-standard-mobile not installed\n return false;\n }\n}\n"],"names":["useSolanaAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","useState","error","setError","apiClient","useMemo","ApiClient","requestChallenge","useCallback","publicKey","validateSolanaPublicKey","authError","err","handleApiError","signIn","signature","message","data","clearError","WALLET_PROVIDERS","isValidSolanaProvider","provider","wallet","hasWalletStandardWallets","walletStandard","wallets","detectSolanaWallets","win","walletObj","SolanaLoginButton","onSuccess","onError","className","variant","size","disabled","hideIfNoWallet","walletContext","isAuthLoading","showWalletSelector","setShowWalletSelector","pendingLogin","setPendingLogin","triggerConnect","setTriggerConnect","isProcessingRef","useRef","browserHasWallets","connected","connecting","signMessage","installedWallets","w","hasWallets","executeSignIn","pubKeyString","challenge","messageBytes","signatureBytes","useEffect","handleClick","handleSelectWallet","walletName","sizeClasses","variantClasses","jsxs","Fragment","jsx","LoadingSpinner","e","registerMobileWallet","mwa","chains","registrationConfig"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const n=require("react/jsx-runtime"),r=require("react"),W=require("./useCedrosLogin-C9MrcZvh.cjs"),P=require("./validation-BuGQrA-K.cjs"),R=require("./LoadingSpinner-d6sSxgQN.cjs");function q(){const{config:e,_internal:t}=W.useCedrosLogin(),[c,o]=r.useState(!1),[v,i]=r.useState(null),g=r.useMemo(()=>new W.ApiClient({baseUrl:e.serverUrl,timeoutMs:e.requestTimeout,retryAttempts:e.retryAttempts}),[e.serverUrl,e.requestTimeout,e.retryAttempts]),s=r.useCallback(async h=>{if(!P.validateSolanaPublicKey(h)){const u={code:"INVALID_PUBLIC_KEY",message:"Invalid Solana public key format"};throw i(u),u}o(!0),i(null);try{return await g.post("/solana/challenge",{publicKey:h},{credentials:"omit"})}catch(u){const d=W.handleApiError(u,"Failed to get challenge");throw i(d),d}finally{o(!1)}},[g]),S=r.useCallback(async(h,u,d)=>{if(!P.validateSolanaPublicKey(h)){const l={code:"INVALID_PUBLIC_KEY",message:"Invalid Solana public key format"};throw i(l),l}o(!0),i(null);try{const l=await g.post("/solana",{publicKey:h,signature:u,message:d});return e.callbacks?.onLoginSuccess?.(l.user,"solana"),t?.handleLoginSuccess(l.user,l.tokens),l}catch(l){const f=W.handleApiError(l,"Solana sign-in failed");throw i(f),f}finally{o(!1)}},[g,e.callbacks,t]),b=r.useCallback(()=>i(null),[]);return{requestChallenge:s,signIn:S,isLoading:c,error:v,clearError:b}}const V=["phantom","solflare","backpack","glow","slope","sollet","coin98","clover","mathWallet","ledger","torus","walletconnect"];function _(e){if(!e||typeof e!="object")return!1;const t=e;return typeof t.connect=="function"||typeof t.signMessage=="function"||typeof t.signTransaction=="function"||"isConnected"in t}function $(){try{const e=window.__wallet_standard__;if(e&&typeof e=="object"&&"get"in e&&typeof e.get=="function"){const t=e.get();return Array.isArray(t)&&t.length>0}}catch{}return!1}function B(){if(typeof window>"u")return!1;const e=window;for(const t of V){const c=e[t];if(c&&typeof c=="object"&&"solana"in c&&_(c.solana))return!0}return!!(_(e.solana)||$())}function O({onSuccess:e,onError:t,className:c="",variant:o="default",size:v="md",disabled:i=!1,hideIfNoWallet:g=!0,walletContext:s}){const{requestChallenge:S,signIn:b,isLoading:h}=q(),[u,d]=r.useState(!1),[l,f]=r.useState(!1),[I,L]=r.useState(!1),j=r.useRef(!1),[D]=r.useState(()=>B()),m=s?.connected??!1,C=s?.connecting??!1,p=s?.publicKey,w=s?.signMessage,E=s?.wallet,y=(s?.wallets??[]).filter(a=>a.adapter.readyState==="Installed"||a.adapter.readyState==="Loadable"),z=s?y.length>0:D,A=r.useCallback(async()=>{if(!j.current){if(!p||!w){t?.(new Error("Wallet not ready"));return}j.current=!0;try{const a=p.toBase58(),k=await S(a),K=new TextEncoder().encode(k.message),N=await w(K);if(!(N instanceof Uint8Array)||N.length===0)throw new Error("Wallet returned invalid signature");let M;try{M=btoa(String.fromCharCode(...N))}catch{throw new Error("Failed to encode signature")}await b(a,M,k.message),e?.()}catch(a){const k=a instanceof Error?a:new Error(String(a));t?.(k)}finally{j.current=!1,f(!1)}}},[p,w,S,b,e,t]);if(r.useEffect(()=>{I&&E&&!m&&!C&&s?.connect&&(L(!1),s.connect().catch(a=>{t?.(a instanceof Error?a:new Error(String(a))),f(!1)}))},[I,E,m,C,s,t]),r.useEffect(()=>{l&&m&&p&&w&&!j.current&&A().catch(()=>{})},[l,m,p,w,A]),g&&!z)return null;const F=async()=>{i||h||C||(m&&p&&w?(f(!0),await A()):E?(f(!0),L(!0)):y.length===1?(s?.select(y[0].adapter.name),f(!0),L(!0)):y.length>1?d(!0):t?.(new Error("No Solana wallet found. Please install Phantom or another Solana wallet.")))},H=a=>{d(!1),s?.select(a),f(!0),L(!0)},T={sm:"cedros-button-sm",md:"cedros-button-md",lg:"cedros-button-lg"},U={default:"cedros-button-
|
|
1
|
+
"use strict";const n=require("react/jsx-runtime"),r=require("react"),W=require("./useCedrosLogin-C9MrcZvh.cjs"),P=require("./validation-BuGQrA-K.cjs"),R=require("./LoadingSpinner-d6sSxgQN.cjs");function q(){const{config:e,_internal:t}=W.useCedrosLogin(),[c,o]=r.useState(!1),[v,i]=r.useState(null),g=r.useMemo(()=>new W.ApiClient({baseUrl:e.serverUrl,timeoutMs:e.requestTimeout,retryAttempts:e.retryAttempts}),[e.serverUrl,e.requestTimeout,e.retryAttempts]),s=r.useCallback(async h=>{if(!P.validateSolanaPublicKey(h)){const u={code:"INVALID_PUBLIC_KEY",message:"Invalid Solana public key format"};throw i(u),u}o(!0),i(null);try{return await g.post("/solana/challenge",{publicKey:h},{credentials:"omit"})}catch(u){const d=W.handleApiError(u,"Failed to get challenge");throw i(d),d}finally{o(!1)}},[g]),S=r.useCallback(async(h,u,d)=>{if(!P.validateSolanaPublicKey(h)){const l={code:"INVALID_PUBLIC_KEY",message:"Invalid Solana public key format"};throw i(l),l}o(!0),i(null);try{const l=await g.post("/solana",{publicKey:h,signature:u,message:d});return e.callbacks?.onLoginSuccess?.(l.user,"solana"),t?.handleLoginSuccess(l.user,l.tokens),l}catch(l){const f=W.handleApiError(l,"Solana sign-in failed");throw i(f),f}finally{o(!1)}},[g,e.callbacks,t]),b=r.useCallback(()=>i(null),[]);return{requestChallenge:s,signIn:S,isLoading:c,error:v,clearError:b}}const V=["phantom","solflare","backpack","glow","slope","sollet","coin98","clover","mathWallet","ledger","torus","walletconnect"];function _(e){if(!e||typeof e!="object")return!1;const t=e;return typeof t.connect=="function"||typeof t.signMessage=="function"||typeof t.signTransaction=="function"||"isConnected"in t}function $(){try{const e=window.__wallet_standard__;if(e&&typeof e=="object"&&"get"in e&&typeof e.get=="function"){const t=e.get();return Array.isArray(t)&&t.length>0}}catch{}return!1}function B(){if(typeof window>"u")return!1;const e=window;for(const t of V){const c=e[t];if(c&&typeof c=="object"&&"solana"in c&&_(c.solana))return!0}return!!(_(e.solana)||$())}function O({onSuccess:e,onError:t,className:c="",variant:o="default",size:v="md",disabled:i=!1,hideIfNoWallet:g=!0,walletContext:s}){const{requestChallenge:S,signIn:b,isLoading:h}=q(),[u,d]=r.useState(!1),[l,f]=r.useState(!1),[I,L]=r.useState(!1),j=r.useRef(!1),[D]=r.useState(()=>B()),m=s?.connected??!1,C=s?.connecting??!1,p=s?.publicKey,w=s?.signMessage,E=s?.wallet,y=(s?.wallets??[]).filter(a=>a.adapter.readyState==="Installed"||a.adapter.readyState==="Loadable"),z=s?y.length>0:D,A=r.useCallback(async()=>{if(!j.current){if(!p||!w){t?.(new Error("Wallet not ready"));return}j.current=!0;try{const a=p.toBase58(),k=await S(a),K=new TextEncoder().encode(k.message),N=await w(K);if(!(N instanceof Uint8Array)||N.length===0)throw new Error("Wallet returned invalid signature");let M;try{M=btoa(String.fromCharCode(...N))}catch{throw new Error("Failed to encode signature")}await b(a,M,k.message),e?.()}catch(a){const k=a instanceof Error?a:new Error(String(a));t?.(k)}finally{j.current=!1,f(!1)}}},[p,w,S,b,e,t]);if(r.useEffect(()=>{I&&E&&!m&&!C&&s?.connect&&(L(!1),s.connect().catch(a=>{t?.(a instanceof Error?a:new Error(String(a))),f(!1)}))},[I,E,m,C,s,t]),r.useEffect(()=>{l&&m&&p&&w&&!j.current&&A().catch(()=>{})},[l,m,p,w,A]),g&&!z)return null;const F=async()=>{i||h||C||(m&&p&&w?(f(!0),await A()):E?(f(!0),L(!0)):y.length===1?(s?.select(y[0].adapter.name),f(!0),L(!0)):y.length>1?d(!0):t?.(new Error("No Solana wallet found. Please install Phantom or another Solana wallet.")))},H=a=>{d(!1),s?.select(a),f(!0),L(!0)},T={sm:"cedros-button-sm",md:"cedros-button-md",lg:"cedros-button-lg"},U={default:"cedros-button-social",outline:"cedros-button-social-outline"},x=h||C||l&&!m;return n.jsxs(n.Fragment,{children:[n.jsxs("button",{type:"button",className:`cedros-button ${U[o]} ${T[v]} ${c}`,onClick:F,disabled:i||x,"aria-label":"Continue with Solana",children:[x?n.jsx(R.LoadingSpinner,{size:"sm"}):n.jsxs("svg",{className:"cedros-button-icon",width:"18",height:"18",viewBox:"0 0 128 128",fill:"currentColor","aria-hidden":"true",children:[n.jsx("path",{d:"M25.38 96.04a4.35 4.35 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7l-17.71 17.72a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7l17.71-17.72z"}),n.jsx("path",{d:"M25.38 11.81a4.47 4.47 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7L103.96 31.96a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7L25.38 11.81z"}),n.jsx("path",{d:"M102.62 53.76a4.35 4.35 0 0 0-3.07-1.27H7.87c-1.93 0-2.9 2.34-1.54 3.7l17.71 17.72a4.35 4.35 0 0 0 3.07 1.27h91.68c1.93 0 2.9-2.34 1.54-3.7L102.62 53.76z"})]}),n.jsx("span",{children:"Continue with Solana"})]}),u&&n.jsx("div",{className:"cedros-modal-backdrop",onClick:()=>d(!1),role:"presentation",children:n.jsxs("div",{className:"cedros-modal cedros-wallet-selector",role:"dialog","aria-modal":"true","aria-labelledby":"wallet-selector-title",onClick:a=>a.stopPropagation(),children:[n.jsxs("div",{className:"cedros-modal-header",children:[n.jsx("h2",{id:"wallet-selector-title",className:"cedros-modal-title",children:"Select Wallet"}),n.jsx("button",{type:"button",className:"cedros-modal-close",onClick:()=>d(!1),"aria-label":"Close",children:n.jsx("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none","aria-hidden":"true",children:n.jsx("path",{d:"M18 6L6 18M6 6l12 12",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round"})})})]}),n.jsx("div",{className:"cedros-modal-content",children:n.jsx("div",{className:"cedros-wallet-list",children:y.map(a=>n.jsxs("button",{type:"button",className:"cedros-wallet-option",onClick:()=>H(a.adapter.name),children:[n.jsx("img",{src:a.adapter.icon,alt:"",width:"32",height:"32",className:"cedros-wallet-icon"}),n.jsx("span",{children:a.adapter.name})]},a.adapter.name))})})]})})]})}function Y(e){if(typeof window>"u")return!1;try{const t=require("@solana-mobile/wallet-standard-mobile"),c=e?.chains??["solana:mainnet"],o={appIdentity:{name:e?.name,uri:e?.uri,icon:e?.icon},chains:c};return typeof t.createDefaultAuthorizationCache=="function"&&(o.authorizationCache=t.createDefaultAuthorizationCache()),typeof t.createDefaultChainSelector=="function"&&(o.chainSelector=t.createDefaultChainSelector()),typeof t.createDefaultWalletNotFoundHandler=="function"&&(o.onWalletNotFound=t.createDefaultWalletNotFoundHandler()),t.registerMwa(o),!0}catch{return!1}}exports.SolanaLoginButton=O;exports.detectSolanaWallets=B;exports.registerMobileWallet=Y;exports.useSolanaAuth=q;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobileWalletAdapter-Dp4yFxCm.cjs","sources":["../src/hooks/useSolanaAuth.ts","../src/utils/walletDetection.ts","../src/components/solana/SolanaLoginButton.tsx","../src/utils/mobileWalletAdapter.ts"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport { validateSolanaPublicKey } from '../utils/validation';\nimport type { AuthResponse, AuthError, ChallengeResponse } from '../types';\n\nexport interface UseSolanaAuthReturn {\n requestChallenge: (publicKey: string) => Promise<ChallengeResponse>;\n signIn: (publicKey: string, signature: string, message: string) => Promise<AuthResponse>;\n isLoading: boolean;\n error: AuthError | null;\n clearError: () => void;\n}\n\n/**\n * Hook for Solana wallet authentication.\n *\n * @example\n * ```tsx\n * function SolanaLogin() {\n * const { requestChallenge, signIn, isLoading } = useSolanaAuth();\n * const { publicKey, signMessage } = useWallet();\n *\n * const handleLogin = async () => {\n * const challenge = await requestChallenge(publicKey.toBase58());\n * const signature = await signMessage(new TextEncoder().encode(challenge.message));\n * const result = await signIn(\n * publicKey.toBase58(),\n * Buffer.from(signature).toString('base64'),\n * challenge.message\n * );\n * };\n * }\n * ```\n */\nexport function useSolanaAuth(): UseSolanaAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n const requestChallenge = useCallback(\n async (publicKey: string): Promise<ChallengeResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<ChallengeResponse>(\n '/solana/challenge',\n { publicKey },\n { credentials: 'omit' }\n );\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Failed to get challenge');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient]\n );\n\n const signIn = useCallback(\n async (publicKey: string, signature: string, message: string): Promise<AuthResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<AuthResponse>('/solana', {\n publicKey,\n signature,\n message,\n });\n config.callbacks?.onLoginSuccess?.(data.user, 'solana');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Solana sign-in failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, config.callbacks, _internal]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n return {\n requestChallenge,\n signIn,\n isLoading,\n error,\n clearError,\n };\n}\n","/**\n * Wallet detection utilities for Solana browser wallets\n */\n\n/**\n * Type for window with potential Solana wallet extensions\n */\ninterface WindowWithWallets extends Window {\n phantom?: { solana?: unknown };\n solflare?: { solana?: unknown };\n backpack?: { solana?: unknown };\n glow?: { solana?: unknown };\n slope?: { solana?: unknown };\n sollet?: { solana?: unknown };\n coin98?: { solana?: unknown };\n clover?: { solana?: unknown };\n mathWallet?: { solana?: unknown };\n ledger?: { solana?: unknown };\n torus?: { solana?: unknown };\n walletconnect?: { solana?: unknown };\n solana?: unknown;\n}\n\n/**\n * Known Solana wallet provider names to check for on the window object\n */\ntype WalletProviderName = Exclude<keyof WindowWithWallets, keyof Window>;\n\nconst WALLET_PROVIDERS: WalletProviderName[] = [\n 'phantom',\n 'solflare',\n 'backpack',\n 'glow',\n 'slope',\n 'sollet',\n 'coin98',\n 'clover',\n 'mathWallet',\n 'ledger',\n 'torus',\n 'walletconnect',\n];\n\n/**\n * UI-9 FIX: Validates that a wallet provider object has expected Solana wallet methods.\n * Prevents spoofed wallet objects from being accepted.\n */\nfunction isValidSolanaProvider(provider: unknown): boolean {\n if (!provider || typeof provider !== 'object') return false;\n const wallet = provider as Record<string, unknown>;\n // Check for at least one expected wallet method/property\n // Real wallets have connect, signMessage, signTransaction, etc.\n return (\n typeof wallet.connect === 'function' ||\n typeof wallet.signMessage === 'function' ||\n typeof wallet.signTransaction === 'function' ||\n 'isConnected' in wallet\n );\n}\n\n/**\n * Check for wallets registered via the Wallet Standard protocol.\n * MWA (via @solana-mobile/wallet-standard-mobile) registers this way.\n */\nfunction hasWalletStandardWallets(): boolean {\n try {\n // The wallet-standard protocol exposes registered wallets via a global\n const walletStandard = (window as unknown as Record<string, unknown>)['__wallet_standard__'];\n if (\n walletStandard &&\n typeof walletStandard === 'object' &&\n 'get' in walletStandard &&\n typeof (walletStandard as Record<string, unknown>).get === 'function'\n ) {\n const wallets = (walletStandard as { get: () => unknown[] }).get();\n return Array.isArray(wallets) && wallets.length > 0;\n }\n } catch {\n // Not available\n }\n return false;\n}\n\n/**\n * Detects if any Solana wallet extensions are installed in the browser.\n * Checks for common wallet adapters like Phantom, Solflare, Backpack, etc.\n *\n * @returns true if at least one Solana wallet is detected\n *\n * @example\n * ```tsx\n * if (detectSolanaWallets()) {\n * // Show Solana login button\n * }\n * ```\n */\nexport function detectSolanaWallets(): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n const win = window as WindowWithWallets;\n\n // Check window object for wallet injections\n // UI-9: Validate wallet has expected methods to reject spoofed providers\n for (const provider of WALLET_PROVIDERS) {\n const walletObj = win[provider];\n if (\n walletObj &&\n typeof walletObj === 'object' &&\n 'solana' in walletObj &&\n isValidSolanaProvider(walletObj.solana)\n ) {\n return true;\n }\n }\n\n // Check for generic Solana provider (e.g., from some mobile wallet browsers)\n // UI-9: Also validate generic provider\n if (isValidSolanaProvider(win.solana)) {\n return true;\n }\n\n // Check for wallet-standard registered wallets (e.g., MWA via registerMwa())\n if (hasWalletStandardWallets()) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Returns list of detected Solana wallet names (for debugging/display)\n *\n * @returns Array of detected wallet provider names\n */\nexport function getDetectedWalletNames(): string[] {\n if (typeof window === 'undefined') {\n return [];\n }\n\n const win = window as WindowWithWallets;\n const detected: string[] = [];\n\n // UI-9: Use validation to reject spoofed providers\n for (const provider of WALLET_PROVIDERS) {\n const walletObj = win[provider];\n if (\n walletObj &&\n typeof walletObj === 'object' &&\n 'solana' in walletObj &&\n isValidSolanaProvider(walletObj.solana)\n ) {\n detected.push(provider);\n }\n }\n\n if (isValidSolanaProvider(win.solana) && detected.length === 0) {\n detected.push('solana');\n }\n\n return detected;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { useSolanaAuth } from '../../hooks/useSolanaAuth';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\nimport { detectSolanaWallets } from '../../utils/walletDetection';\n\nexport interface SolanaLoginButtonProps {\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n className?: string;\n variant?: 'default' | 'outline';\n size?: 'sm' | 'md' | 'lg';\n disabled?: boolean;\n /**\n * Hide the button if no Solana wallets are detected.\n * When true (default), button renders nothing if no wallets are installed.\n * When false, button always renders (useful for showing \"install wallet\" prompts).\n * @default true\n */\n hideIfNoWallet?: boolean;\n /**\n * Solana wallet adapter context. Pass this from @solana/wallet-adapter-react's useWallet().\n * The button will handle connection and signing automatically for a one-click experience.\n */\n walletContext?: {\n publicKey: { toBase58: () => string } | null;\n signMessage: ((message: Uint8Array) => Promise<Uint8Array>) | null;\n connected: boolean;\n connecting: boolean;\n connect: () => Promise<void>;\n wallet: { adapter: { name: string } } | null;\n select: (walletName: string) => void;\n wallets: Array<{\n adapter: {\n name: string;\n icon: string;\n readyState: string;\n };\n }>;\n };\n}\n\n/**\n * Solana wallet login button with one-click authentication.\n *\n * Handles wallet connection and message signing automatically.\n * If wallet is already connected, signs immediately.\n * If not connected, connects first then auto-signs.\n */\nexport function SolanaLoginButton({\n onSuccess,\n onError,\n className = '',\n variant = 'default',\n size = 'md',\n disabled = false,\n hideIfNoWallet = true,\n walletContext,\n}: SolanaLoginButtonProps) {\n const { requestChallenge, signIn, isLoading: isAuthLoading } = useSolanaAuth();\n const [showWalletSelector, setShowWalletSelector] = useState(false);\n const [pendingLogin, setPendingLogin] = useState(false);\n const [triggerConnect, setTriggerConnect] = useState(false);\n const isProcessingRef = useRef(false);\n\n // Detect wallets from browser (used when walletContext not provided)\n // Use lazy initializer to avoid re-detecting on every render\n const [browserHasWallets] = useState(() => detectSolanaWallets());\n\n const connected = walletContext?.connected ?? false;\n const connecting = walletContext?.connecting ?? false;\n const publicKey = walletContext?.publicKey;\n const signMessage = walletContext?.signMessage;\n const wallet = walletContext?.wallet;\n const wallets = walletContext?.wallets ?? [];\n\n // Get installed/ready wallets from wallet adapter context\n const installedWallets = wallets.filter(\n (w) => w.adapter.readyState === 'Installed' || w.adapter.readyState === 'Loadable'\n );\n\n // Determine if any wallets are available\n // If walletContext is provided, use its wallet list; otherwise use browser detection\n const hasWallets = walletContext ? installedWallets.length > 0 : browserHasWallets;\n\n // Execute the actual sign-in flow\n const executeSignIn = useCallback(async () => {\n if (isProcessingRef.current) return;\n if (!publicKey || !signMessage) {\n onError?.(new Error('Wallet not ready'));\n return;\n }\n\n isProcessingRef.current = true;\n try {\n const pubKeyString = publicKey.toBase58();\n\n // Request challenge from server\n const challenge = await requestChallenge(pubKeyString);\n\n // Sign the message with wallet\n const messageBytes = new TextEncoder().encode(challenge.message);\n const signatureBytes = await signMessage(messageBytes);\n\n // Validate signature\n if (!(signatureBytes instanceof Uint8Array) || signatureBytes.length === 0) {\n throw new Error('Wallet returned invalid signature');\n }\n\n // Encode signature\n let signature: string;\n try {\n signature = btoa(String.fromCharCode(...signatureBytes));\n } catch {\n throw new Error('Failed to encode signature');\n }\n\n // Authenticate with server\n await signIn(pubKeyString, signature, challenge.message);\n\n onSuccess?.();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\n } finally {\n isProcessingRef.current = false;\n setPendingLogin(false);\n }\n }, [publicKey, signMessage, requestChallenge, signIn, onSuccess, onError]);\n\n // Auto-connect when wallet is selected and triggerConnect is set\n useEffect(() => {\n if (triggerConnect && wallet && !connected && !connecting && walletContext?.connect) {\n setTriggerConnect(false);\n walletContext.connect().catch((err) => {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n setPendingLogin(false);\n });\n }\n }, [triggerConnect, wallet, connected, connecting, walletContext, onError]);\n\n // Auto-execute sign-in when connected with pending login\n // COMP-04: Catch unhandled rejections from executeSignIn (defensive - errors already\n // handled internally, but .catch() prevents unhandledrejection events)\n useEffect(() => {\n if (pendingLogin && connected && publicKey && signMessage && !isProcessingRef.current) {\n executeSignIn().catch(() => {\n /* Errors already passed to onError callback inside executeSignIn */\n });\n }\n }, [pendingLogin, connected, publicKey, signMessage, executeSignIn]);\n\n // Hide button if no wallets detected and hideIfNoWallet is true\n // Note: This must come after all hooks to satisfy React rules of hooks\n if (hideIfNoWallet && !hasWallets) {\n return null;\n }\n\n const handleClick = async () => {\n if (disabled || isAuthLoading || connecting) return;\n\n if (connected && publicKey && signMessage) {\n // Already connected - sign immediately\n setPendingLogin(true);\n await executeSignIn();\n } else if (wallet) {\n // Wallet selected but not connected - connect and queue login\n setPendingLogin(true);\n setTriggerConnect(true);\n } else if (installedWallets.length === 1) {\n // Only one wallet installed - auto-select it\n walletContext?.select(installedWallets[0].adapter.name);\n setPendingLogin(true);\n setTriggerConnect(true);\n } else if (installedWallets.length > 1) {\n // Multiple wallets - show selector\n setShowWalletSelector(true);\n } else {\n // No wallets - show error\n onError?.(\n new Error('No Solana wallet found. Please install Phantom or another Solana wallet.')\n );\n }\n };\n\n const handleSelectWallet = (walletName: string) => {\n setShowWalletSelector(false);\n walletContext?.select(walletName);\n setPendingLogin(true);\n setTriggerConnect(true);\n };\n\n const sizeClasses = {\n sm: 'cedros-button-sm',\n md: 'cedros-button-md',\n lg: 'cedros-button-lg',\n };\n\n const variantClasses = {\n default: 'cedros-button-solana',\n outline: 'cedros-button-solana-outline',\n };\n\n const isLoading = isAuthLoading || connecting || (pendingLogin && !connected);\n\n return (\n <>\n <button\n type=\"button\"\n className={`cedros-button ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}\n onClick={handleClick}\n disabled={disabled || isLoading}\n aria-label=\"Continue with Solana\"\n >\n {isLoading ? (\n <LoadingSpinner size=\"sm\" />\n ) : (\n <svg\n className=\"cedros-button-icon\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 128 128\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M25.38 96.04a4.35 4.35 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7l-17.71 17.72a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7l17.71-17.72z\" />\n <path d=\"M25.38 11.81a4.47 4.47 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7L103.96 31.96a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7L25.38 11.81z\" />\n <path d=\"M102.62 53.76a4.35 4.35 0 0 0-3.07-1.27H7.87c-1.93 0-2.9 2.34-1.54 3.7l17.71 17.72a4.35 4.35 0 0 0 3.07 1.27h91.68c1.93 0 2.9-2.34 1.54-3.7L102.62 53.76z\" />\n </svg>\n )}\n <span>Continue with Solana</span>\n </button>\n\n {/* Wallet Selector Modal */}\n {showWalletSelector && (\n <div\n className=\"cedros-modal-backdrop\"\n onClick={() => setShowWalletSelector(false)}\n role=\"presentation\"\n >\n <div\n className=\"cedros-modal cedros-wallet-selector\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"wallet-selector-title\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"cedros-modal-header\">\n <h2 id=\"wallet-selector-title\" className=\"cedros-modal-title\">\n Select Wallet\n </h2>\n <button\n type=\"button\"\n className=\"cedros-modal-close\"\n onClick={() => setShowWalletSelector(false)}\n aria-label=\"Close\"\n >\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M18 6L6 18M6 6l12 12\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n </div>\n <div className=\"cedros-modal-content\">\n <div className=\"cedros-wallet-list\">\n {installedWallets.map((w) => (\n <button\n key={w.adapter.name}\n type=\"button\"\n className=\"cedros-wallet-option\"\n onClick={() => handleSelectWallet(w.adapter.name)}\n >\n <img\n src={w.adapter.icon}\n alt=\"\"\n width=\"32\"\n height=\"32\"\n className=\"cedros-wallet-icon\"\n />\n <span>{w.adapter.name}</span>\n </button>\n ))}\n </div>\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n","/**\n * Mobile Wallet Adapter (MWA) registration for web.\n *\n * On Android Chrome, MWA lets users authenticate with their installed Solana\n * wallet app (e.g., Phantom, Solflare) via Android Intents — no browser\n * extension needed.\n *\n * Once registered, MWA appears as a wallet option in the wallet adapter's\n * wallet list (alongside browser extension wallets). Users see it as\n * \"Use Installed Wallet\" in the wallet selector.\n *\n * Requires the optional peer dependency: @solana-mobile/wallet-standard-mobile\n *\n * @see https://docs.solanamobile.com/get-started/web/installation\n */\n\nexport interface MobileWalletConfig {\n /** App name shown in the wallet's authorization dialog */\n name?: string;\n /** App URI for identity verification */\n uri?: string;\n /** App icon path/URL shown in the wallet dialog */\n icon?: string;\n /** Solana cluster(s) to support. Default: ['solana:mainnet'] */\n chains?: string[];\n}\n\n/**\n * Register Mobile Wallet Adapter as a wallet-standard wallet.\n *\n * Call this once at your application root (before rendering). After registration,\n * MWA automatically appears as \"Use Installed Wallet\" for users browsing on\n * Android Chrome with a Solana wallet app installed.\n *\n * Must be called in a non-SSR context (browser only). For Next.js, call in a\n * Client Component with `'use client'`.\n *\n * @example\n * ```tsx\n * import { registerMobileWallet, CedrosLoginProvider } from '@cedros/login-react';\n *\n * // Register before provider mounts\n * registerMobileWallet({ name: 'My App', uri: 'https://myapp.com' });\n *\n * function App() {\n * return (\n * <CedrosLoginProvider config={{ serverUrl: '...' }}>\n * <LoginForm />\n * </CedrosLoginProvider>\n * );\n * }\n * ```\n *\n * @returns true if registration succeeded, false if package not installed or SSR\n */\nexport function registerMobileWallet(config?: MobileWalletConfig): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n try {\n // Dynamic import to avoid bundling the optional peer dep\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mwa = require('@solana-mobile/wallet-standard-mobile');\n\n const chains = config?.chains ?? ['solana:mainnet'];\n\n const registrationConfig: Record<string, unknown> = {\n appIdentity: {\n name: config?.name,\n uri: config?.uri,\n icon: config?.icon,\n },\n chains,\n };\n\n // Use built-in defaults for optional config if available\n if (typeof mwa.createDefaultAuthorizationCache === 'function') {\n registrationConfig.authorizationCache = mwa.createDefaultAuthorizationCache();\n }\n if (typeof mwa.createDefaultChainSelector === 'function') {\n registrationConfig.chainSelector = mwa.createDefaultChainSelector();\n }\n if (typeof mwa.createDefaultWalletNotFoundHandler === 'function') {\n registrationConfig.onWalletNotFound = mwa.createDefaultWalletNotFoundHandler();\n }\n\n mwa.registerMwa(registrationConfig);\n return true;\n } catch {\n // @solana-mobile/wallet-standard-mobile not installed\n return false;\n }\n}\n"],"names":["useSolanaAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","useState","error","setError","apiClient","useMemo","ApiClient","requestChallenge","useCallback","publicKey","validateSolanaPublicKey","authError","err","handleApiError","signIn","signature","message","data","clearError","WALLET_PROVIDERS","isValidSolanaProvider","provider","wallet","hasWalletStandardWallets","walletStandard","wallets","detectSolanaWallets","win","walletObj","SolanaLoginButton","onSuccess","onError","className","variant","size","disabled","hideIfNoWallet","walletContext","isAuthLoading","showWalletSelector","setShowWalletSelector","pendingLogin","setPendingLogin","triggerConnect","setTriggerConnect","isProcessingRef","useRef","browserHasWallets","connected","connecting","signMessage","installedWallets","w","hasWallets","executeSignIn","pubKeyString","challenge","messageBytes","signatureBytes","useEffect","handleClick","handleSelectWallet","walletName","sizeClasses","variantClasses","jsxs","Fragment","jsx","LoadingSpinner","e","registerMobileWallet","mwa","chains","registrationConfig"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mobileWalletAdapter-DOdmFAk0.cjs","sources":["../src/hooks/useSolanaAuth.ts","../src/utils/walletDetection.ts","../src/components/solana/SolanaLoginButton.tsx","../src/utils/mobileWalletAdapter.ts"],"sourcesContent":["import { useState, useCallback, useMemo } from 'react';\nimport { useCedrosLogin } from '../context/useCedrosLogin';\nimport { ApiClient, handleApiError } from '../utils/apiClient';\nimport { validateSolanaPublicKey } from '../utils/validation';\nimport type { AuthResponse, AuthError, ChallengeResponse } from '../types';\n\nexport interface UseSolanaAuthReturn {\n requestChallenge: (publicKey: string) => Promise<ChallengeResponse>;\n signIn: (publicKey: string, signature: string, message: string) => Promise<AuthResponse>;\n isLoading: boolean;\n error: AuthError | null;\n clearError: () => void;\n}\n\n/**\n * Hook for Solana wallet authentication.\n *\n * @example\n * ```tsx\n * function SolanaLogin() {\n * const { requestChallenge, signIn, isLoading } = useSolanaAuth();\n * const { publicKey, signMessage } = useWallet();\n *\n * const handleLogin = async () => {\n * const challenge = await requestChallenge(publicKey.toBase58());\n * const signature = await signMessage(new TextEncoder().encode(challenge.message));\n * const result = await signIn(\n * publicKey.toBase58(),\n * Buffer.from(signature).toString('base64'),\n * challenge.message\n * );\n * };\n * }\n * ```\n */\nexport function useSolanaAuth(): UseSolanaAuthReturn {\n const { config, _internal } = useCedrosLogin();\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<AuthError | null>(null);\n\n const apiClient = useMemo(\n () =>\n new ApiClient({\n baseUrl: config.serverUrl,\n timeoutMs: config.requestTimeout,\n retryAttempts: config.retryAttempts,\n }),\n [config.serverUrl, config.requestTimeout, config.retryAttempts]\n );\n\n const requestChallenge = useCallback(\n async (publicKey: string): Promise<ChallengeResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<ChallengeResponse>(\n '/solana/challenge',\n { publicKey },\n { credentials: 'omit' }\n );\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Failed to get challenge');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient]\n );\n\n const signIn = useCallback(\n async (publicKey: string, signature: string, message: string): Promise<AuthResponse> => {\n // Validate public key format before making API call\n if (!validateSolanaPublicKey(publicKey)) {\n const authError: AuthError = {\n code: 'INVALID_PUBLIC_KEY',\n message: 'Invalid Solana public key format',\n };\n setError(authError);\n throw authError;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const data = await apiClient.post<AuthResponse>('/solana', {\n publicKey,\n signature,\n message,\n });\n config.callbacks?.onLoginSuccess?.(data.user, 'solana');\n _internal?.handleLoginSuccess(data.user, data.tokens);\n return data;\n } catch (err) {\n const authError = handleApiError(err, 'Solana sign-in failed');\n setError(authError);\n throw authError;\n } finally {\n setIsLoading(false);\n }\n },\n [apiClient, config.callbacks, _internal]\n );\n\n const clearError = useCallback(() => setError(null), []);\n\n return {\n requestChallenge,\n signIn,\n isLoading,\n error,\n clearError,\n };\n}\n","/**\n * Wallet detection utilities for Solana browser wallets\n */\n\n/**\n * Type for window with potential Solana wallet extensions\n */\ninterface WindowWithWallets extends Window {\n phantom?: { solana?: unknown };\n solflare?: { solana?: unknown };\n backpack?: { solana?: unknown };\n glow?: { solana?: unknown };\n slope?: { solana?: unknown };\n sollet?: { solana?: unknown };\n coin98?: { solana?: unknown };\n clover?: { solana?: unknown };\n mathWallet?: { solana?: unknown };\n ledger?: { solana?: unknown };\n torus?: { solana?: unknown };\n walletconnect?: { solana?: unknown };\n solana?: unknown;\n}\n\n/**\n * Known Solana wallet provider names to check for on the window object\n */\ntype WalletProviderName = Exclude<keyof WindowWithWallets, keyof Window>;\n\nconst WALLET_PROVIDERS: WalletProviderName[] = [\n 'phantom',\n 'solflare',\n 'backpack',\n 'glow',\n 'slope',\n 'sollet',\n 'coin98',\n 'clover',\n 'mathWallet',\n 'ledger',\n 'torus',\n 'walletconnect',\n];\n\n/**\n * UI-9 FIX: Validates that a wallet provider object has expected Solana wallet methods.\n * Prevents spoofed wallet objects from being accepted.\n */\nfunction isValidSolanaProvider(provider: unknown): boolean {\n if (!provider || typeof provider !== 'object') return false;\n const wallet = provider as Record<string, unknown>;\n // Check for at least one expected wallet method/property\n // Real wallets have connect, signMessage, signTransaction, etc.\n return (\n typeof wallet.connect === 'function' ||\n typeof wallet.signMessage === 'function' ||\n typeof wallet.signTransaction === 'function' ||\n 'isConnected' in wallet\n );\n}\n\n/**\n * Check for wallets registered via the Wallet Standard protocol.\n * MWA (via @solana-mobile/wallet-standard-mobile) registers this way.\n */\nfunction hasWalletStandardWallets(): boolean {\n try {\n // The wallet-standard protocol exposes registered wallets via a global\n const walletStandard = (window as unknown as Record<string, unknown>)['__wallet_standard__'];\n if (\n walletStandard &&\n typeof walletStandard === 'object' &&\n 'get' in walletStandard &&\n typeof (walletStandard as Record<string, unknown>).get === 'function'\n ) {\n const wallets = (walletStandard as { get: () => unknown[] }).get();\n return Array.isArray(wallets) && wallets.length > 0;\n }\n } catch {\n // Not available\n }\n return false;\n}\n\n/**\n * Detects if any Solana wallet extensions are installed in the browser.\n * Checks for common wallet adapters like Phantom, Solflare, Backpack, etc.\n *\n * @returns true if at least one Solana wallet is detected\n *\n * @example\n * ```tsx\n * if (detectSolanaWallets()) {\n * // Show Solana login button\n * }\n * ```\n */\nexport function detectSolanaWallets(): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n const win = window as WindowWithWallets;\n\n // Check window object for wallet injections\n // UI-9: Validate wallet has expected methods to reject spoofed providers\n for (const provider of WALLET_PROVIDERS) {\n const walletObj = win[provider];\n if (\n walletObj &&\n typeof walletObj === 'object' &&\n 'solana' in walletObj &&\n isValidSolanaProvider(walletObj.solana)\n ) {\n return true;\n }\n }\n\n // Check for generic Solana provider (e.g., from some mobile wallet browsers)\n // UI-9: Also validate generic provider\n if (isValidSolanaProvider(win.solana)) {\n return true;\n }\n\n // Check for wallet-standard registered wallets (e.g., MWA via registerMwa())\n if (hasWalletStandardWallets()) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Returns list of detected Solana wallet names (for debugging/display)\n *\n * @returns Array of detected wallet provider names\n */\nexport function getDetectedWalletNames(): string[] {\n if (typeof window === 'undefined') {\n return [];\n }\n\n const win = window as WindowWithWallets;\n const detected: string[] = [];\n\n // UI-9: Use validation to reject spoofed providers\n for (const provider of WALLET_PROVIDERS) {\n const walletObj = win[provider];\n if (\n walletObj &&\n typeof walletObj === 'object' &&\n 'solana' in walletObj &&\n isValidSolanaProvider(walletObj.solana)\n ) {\n detected.push(provider);\n }\n }\n\n if (isValidSolanaProvider(win.solana) && detected.length === 0) {\n detected.push('solana');\n }\n\n return detected;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { useSolanaAuth } from '../../hooks/useSolanaAuth';\nimport { LoadingSpinner } from '../shared/LoadingSpinner';\nimport { detectSolanaWallets } from '../../utils/walletDetection';\n\nexport interface SolanaLoginButtonProps {\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n className?: string;\n variant?: 'default' | 'outline';\n size?: 'sm' | 'md' | 'lg';\n disabled?: boolean;\n /**\n * Hide the button if no Solana wallets are detected.\n * When true (default), button renders nothing if no wallets are installed.\n * When false, button always renders (useful for showing \"install wallet\" prompts).\n * @default true\n */\n hideIfNoWallet?: boolean;\n /**\n * Solana wallet adapter context. Pass this from @solana/wallet-adapter-react's useWallet().\n * The button will handle connection and signing automatically for a one-click experience.\n */\n walletContext?: {\n publicKey: { toBase58: () => string } | null;\n signMessage: ((message: Uint8Array) => Promise<Uint8Array>) | null;\n connected: boolean;\n connecting: boolean;\n connect: () => Promise<void>;\n wallet: { adapter: { name: string } } | null;\n select: (walletName: string) => void;\n wallets: Array<{\n adapter: {\n name: string;\n icon: string;\n readyState: string;\n };\n }>;\n };\n}\n\n/**\n * Solana wallet login button with one-click authentication.\n *\n * Handles wallet connection and message signing automatically.\n * If wallet is already connected, signs immediately.\n * If not connected, connects first then auto-signs.\n */\nexport function SolanaLoginButton({\n onSuccess,\n onError,\n className = '',\n variant = 'default',\n size = 'md',\n disabled = false,\n hideIfNoWallet = true,\n walletContext,\n}: SolanaLoginButtonProps) {\n const { requestChallenge, signIn, isLoading: isAuthLoading } = useSolanaAuth();\n const [showWalletSelector, setShowWalletSelector] = useState(false);\n const [pendingLogin, setPendingLogin] = useState(false);\n const [triggerConnect, setTriggerConnect] = useState(false);\n const isProcessingRef = useRef(false);\n\n // Detect wallets from browser (used when walletContext not provided)\n // Use lazy initializer to avoid re-detecting on every render\n const [browserHasWallets] = useState(() => detectSolanaWallets());\n\n const connected = walletContext?.connected ?? false;\n const connecting = walletContext?.connecting ?? false;\n const publicKey = walletContext?.publicKey;\n const signMessage = walletContext?.signMessage;\n const wallet = walletContext?.wallet;\n const wallets = walletContext?.wallets ?? [];\n\n // Get installed/ready wallets from wallet adapter context\n const installedWallets = wallets.filter(\n (w) => w.adapter.readyState === 'Installed' || w.adapter.readyState === 'Loadable'\n );\n\n // Determine if any wallets are available\n // If walletContext is provided, use its wallet list; otherwise use browser detection\n const hasWallets = walletContext ? installedWallets.length > 0 : browserHasWallets;\n\n // Execute the actual sign-in flow\n const executeSignIn = useCallback(async () => {\n if (isProcessingRef.current) return;\n if (!publicKey || !signMessage) {\n onError?.(new Error('Wallet not ready'));\n return;\n }\n\n isProcessingRef.current = true;\n try {\n const pubKeyString = publicKey.toBase58();\n\n // Request challenge from server\n const challenge = await requestChallenge(pubKeyString);\n\n // Sign the message with wallet\n const messageBytes = new TextEncoder().encode(challenge.message);\n const signatureBytes = await signMessage(messageBytes);\n\n // Validate signature\n if (!(signatureBytes instanceof Uint8Array) || signatureBytes.length === 0) {\n throw new Error('Wallet returned invalid signature');\n }\n\n // Encode signature\n let signature: string;\n try {\n signature = btoa(String.fromCharCode(...signatureBytes));\n } catch {\n throw new Error('Failed to encode signature');\n }\n\n // Authenticate with server\n await signIn(pubKeyString, signature, challenge.message);\n\n onSuccess?.();\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error);\n } finally {\n isProcessingRef.current = false;\n setPendingLogin(false);\n }\n }, [publicKey, signMessage, requestChallenge, signIn, onSuccess, onError]);\n\n // Auto-connect when wallet is selected and triggerConnect is set\n useEffect(() => {\n if (triggerConnect && wallet && !connected && !connecting && walletContext?.connect) {\n setTriggerConnect(false);\n walletContext.connect().catch((err) => {\n onError?.(err instanceof Error ? err : new Error(String(err)));\n setPendingLogin(false);\n });\n }\n }, [triggerConnect, wallet, connected, connecting, walletContext, onError]);\n\n // Auto-execute sign-in when connected with pending login\n // COMP-04: Catch unhandled rejections from executeSignIn (defensive - errors already\n // handled internally, but .catch() prevents unhandledrejection events)\n useEffect(() => {\n if (pendingLogin && connected && publicKey && signMessage && !isProcessingRef.current) {\n executeSignIn().catch(() => {\n /* Errors already passed to onError callback inside executeSignIn */\n });\n }\n }, [pendingLogin, connected, publicKey, signMessage, executeSignIn]);\n\n // Hide button if no wallets detected and hideIfNoWallet is true\n // Note: This must come after all hooks to satisfy React rules of hooks\n if (hideIfNoWallet && !hasWallets) {\n return null;\n }\n\n const handleClick = async () => {\n if (disabled || isAuthLoading || connecting) return;\n\n if (connected && publicKey && signMessage) {\n // Already connected - sign immediately\n setPendingLogin(true);\n await executeSignIn();\n } else if (wallet) {\n // Wallet selected but not connected - connect and queue login\n setPendingLogin(true);\n setTriggerConnect(true);\n } else if (installedWallets.length === 1) {\n // Only one wallet installed - auto-select it\n walletContext?.select(installedWallets[0].adapter.name);\n setPendingLogin(true);\n setTriggerConnect(true);\n } else if (installedWallets.length > 1) {\n // Multiple wallets - show selector\n setShowWalletSelector(true);\n } else {\n // No wallets - show error\n onError?.(\n new Error('No Solana wallet found. Please install Phantom or another Solana wallet.')\n );\n }\n };\n\n const handleSelectWallet = (walletName: string) => {\n setShowWalletSelector(false);\n walletContext?.select(walletName);\n setPendingLogin(true);\n setTriggerConnect(true);\n };\n\n const sizeClasses = {\n sm: 'cedros-button-sm',\n md: 'cedros-button-md',\n lg: 'cedros-button-lg',\n };\n\n const variantClasses = {\n default: 'cedros-button-social',\n outline: 'cedros-button-social-outline',\n };\n\n const isLoading = isAuthLoading || connecting || (pendingLogin && !connected);\n\n return (\n <>\n <button\n type=\"button\"\n className={`cedros-button ${variantClasses[variant]} ${sizeClasses[size]} ${className}`}\n onClick={handleClick}\n disabled={disabled || isLoading}\n aria-label=\"Continue with Solana\"\n >\n {isLoading ? (\n <LoadingSpinner size=\"sm\" />\n ) : (\n <svg\n className=\"cedros-button-icon\"\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 128 128\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n >\n <path d=\"M25.38 96.04a4.35 4.35 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7l-17.71 17.72a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7l17.71-17.72z\" />\n <path d=\"M25.38 11.81a4.47 4.47 0 0 1 3.07-1.27h91.68c1.93 0 2.9 2.34 1.54 3.7L103.96 31.96a4.35 4.35 0 0 1-3.07 1.27H9.21c-1.93 0-2.9-2.34-1.54-3.7L25.38 11.81z\" />\n <path d=\"M102.62 53.76a4.35 4.35 0 0 0-3.07-1.27H7.87c-1.93 0-2.9 2.34-1.54 3.7l17.71 17.72a4.35 4.35 0 0 0 3.07 1.27h91.68c1.93 0 2.9-2.34 1.54-3.7L102.62 53.76z\" />\n </svg>\n )}\n <span>Continue with Solana</span>\n </button>\n\n {/* Wallet Selector Modal */}\n {showWalletSelector && (\n <div\n className=\"cedros-modal-backdrop\"\n onClick={() => setShowWalletSelector(false)}\n role=\"presentation\"\n >\n <div\n className=\"cedros-modal cedros-wallet-selector\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"wallet-selector-title\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"cedros-modal-header\">\n <h2 id=\"wallet-selector-title\" className=\"cedros-modal-title\">\n Select Wallet\n </h2>\n <button\n type=\"button\"\n className=\"cedros-modal-close\"\n onClick={() => setShowWalletSelector(false)}\n aria-label=\"Close\"\n >\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M18 6L6 18M6 6l12 12\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n />\n </svg>\n </button>\n </div>\n <div className=\"cedros-modal-content\">\n <div className=\"cedros-wallet-list\">\n {installedWallets.map((w) => (\n <button\n key={w.adapter.name}\n type=\"button\"\n className=\"cedros-wallet-option\"\n onClick={() => handleSelectWallet(w.adapter.name)}\n >\n <img\n src={w.adapter.icon}\n alt=\"\"\n width=\"32\"\n height=\"32\"\n className=\"cedros-wallet-icon\"\n />\n <span>{w.adapter.name}</span>\n </button>\n ))}\n </div>\n </div>\n </div>\n </div>\n )}\n </>\n );\n}\n","/**\n * Mobile Wallet Adapter (MWA) registration for web.\n *\n * On Android Chrome, MWA lets users authenticate with their installed Solana\n * wallet app (e.g., Phantom, Solflare) via Android Intents — no browser\n * extension needed.\n *\n * Once registered, MWA appears as a wallet option in the wallet adapter's\n * wallet list (alongside browser extension wallets). Users see it as\n * \"Use Installed Wallet\" in the wallet selector.\n *\n * Requires the optional peer dependency: @solana-mobile/wallet-standard-mobile\n *\n * @see https://docs.solanamobile.com/get-started/web/installation\n */\n\nexport interface MobileWalletConfig {\n /** App name shown in the wallet's authorization dialog */\n name?: string;\n /** App URI for identity verification */\n uri?: string;\n /** App icon path/URL shown in the wallet dialog */\n icon?: string;\n /** Solana cluster(s) to support. Default: ['solana:mainnet'] */\n chains?: string[];\n}\n\n/**\n * Register Mobile Wallet Adapter as a wallet-standard wallet.\n *\n * Call this once at your application root (before rendering). After registration,\n * MWA automatically appears as \"Use Installed Wallet\" for users browsing on\n * Android Chrome with a Solana wallet app installed.\n *\n * Must be called in a non-SSR context (browser only). For Next.js, call in a\n * Client Component with `'use client'`.\n *\n * @example\n * ```tsx\n * import { registerMobileWallet, CedrosLoginProvider } from '@cedros/login-react';\n *\n * // Register before provider mounts\n * registerMobileWallet({ name: 'My App', uri: 'https://myapp.com' });\n *\n * function App() {\n * return (\n * <CedrosLoginProvider config={{ serverUrl: '...' }}>\n * <LoginForm />\n * </CedrosLoginProvider>\n * );\n * }\n * ```\n *\n * @returns true if registration succeeded, false if package not installed or SSR\n */\nexport function registerMobileWallet(config?: MobileWalletConfig): boolean {\n if (typeof window === 'undefined') {\n return false;\n }\n\n try {\n // Dynamic import to avoid bundling the optional peer dep\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const mwa = require('@solana-mobile/wallet-standard-mobile');\n\n const chains = config?.chains ?? ['solana:mainnet'];\n\n const registrationConfig: Record<string, unknown> = {\n appIdentity: {\n name: config?.name,\n uri: config?.uri,\n icon: config?.icon,\n },\n chains,\n };\n\n // Use built-in defaults for optional config if available\n if (typeof mwa.createDefaultAuthorizationCache === 'function') {\n registrationConfig.authorizationCache = mwa.createDefaultAuthorizationCache();\n }\n if (typeof mwa.createDefaultChainSelector === 'function') {\n registrationConfig.chainSelector = mwa.createDefaultChainSelector();\n }\n if (typeof mwa.createDefaultWalletNotFoundHandler === 'function') {\n registrationConfig.onWalletNotFound = mwa.createDefaultWalletNotFoundHandler();\n }\n\n mwa.registerMwa(registrationConfig);\n return true;\n } catch {\n // @solana-mobile/wallet-standard-mobile not installed\n return false;\n }\n}\n"],"names":["useSolanaAuth","config","_internal","useCedrosLogin","isLoading","setIsLoading","useState","error","setError","apiClient","useMemo","ApiClient","requestChallenge","useCallback","publicKey","validateSolanaPublicKey","authError","err","handleApiError","signIn","signature","message","data","clearError","WALLET_PROVIDERS","isValidSolanaProvider","provider","wallet","hasWalletStandardWallets","walletStandard","wallets","detectSolanaWallets","win","walletObj","SolanaLoginButton","onSuccess","onError","className","variant","size","disabled","hideIfNoWallet","walletContext","isAuthLoading","showWalletSelector","setShowWalletSelector","pendingLogin","setPendingLogin","triggerConnect","setTriggerConnect","isProcessingRef","useRef","browserHasWallets","connected","connecting","signMessage","installedWallets","w","hasWallets","executeSignIn","pubKeyString","challenge","messageBytes","signatureBytes","useEffect","handleClick","handleSelectWallet","walletName","sizeClasses","variantClasses","jsxs","Fragment","jsx","LoadingSpinner","e","registerMobileWallet","mwa","chains","registrationConfig"],"mappings":"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"}
|
package/dist/solana-only.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./useAuth-X6Ds6WW4.cjs"),o=require("./useCedrosLogin-C9MrcZvh.cjs"),e=require("./mobileWalletAdapter-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./useAuth-X6Ds6WW4.cjs"),o=require("./useCedrosLogin-C9MrcZvh.cjs"),e=require("./mobileWalletAdapter-DOdmFAk0.cjs"),n=require("./LoadingSpinner-d6sSxgQN.cjs"),i=require("./ErrorMessage-CHbYbVi2.cjs");exports.CedrosLoginProvider=r.CedrosLoginProvider;exports.useAuth=r.useAuth;exports.useCedrosLogin=o.useCedrosLogin;exports.SolanaLoginButton=e.SolanaLoginButton;exports.registerMobileWallet=e.registerMobileWallet;exports.useSolanaAuth=e.useSolanaAuth;exports.LoadingSpinner=n.LoadingSpinner;exports.ErrorMessage=i.ErrorMessage;
|
package/dist/solana-only.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { C as e, u as s } from "./useAuth-m5Hf89v8.js";
|
|
2
2
|
import { u as t } from "./useCedrosLogin-_94MmGGq.js";
|
|
3
|
-
import { S as u, r as i, u as g } from "./mobileWalletAdapter-
|
|
3
|
+
import { S as u, r as i, u as g } from "./mobileWalletAdapter-BulleEbi.js";
|
|
4
4
|
import { L as f } from "./LoadingSpinner-6vml-zwr.js";
|
|
5
5
|
import { E as m } from "./ErrorMessage-CcEK0pYO.js";
|
|
6
6
|
export {
|
package/package.json
CHANGED