mirror of
https://github.com/neynarxyz/create-farcaster-mini-app.git
synced 2025-11-15 23:58:56 -05:00
Revert "Merge pull request #18 from neynarxyz/veganbeef/fix-siwn"
This reverts commit 78626c2dc7d4646bdd9b60b8f67f3ffe5c1defb5, reversing changes made to b1fdfc19a92241638692d58494f48ce1bb25df74.
This commit is contained in:
parent
181c364de4
commit
349cdea489
10
bin/init.js
10
bin/init.js
@ -460,6 +460,7 @@ export async function init(
|
|||||||
// Add dependencies
|
// Add dependencies
|
||||||
packageJson.dependencies = {
|
packageJson.dependencies = {
|
||||||
'@farcaster/auth-client': '>=0.3.0 <1.0.0',
|
'@farcaster/auth-client': '>=0.3.0 <1.0.0',
|
||||||
|
'@farcaster/auth-kit': '>=0.6.0 <1.0.0',
|
||||||
'@farcaster/miniapp-node': '>=0.1.5 <1.0.0',
|
'@farcaster/miniapp-node': '>=0.1.5 <1.0.0',
|
||||||
'@farcaster/miniapp-sdk': '>=0.1.6 <1.0.0',
|
'@farcaster/miniapp-sdk': '>=0.1.6 <1.0.0',
|
||||||
'@farcaster/miniapp-wagmi-connector': '^1.0.0',
|
'@farcaster/miniapp-wagmi-connector': '^1.0.0',
|
||||||
@ -707,15 +708,6 @@ export async function init(
|
|||||||
fs.rmSync(binPath, { recursive: true, force: true });
|
fs.rmSync(binPath, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove NeynarAuthButton directory if useSponsoredSigner is false
|
|
||||||
if (!answers.useSponsoredSigner) {
|
|
||||||
console.log('\nRemoving NeynarAuthButton directory (useSponsoredSigner is false)...');
|
|
||||||
const neynarAuthButtonPath = path.join(projectPath, 'src', 'components', 'ui', 'NeynarAuthButton');
|
|
||||||
if (fs.existsSync(neynarAuthButtonPath)) {
|
|
||||||
fs.rmSync(neynarAuthButtonPath, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize git repository
|
// Initialize git repository
|
||||||
console.log('\nInitializing git repository...');
|
console.log('\nInitializing git repository...');
|
||||||
execSync('git init', { cwd: projectPath });
|
execSync('git init', { cwd: projectPath });
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import '@farcaster/auth-kit/styles.css';
|
import { useCallback, useEffect, useState, useRef } from 'react';
|
||||||
import { useSignIn } from '@farcaster/auth-kit';
|
import sdk, { SignIn as SignInCore } from '@farcaster/frame-sdk';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useMiniApp } from '@neynar/react';
|
||||||
import { cn } from '~/lib/utils';
|
|
||||||
import { Button } from '~/components/ui/Button';
|
import { Button } from '~/components/ui/Button';
|
||||||
import { AuthDialog } from '~/components/ui/NeynarAuthButton/AuthDialog';
|
import { AuthDialog } from '~/components/ui/NeynarAuthButton/AuthDialog';
|
||||||
import { ProfileButton } from '~/components/ui/NeynarAuthButton/ProfileButton';
|
import { ProfileButton } from '~/components/ui/NeynarAuthButton/ProfileButton';
|
||||||
@ -24,7 +23,6 @@ type User = {
|
|||||||
// Add other user properties as needed
|
// Add other user properties as needed
|
||||||
};
|
};
|
||||||
|
|
||||||
const STORAGE_KEY = 'neynar_authenticated_user';
|
|
||||||
const FARCASTER_FID = 9152;
|
const FARCASTER_FID = 9152;
|
||||||
|
|
||||||
interface StoredAuthState {
|
interface StoredAuthState {
|
||||||
@ -98,7 +96,12 @@ export function NeynarAuthButton() {
|
|||||||
const [storedAuth, setStoredAuth] = useState<StoredAuthState | null>(null);
|
const [storedAuth, setStoredAuth] = useState<StoredAuthState | null>(null);
|
||||||
const [signersLoading, setSignersLoading] = useState(false);
|
const [signersLoading, setSignersLoading] = useState(false);
|
||||||
const { context } = useMiniApp();
|
const { context } = useMiniApp();
|
||||||
const { data: session } = useSession();
|
const {
|
||||||
|
authenticatedUser: quickAuthUser,
|
||||||
|
signIn: quickAuthSignIn,
|
||||||
|
signOut: quickAuthSignOut,
|
||||||
|
} = useQuickAuth();
|
||||||
|
|
||||||
// New state for unified dialog flow
|
// New state for unified dialog flow
|
||||||
const [showDialog, setShowDialog] = useState(false);
|
const [showDialog, setShowDialog] = useState(false);
|
||||||
const [dialogStep, setDialogStep] = useState<'signin' | 'access' | 'loading'>(
|
const [dialogStep, setDialogStep] = useState<'signin' | 'access' | 'loading'>(
|
||||||
@ -112,6 +115,12 @@ export function NeynarAuthButton() {
|
|||||||
);
|
);
|
||||||
const [message, setMessage] = useState<string | null>(null);
|
const [message, setMessage] = useState<string | null>(null);
|
||||||
const [signature, setSignature] = useState<string | null>(null);
|
const [signature, setSignature] = useState<string | null>(null);
|
||||||
|
const [isSignerFlowRunning, setIsSignerFlowRunning] = useState(false);
|
||||||
|
const signerFlowStartedRef = useRef(false);
|
||||||
|
const [backendUserProfile, setBackendUserProfile] = useState<{
|
||||||
|
username?: string;
|
||||||
|
pfpUrl?: string;
|
||||||
|
}>({});
|
||||||
|
|
||||||
// Determine which flow to use based on context
|
// Determine which flow to use based on context
|
||||||
const useBackendFlow = context !== undefined;
|
const useBackendFlow = context !== undefined;
|
||||||
@ -144,25 +153,15 @@ export function NeynarAuthButton() {
|
|||||||
if (!useBackendFlow) return;
|
if (!useBackendFlow) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// For backend flow, we need to sign in again with the additional data
|
// For backend flow, use QuickAuth to sign in
|
||||||
if (message && signature) {
|
if (signers && signers.length > 0) {
|
||||||
const signInData = {
|
await quickAuthSignIn();
|
||||||
message,
|
|
||||||
signature,
|
|
||||||
redirect: false,
|
|
||||||
nonce: nonce || '',
|
|
||||||
fid: user?.fid?.toString() || '',
|
|
||||||
signers: JSON.stringify(signers),
|
|
||||||
user: JSON.stringify(user),
|
|
||||||
};
|
|
||||||
|
|
||||||
await backendSignIn('neynar', signInData);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Error updating session with signers:', error);
|
console.error('❌ Error updating session with signers:', error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[useBackendFlow, message, signature, nonce]
|
[useBackendFlow, quickAuthSignIn],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Helper function to fetch user data from Neynar API
|
// Helper function to fetch user data from Neynar API
|
||||||
@ -244,14 +243,18 @@ export function NeynarAuthButton() {
|
|||||||
if (useBackendFlow) {
|
if (useBackendFlow) {
|
||||||
// For backend flow, update session with signers
|
// For backend flow, update session with signers
|
||||||
if (signerData.signers && signerData.signers.length > 0) {
|
if (signerData.signers && signerData.signers.length > 0) {
|
||||||
const user =
|
// Get user data for the first signer
|
||||||
signerData.user ||
|
let user: StoredAuthState['user'] | null = null;
|
||||||
(await fetchUserData(signerData.signers[0].fid));
|
if (signerData.signers[0].fid) {
|
||||||
|
user = (await fetchUserData(
|
||||||
|
signerData.signers[0].fid,
|
||||||
|
)) as StoredAuthState['user'];
|
||||||
|
}
|
||||||
await updateSessionWithSigners(signerData.signers, user);
|
await updateSessionWithSigners(signerData.signers, user);
|
||||||
}
|
}
|
||||||
return signerData.signers;
|
return signerData.signers;
|
||||||
} else {
|
} else {
|
||||||
// For frontend flow, store in localStorage
|
// For frontend flow, store in memory only
|
||||||
let user: StoredAuthState['user'] | null = null;
|
let user: StoredAuthState['user'] | null = null;
|
||||||
|
|
||||||
if (signerData.signers && signerData.signers.length > 0) {
|
if (signerData.signers && signerData.signers.length > 0) {
|
||||||
@ -261,13 +264,12 @@ export function NeynarAuthButton() {
|
|||||||
user = fetchedUser;
|
user = fetchedUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store signers in localStorage, preserving existing auth data
|
// Store signers in memory only
|
||||||
const updatedState: StoredAuthState = {
|
const updatedState: StoredAuthState = {
|
||||||
isAuthenticated: !!user,
|
isAuthenticated: !!user,
|
||||||
signers: signerData.signers || [],
|
signers: signerData.signers || [],
|
||||||
user,
|
user,
|
||||||
};
|
};
|
||||||
setItem<StoredAuthState>(STORAGE_KEY, updatedState);
|
|
||||||
setStoredAuth(updatedState);
|
setStoredAuth(updatedState);
|
||||||
|
|
||||||
return signerData.signers;
|
return signerData.signers;
|
||||||
@ -289,14 +291,46 @@ export function NeynarAuthButton() {
|
|||||||
// Helper function to poll signer status
|
// Helper function to poll signer status
|
||||||
const startPolling = useCallback(
|
const startPolling = useCallback(
|
||||||
(signerUuid: string, message: string, signature: string) => {
|
(signerUuid: string, message: string, signature: string) => {
|
||||||
|
// Clear any existing polling interval before starting a new one
|
||||||
|
if (pollingInterval) {
|
||||||
|
clearInterval(pollingInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
let retryCount = 0;
|
||||||
|
const maxRetries = 10; // Maximum 10 retries (20 seconds total)
|
||||||
|
const maxPollingTime = 60000; // Maximum 60 seconds of polling
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
const interval = setInterval(async () => {
|
const interval = setInterval(async () => {
|
||||||
|
// Check if we've been polling too long
|
||||||
|
if (Date.now() - startTime > maxPollingTime) {
|
||||||
|
clearInterval(interval);
|
||||||
|
setPollingInterval(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`/api/auth/signer?signerUuid=${signerUuid}`,
|
`/api/auth/signer?signerUuid=${signerUuid}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to poll signer status');
|
// Check if it's a rate limit error
|
||||||
|
if (response.status === 429) {
|
||||||
|
clearInterval(interval);
|
||||||
|
setPollingInterval(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment retry count for other errors
|
||||||
|
retryCount++;
|
||||||
|
if (retryCount >= maxRetries) {
|
||||||
|
clearInterval(interval);
|
||||||
|
setPollingInterval(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Failed to poll signer status: ${response.status}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const signerData = await response.json();
|
const signerData = await response.json();
|
||||||
@ -318,7 +352,7 @@ export function NeynarAuthButton() {
|
|||||||
|
|
||||||
setPollingInterval(interval);
|
setPollingInterval(interval);
|
||||||
},
|
},
|
||||||
[fetchAllSigners]
|
[fetchAllSigners, pollingInterval],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Cleanup polling on unmount
|
// Cleanup polling on unmount
|
||||||
@ -327,6 +361,7 @@ export function NeynarAuthButton() {
|
|||||||
if (pollingInterval) {
|
if (pollingInterval) {
|
||||||
clearInterval(pollingInterval);
|
clearInterval(pollingInterval);
|
||||||
}
|
}
|
||||||
|
signerFlowStartedRef.current = false;
|
||||||
};
|
};
|
||||||
}, [pollingInterval]);
|
}, [pollingInterval]);
|
||||||
|
|
||||||
@ -349,78 +384,118 @@ export function NeynarAuthButton() {
|
|||||||
generateNonce();
|
generateNonce();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Load stored auth state on mount (only for frontend flow)
|
// Backend flow using QuickAuth
|
||||||
useEffect(() => {
|
const handleBackendSignIn = useCallback(async () => {
|
||||||
if (!useBackendFlow) {
|
if (!nonce) {
|
||||||
const stored = getItem<StoredAuthState>(STORAGE_KEY);
|
console.error('❌ No nonce available for backend sign-in');
|
||||||
if (stored && stored.isAuthenticated) {
|
return;
|
||||||
setStoredAuth(stored);
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setSignersLoading(true);
|
||||||
|
const result = await sdk.actions.signIn({ nonce });
|
||||||
|
|
||||||
|
setMessage(result.message);
|
||||||
|
setSignature(result.signature);
|
||||||
|
// Use QuickAuth to sign in
|
||||||
|
await quickAuthSignIn();
|
||||||
|
// Fetch user profile after sign in
|
||||||
|
if (quickAuthUser?.fid) {
|
||||||
|
const user = await fetchUserData(quickAuthUser.fid);
|
||||||
|
setBackendUserProfile({
|
||||||
|
username: user?.username || '',
|
||||||
|
pfpUrl: user?.pfp_url || '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof SignInCore.RejectedByUser) {
|
||||||
|
console.log('ℹ️ Sign-in rejected by user');
|
||||||
|
} else {
|
||||||
|
console.error('❌ Backend sign-in error:', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [useBackendFlow]);
|
}, [nonce, quickAuthSignIn, quickAuthUser, fetchUserData]);
|
||||||
|
|
||||||
// Success callback - this is critical!
|
|
||||||
const onSuccessCallback = useCallback(
|
|
||||||
async (res: unknown) => {
|
|
||||||
if (!useBackendFlow) {
|
|
||||||
// Only handle localStorage for frontend flow
|
|
||||||
const existingAuth = getItem<StoredAuthState>(STORAGE_KEY);
|
|
||||||
const user = await fetchUserData(res.fid);
|
|
||||||
const authState: StoredAuthState = {
|
|
||||||
...existingAuth,
|
|
||||||
isAuthenticated: true,
|
|
||||||
user: user as StoredAuthState['user'],
|
|
||||||
signers: existingAuth?.signers || [], // Preserve existing signers
|
|
||||||
};
|
|
||||||
setItem<StoredAuthState>(STORAGE_KEY, authState);
|
|
||||||
setStoredAuth(authState);
|
|
||||||
}
|
|
||||||
// For backend flow, the session will be handled by NextAuth
|
|
||||||
},
|
|
||||||
[useBackendFlow, fetchUserData]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Error callback
|
|
||||||
const onErrorCallback = useCallback((error?: Error | null) => {
|
|
||||||
console.error('❌ Sign in error:', error);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const signInState = useSignIn({
|
|
||||||
nonce: nonce || undefined,
|
|
||||||
onSuccess: onSuccessCallback,
|
|
||||||
onError: onErrorCallback,
|
|
||||||
});
|
|
||||||
|
|
||||||
const {
|
|
||||||
signIn: frontendSignIn,
|
|
||||||
signOut: frontendSignOut,
|
|
||||||
connect,
|
|
||||||
reconnect,
|
|
||||||
isSuccess,
|
|
||||||
isError,
|
|
||||||
error,
|
|
||||||
channelToken,
|
|
||||||
url,
|
|
||||||
data,
|
|
||||||
validSignature,
|
|
||||||
} = signInState;
|
|
||||||
|
|
||||||
|
// Fetch user profile when quickAuthUser.fid changes (for backend flow)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMessage(data?.message || null);
|
if (useBackendFlow && quickAuthUser?.fid) {
|
||||||
setSignature(data?.signature || null);
|
(async () => {
|
||||||
}, [data?.message, data?.signature]);
|
const user = await fetchUserData(quickAuthUser.fid);
|
||||||
|
setBackendUserProfile({
|
||||||
// Connect for frontend flow when nonce is available
|
username: user?.username || '',
|
||||||
useEffect(() => {
|
pfpUrl: user?.pfp_url || '',
|
||||||
if (!useBackendFlow && nonce && !channelToken) {
|
});
|
||||||
connect();
|
})();
|
||||||
}
|
}
|
||||||
}, [useBackendFlow, nonce, channelToken, connect]);
|
}, [useBackendFlow, quickAuthUser?.fid, fetchUserData]);
|
||||||
|
|
||||||
|
const handleFrontEndSignIn = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setSignersLoading(true);
|
||||||
|
const result = await sdk.actions.signIn({ nonce: nonce || '' });
|
||||||
|
|
||||||
|
setMessage(result.message);
|
||||||
|
setSignature(result.signature);
|
||||||
|
|
||||||
|
// For frontend flow, we'll handle the signer flow in the useEffect
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof SignInCore.RejectedByUser) {
|
||||||
|
console.log('ℹ️ Sign-in rejected by user');
|
||||||
|
} else {
|
||||||
|
console.error('❌ Frontend sign-in error:', e);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setSignersLoading(false);
|
||||||
|
}
|
||||||
|
}, [nonce]);
|
||||||
|
|
||||||
|
const handleSignOut = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
setSignersLoading(true);
|
||||||
|
|
||||||
|
if (useBackendFlow) {
|
||||||
|
// Use QuickAuth sign out
|
||||||
|
await quickAuthSignOut();
|
||||||
|
} else {
|
||||||
|
// Frontend flow sign out
|
||||||
|
setStoredAuth(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common cleanup for both flows
|
||||||
|
setShowDialog(false);
|
||||||
|
setDialogStep('signin');
|
||||||
|
setSignerApprovalUrl(null);
|
||||||
|
setMessage(null);
|
||||||
|
setSignature(null);
|
||||||
|
|
||||||
|
// Reset polling interval
|
||||||
|
if (pollingInterval) {
|
||||||
|
clearInterval(pollingInterval);
|
||||||
|
setPollingInterval(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset signer flow flag
|
||||||
|
signerFlowStartedRef.current = false;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error during sign out:', error);
|
||||||
|
// Optionally handle error state
|
||||||
|
} finally {
|
||||||
|
setSignersLoading(false);
|
||||||
|
}
|
||||||
|
}, [useBackendFlow, pollingInterval, quickAuthSignOut]);
|
||||||
|
|
||||||
// Handle fetching signers after successful authentication
|
// Handle fetching signers after successful authentication
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (message && signature) {
|
if (
|
||||||
|
message &&
|
||||||
|
signature &&
|
||||||
|
!isSignerFlowRunning &&
|
||||||
|
!signerFlowStartedRef.current
|
||||||
|
) {
|
||||||
|
signerFlowStartedRef.current = true;
|
||||||
|
|
||||||
const handleSignerFlow = async () => {
|
const handleSignerFlow = async () => {
|
||||||
|
setIsSignerFlowRunning(true);
|
||||||
try {
|
try {
|
||||||
const clientContext = context?.client as Record<string, unknown>;
|
const clientContext = context?.client as Record<string, unknown>;
|
||||||
const isMobileContext =
|
const isMobileContext =
|
||||||
@ -436,6 +511,7 @@ export function NeynarAuthButton() {
|
|||||||
|
|
||||||
// First, fetch existing signers
|
// First, fetch existing signers
|
||||||
const signers = await fetchAllSigners(message, signature);
|
const signers = await fetchAllSigners(message, signature);
|
||||||
|
|
||||||
if (useBackendFlow && isMobileContext) setSignersLoading(true);
|
if (useBackendFlow && isMobileContext) setSignersLoading(true);
|
||||||
|
|
||||||
// Check if no signers exist or if we have empty signers
|
// Check if no signers exist or if we have empty signers
|
||||||
@ -456,9 +532,9 @@ export function NeynarAuthButton() {
|
|||||||
setShowDialog(false);
|
setShowDialog(false);
|
||||||
await sdk.actions.openUrl(
|
await sdk.actions.openUrl(
|
||||||
signedKeyData.signer_approval_url.replace(
|
signedKeyData.signer_approval_url.replace(
|
||||||
'https://client.farcaster.xyz/deeplinks/',
|
'https://client.farcaster.xyz/deeplinks/signed-key-request',
|
||||||
'farcaster://'
|
'https://farcaster.xyz/~/connect',
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setShowDialog(true); // Ensure dialog is shown during loading
|
setShowDialog(true); // Ensure dialog is shown during loading
|
||||||
@ -480,116 +556,25 @@ export function NeynarAuthButton() {
|
|||||||
setSignersLoading(false);
|
setSignersLoading(false);
|
||||||
setShowDialog(false);
|
setShowDialog(false);
|
||||||
setSignerApprovalUrl(null);
|
setSignerApprovalUrl(null);
|
||||||
|
} finally {
|
||||||
|
setIsSignerFlowRunning(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSignerFlow();
|
handleSignerFlow();
|
||||||
}
|
}
|
||||||
}, [
|
}, [message, signature]); // Simplified dependencies
|
||||||
message,
|
|
||||||
signature,
|
|
||||||
fetchAllSigners,
|
|
||||||
createSigner,
|
|
||||||
generateSignedKeyRequest,
|
|
||||||
startPolling,
|
|
||||||
context,
|
|
||||||
useBackendFlow,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Backend flow using NextAuth
|
|
||||||
const handleBackendSignIn = useCallback(async () => {
|
|
||||||
if (!nonce) {
|
|
||||||
console.error('❌ No nonce available for backend sign-in');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
setSignersLoading(true);
|
|
||||||
const result = await sdk.actions.signIn({ nonce });
|
|
||||||
|
|
||||||
const signInData = {
|
|
||||||
message: result.message,
|
|
||||||
signature: result.signature,
|
|
||||||
redirect: false,
|
|
||||||
nonce: nonce,
|
|
||||||
};
|
|
||||||
|
|
||||||
const nextAuthResult = await backendSignIn('neynar', signInData);
|
|
||||||
if (nextAuthResult?.ok) {
|
|
||||||
setMessage(result.message);
|
|
||||||
setSignature(result.signature);
|
|
||||||
} else {
|
|
||||||
console.error('❌ NextAuth sign-in failed:', nextAuthResult);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof SignInCore.RejectedByUser) {
|
|
||||||
console.log('ℹ️ Sign-in rejected by user');
|
|
||||||
} else {
|
|
||||||
console.error('❌ Backend sign-in error:', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [nonce]);
|
|
||||||
|
|
||||||
const handleFrontEndSignIn = useCallback(() => {
|
|
||||||
if (isError) {
|
|
||||||
reconnect();
|
|
||||||
}
|
|
||||||
setDialogStep('signin');
|
|
||||||
setShowDialog(true);
|
|
||||||
frontendSignIn();
|
|
||||||
}, [isError, reconnect, frontendSignIn]);
|
|
||||||
|
|
||||||
const handleSignOut = useCallback(async () => {
|
|
||||||
try {
|
|
||||||
setSignersLoading(true);
|
|
||||||
|
|
||||||
if (useBackendFlow) {
|
|
||||||
// Only sign out from NextAuth if the current session is from Neynar provider
|
|
||||||
if (session?.provider === 'neynar') {
|
|
||||||
await backendSignOut({ redirect: false });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Frontend flow sign out
|
|
||||||
frontendSignOut();
|
|
||||||
removeItem(STORAGE_KEY);
|
|
||||||
setStoredAuth(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common cleanup for both flows
|
|
||||||
setShowDialog(false);
|
|
||||||
setDialogStep('signin');
|
|
||||||
setSignerApprovalUrl(null);
|
|
||||||
setMessage(null);
|
|
||||||
setSignature(null);
|
|
||||||
|
|
||||||
// Reset polling interval
|
|
||||||
if (pollingInterval) {
|
|
||||||
clearInterval(pollingInterval);
|
|
||||||
setPollingInterval(null);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Error during sign out:', error);
|
|
||||||
// Optionally handle error state
|
|
||||||
} finally {
|
|
||||||
setSignersLoading(false);
|
|
||||||
}
|
|
||||||
}, [useBackendFlow, frontendSignOut, pollingInterval, session]);
|
|
||||||
|
|
||||||
const authenticated = useBackendFlow
|
const authenticated = useBackendFlow
|
||||||
? !!(
|
? !!quickAuthUser?.fid
|
||||||
session?.provider === 'neynar' &&
|
: storedAuth?.isAuthenticated &&
|
||||||
session?.user?.fid &&
|
|
||||||
session?.signers &&
|
|
||||||
session.signers.length > 0
|
|
||||||
)
|
|
||||||
: ((isSuccess && validSignature) || storedAuth?.isAuthenticated) &&
|
|
||||||
!!(storedAuth?.signers && storedAuth.signers.length > 0);
|
!!(storedAuth?.signers && storedAuth.signers.length > 0);
|
||||||
|
|
||||||
const userData = useBackendFlow
|
const userData = useBackendFlow
|
||||||
? {
|
? {
|
||||||
fid: session?.user?.fid,
|
fid: quickAuthUser?.fid,
|
||||||
username: session?.user?.username || '',
|
username: backendUserProfile.username ?? '',
|
||||||
pfpUrl: session?.user?.pfp_url || '',
|
pfpUrl: backendUserProfile.pfpUrl ?? '',
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
fid: storedAuth?.user?.fid,
|
fid: storedAuth?.user?.fid,
|
||||||
@ -618,18 +603,17 @@ export function NeynarAuthButton() {
|
|||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
onClick={useBackendFlow ? handleBackendSignIn : handleFrontEndSignIn}
|
onClick={useBackendFlow ? handleBackendSignIn : handleFrontEndSignIn}
|
||||||
disabled={!useBackendFlow && !url}
|
disabled={signersLoading}
|
||||||
className={cn(
|
className={cn(
|
||||||
'btn btn-primary flex items-center gap-3',
|
'btn btn-primary flex items-center gap-3',
|
||||||
'disabled:opacity-50 disabled:cursor-not-allowed',
|
'disabled:opacity-50 disabled:cursor-not-allowed',
|
||||||
'transform transition-all duration-200 active:scale-[0.98]',
|
'transform transition-all duration-200 active:scale-[0.98]',
|
||||||
!url && !useBackendFlow && 'cursor-not-allowed'
|
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{!useBackendFlow && !url ? (
|
{signersLoading ? (
|
||||||
<>
|
<>
|
||||||
<div className="spinner-primary w-5 h-5" />
|
<div className="spinner-primary w-5 h-5" />
|
||||||
<span>Initializing...</span>
|
<span>Loading...</span>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@ -652,9 +636,9 @@ export function NeynarAuthButton() {
|
|||||||
setPollingInterval(null);
|
setPollingInterval(null);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
url={url}
|
url={undefined}
|
||||||
isError={isError}
|
isError={false}
|
||||||
error={error}
|
error={null}
|
||||||
step={dialogStep}
|
step={dialogStep}
|
||||||
isLoading={signersLoading}
|
isLoading={signersLoading}
|
||||||
signerApprovalUrl={signerApprovalUrl}
|
signerApprovalUrl={signerApprovalUrl}
|
||||||
@ -662,4 +646,4 @@ export function NeynarAuthButton() {
|
|||||||
}
|
}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,19 +5,10 @@ import { type Haptics } from '@farcaster/miniapp-sdk';
|
|||||||
import { useMiniApp } from '@neynar/react';
|
import { useMiniApp } from '@neynar/react';
|
||||||
import { APP_URL } from '~/lib/constants';
|
import { APP_URL } from '~/lib/constants';
|
||||||
import { Button } from '../Button';
|
import { Button } from '../Button';
|
||||||
|
import { NeynarAuthButton } from '../NeynarAuthButton/index';
|
||||||
import { ShareButton } from '../Share';
|
import { ShareButton } from '../Share';
|
||||||
import { SignIn } from '../wallet/SignIn';
|
import { SignIn } from '../wallet/SignIn';
|
||||||
|
|
||||||
// Optional import for NeynarAuthButton - may not exist in all templates
|
|
||||||
let NeynarAuthButton: React.ComponentType | null = null;
|
|
||||||
try {
|
|
||||||
const module = require('../NeynarAuthButton/index');
|
|
||||||
NeynarAuthButton = module.NeynarAuthButton;
|
|
||||||
} catch (error) {
|
|
||||||
// Component doesn't exist, that's okay
|
|
||||||
console.log('NeynarAuthButton not available in this template');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActionsTab component handles mini app actions like sharing, notifications, and haptic feedback.
|
* ActionsTab component handles mini app actions like sharing, notifications, and haptic feedback.
|
||||||
*
|
*
|
||||||
@ -149,7 +140,7 @@ export function ActionsTab() {
|
|||||||
<SignIn />
|
<SignIn />
|
||||||
|
|
||||||
{/* Neynar Authentication */}
|
{/* Neynar Authentication */}
|
||||||
{NeynarAuthButton && <NeynarAuthButton />}
|
<NeynarAuthButton />
|
||||||
|
|
||||||
{/* Mini app actions */}
|
{/* Mini app actions */}
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user