This commit is contained in:
Shreyaschorge 2025-07-15 21:38:47 +05:30
parent b366d97b53
commit 378ea65154
No known key found for this signature in database
8 changed files with 57 additions and 55 deletions

View File

@ -1,12 +1,12 @@
#!/usr/bin/env node
import inquirer from 'inquirer';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import { execSync } from 'child_process';
import fs from 'fs';
import path from 'path';
import crypto from 'crypto';
import fs from 'fs';
import { dirname } from 'path';
import path from 'path';
import { fileURLToPath } from 'url';
import inquirer from 'inquirer';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
@ -47,7 +47,7 @@ async function queryNeynarApp(apiKey) {
}
try {
const response = await fetch(
`https://api.neynar.com/portal/app_by_api_key?starter_kit=true`,
'https://api.neynar.com/portal/app_by_api_key?starter_kit=true',
{
headers: {
'x-api-key': apiKey,
@ -444,7 +444,7 @@ export async function init(
// Update package.json
console.log('\nUpdating package.json...');
const packageJsonPath = path.join(projectPath, 'package.json');
let packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
packageJson.name = finalProjectName;
packageJson.version = '0.1.0';

View File

@ -1,9 +1,9 @@
import localtunnel from 'localtunnel';
import { spawn } from 'child_process';
import { createServer } from 'net';
import dotenv from 'dotenv';
import path from 'path';
import { fileURLToPath } from 'url';
import dotenv from 'dotenv';
import localtunnel from 'localtunnel';
// Load environment variables
dotenv.config({ path: '.env.local' });
@ -96,7 +96,7 @@ async function startDev() {
? `1. Run: netstat -ano | findstr :${port}\n` +
'2. Note the PID (Process ID) from the output\n' +
'3. Run: taskkill /PID <PID> /F\n'
: `On macOS/Linux, run:\nnpm run cleanup\n`) +
: 'On macOS/Linux, run:\nnpm run cleanup\n') +
'\nThen try running this command again.',
);
process.exit(1);

View File

@ -8,15 +8,12 @@ export async function POST(request: Request) {
const { token } = await request.json();
if (!token) {
return NextResponse.json(
{ error: 'Token is required' },
{ status: 400 }
);
return NextResponse.json({ error: 'Token is required' }, { status: 400 });
}
// Get domain from environment or request
const domain = process.env.NEXT_PUBLIC_URL
? new URL(process.env.NEXT_PUBLIC_URL).hostname
const domain = process.env.NEXT_PUBLIC_URL
? new URL(process.env.NEXT_PUBLIC_URL).hostname
: request.headers.get('host') || 'localhost';
try {
@ -35,10 +32,7 @@ export async function POST(request: Request) {
} catch (e) {
if (e instanceof Errors.InvalidTokenError) {
console.info('Invalid token:', e.message);
return NextResponse.json(
{ error: 'Invalid token' },
{ status: 401 }
);
return NextResponse.json({ error: 'Invalid token' }, { status: 401 });
}
throw e;
}
@ -46,7 +40,7 @@ export async function POST(request: Request) {
console.error('Token validation error:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
{ status: 500 },
);
}
}
}

View File

@ -1,5 +1,4 @@
import type { Metadata } from 'next';
import '~/app/globals.css';
import { Providers } from '~/app/providers';
import { APP_NAME, APP_DESCRIPTION } from '~/lib/constants';

View File

@ -16,8 +16,14 @@ export function ProfileButton({
useDetectClickOutside(ref, () => setShowDropdown(false));
const name = userData?.username && userData.username.trim() !== '' ? userData.username : `!${userData?.fid}`;
const pfpUrl = userData?.pfpUrl && userData.pfpUrl.trim() !== '' ? userData.pfpUrl : 'https://farcaster.xyz/avatar.png';
const name =
userData?.username && userData.username.trim() !== ''
? userData.username
: `!${userData?.fid}`;
const pfpUrl =
userData?.pfpUrl && userData.pfpUrl.trim() !== ''
? userData.pfpUrl
: 'https://farcaster.xyz/avatar.png';
return (
<div className="relative" ref={ref}>

View File

@ -1,13 +1,13 @@
'use client';
import { useCallback, useEffect, useState, useRef } from 'react';
import { cn } from '~/lib/utils';
import { Button } from '~/components/ui/Button';
import { ProfileButton } from '~/components/ui/NeynarAuthButton/ProfileButton';
import { AuthDialog } from '~/components/ui/NeynarAuthButton/AuthDialog';
import { useMiniApp } from '@neynar/react';
import sdk, { SignIn as SignInCore } from '@farcaster/frame-sdk';
import { useMiniApp } from '@neynar/react';
import { Button } from '~/components/ui/Button';
import { AuthDialog } from '~/components/ui/NeynarAuthButton/AuthDialog';
import { ProfileButton } from '~/components/ui/NeynarAuthButton/ProfileButton';
import { useQuickAuth } from '~/hooks/useQuickAuth';
import { cn } from '~/lib/utils';
type User = {
fid: number;
@ -142,7 +142,7 @@ export function NeynarAuthButton() {
const updateSessionWithSigners = useCallback(
async (
signers: StoredAuthState['signers'],
user: StoredAuthState['user'],
_user: StoredAuthState['user'],
) => {
if (!useBackendFlow) return;
@ -392,7 +392,7 @@ export function NeynarAuthButton() {
setMessage(result.message);
setSignature(result.signature);
// Use QuickAuth to sign in
const signInResult = await quickAuthSignIn();
await quickAuthSignIn();
// Fetch user profile after sign in
if (quickAuthUser?.fid) {
const user = await fetchUserData(quickAuthUser.fid);

View File

@ -1,9 +1,9 @@
'use client';
import { useCallback, useState } from 'react';
import sdk, { SignIn as SignInCore } from '@farcaster/miniapp-sdk';
import { Button } from '../Button';
import { SignIn as SignInCore } from '@farcaster/miniapp-sdk';
import { useQuickAuth } from '~/hooks/useQuickAuth';
import { Button } from '../Button';
/**
* SignIn component handles Farcaster authentication using QuickAuth.

View File

@ -34,25 +34,25 @@ interface UseQuickAuthReturn {
/**
* Custom hook for managing QuickAuth authentication state
*
*
* This hook provides a complete authentication flow using Farcaster's QuickAuth:
* - Automatically checks for existing authentication on mount
* - Validates tokens with the server-side API
* - Manages authentication state in memory (no persistence)
* - Provides sign-in/sign-out functionality
*
*
* QuickAuth tokens are managed in memory only, so signing out of the Farcaster
* client will automatically sign the user out of this mini app as well.
*
*
* @returns {UseQuickAuthReturn} Object containing user state and authentication methods
*
*
* @example
* ```tsx
* const { authenticatedUser, status, signIn, signOut } = useQuickAuth();
*
*
* if (status === 'loading') return <div>Loading...</div>;
* if (status === 'unauthenticated') return <button onClick={signIn}>Sign In</button>;
*
*
* return (
* <div>
* <p>Welcome, FID: {authenticatedUser?.fid}</p>
@ -63,17 +63,20 @@ interface UseQuickAuthReturn {
*/
export function useQuickAuth(): UseQuickAuthReturn {
// Current authenticated user data
const [authenticatedUser, setAuthenticatedUser] = useState<AuthenticatedUser | null>(null);
const [authenticatedUser, setAuthenticatedUser] =
useState<AuthenticatedUser | null>(null);
// Current authentication status
const [status, setStatus] = useState<QuickAuthStatus>('loading');
/**
* Validates a QuickAuth token with the server-side API
*
*
* @param {string} authToken - The JWT token to validate
* @returns {Promise<AuthenticatedUser | null>} User data if valid, null otherwise
*/
const validateTokenWithServer = async (authToken: string): Promise<AuthenticatedUser | null> => {
const validateTokenWithServer = async (
authToken: string,
): Promise<AuthenticatedUser | null> => {
try {
const validationResponse = await fetch('/api/auth/validate', {
method: 'POST',
@ -85,7 +88,7 @@ export function useQuickAuth(): UseQuickAuthReturn {
const responseData = await validationResponse.json();
return responseData.user;
}
return null;
} catch (error) {
console.error('Token validation failed:', error);
@ -102,11 +105,11 @@ export function useQuickAuth(): UseQuickAuthReturn {
try {
// Attempt to retrieve existing token from QuickAuth SDK
const { token } = await sdk.quickAuth.getToken();
if (token) {
// Validate the token with our server-side API
const validatedUserSession = await validateTokenWithServer(token);
if (validatedUserSession) {
// Token is valid, set authenticated state
setAuthenticatedUser(validatedUserSession);
@ -130,24 +133,24 @@ export function useQuickAuth(): UseQuickAuthReturn {
/**
* Initiates the QuickAuth sign-in process
*
*
* Uses sdk.quickAuth.getToken() to get a QuickAuth session token.
* If there is already a session token in memory that hasn't expired,
* it will be immediately returned, otherwise a fresh one will be acquired.
*
*
* @returns {Promise<boolean>} True if sign-in was successful, false otherwise
*/
const signIn = useCallback(async (): Promise<boolean> => {
try {
setStatus('loading');
// Get QuickAuth session token
const { token } = await sdk.quickAuth.getToken();
if (token) {
// Validate the token with our server-side API
const validatedUserSession = await validateTokenWithServer(token);
if (validatedUserSession) {
// Authentication successful, update user state
setAuthenticatedUser(validatedUserSession);
@ -155,7 +158,7 @@ export function useQuickAuth(): UseQuickAuthReturn {
return true;
}
}
// Authentication failed, clear user state
setStatus('unauthenticated');
return false;
@ -168,7 +171,7 @@ export function useQuickAuth(): UseQuickAuthReturn {
/**
* Signs out the current user and clears the authentication state
*
*
* Since QuickAuth tokens are managed in memory only, this simply clears
* the local user state. The actual token will be cleared when the
* user signs out of their Farcaster client.
@ -181,7 +184,7 @@ export function useQuickAuth(): UseQuickAuthReturn {
/**
* Retrieves the current authentication token from QuickAuth
*
*
* @returns {Promise<string | null>} The current auth token, or null if not authenticated
*/
const getToken = useCallback(async (): Promise<string | null> => {
@ -201,4 +204,4 @@ export function useQuickAuth(): UseQuickAuthReturn {
signOut,
getToken,
};
}
}