laconic-deployer-frontend/apps/deploy-fe/src/components/DirectKeyAuth.tsx

160 lines
4.8 KiB
TypeScript

// src/components/DirectKeyAuth.tsx
'use client'
import { useState, useEffect } from 'react'
import { Button } from '@workspace/ui/components/button'
import { Wallet } from 'ethers' // Add this to your package.json if not already there
export function DirectKeyAuth() {
const [sessionStatus, setSessionStatus] = useState<'checking' | 'authenticated' | 'unauthenticated'>('checking')
const [sessionData, setSessionData] = useState<any>(null)
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
// Check if we already have a session
const checkSession = async () => {
try {
setSessionStatus('checking')
const response = await fetch('http://localhost:8000/auth/session', {
method: 'GET',
credentials: 'include',
})
if (response.ok) {
const data = await response.json()
setSessionStatus('authenticated')
setSessionData(data)
console.log('Session check successful:', data)
} else {
setSessionStatus('unauthenticated')
setSessionData(null)
console.log('Session check failed:', await response.text())
}
} catch (error) {
console.error('Error checking session:', error)
setSessionStatus('unauthenticated')
setSessionData(null)
}
}
// Check session on component mount
useEffect(() => {
checkSession()
}, [])
// Sign in with private key
const signInWithKey = async () => {
try {
setIsLoading(true)
setError(null)
// Create wallet from private key
const privateKey = '0x23ad64eabeba406086636c621893370c32d8678b5c879195ed4616e842b7aa42';
const wallet = new Wallet(privateKey);
// Get the address
const address = wallet.address;
console.log('Derived address:', address);
// Create SIWE message
const domain = window.location.host;
const origin = window.location.origin;
const nonce = Math.random().toString(36).slice(2);
const issuedAt = new Date().toISOString();
const message = `${domain} wants you to sign in with your Ethereum account:
${address}
Sign in With Ethereum.
URI: ${origin}
Version: 1
Chain ID: 1
Nonce: ${nonce}
Issued At: ${issuedAt}`;
console.log('Message to sign:', message);
// Sign the message
const signature = await wallet.signMessage(message);
console.log('Generated signature:', signature);
// Send to backend
const response = await fetch('http://localhost:8000/auth/validate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({
message,
signature
})
});
const responseData = await response.text();
console.log('Response data:', responseData);
if (response.ok) {
console.log('Authentication successful!');
await checkSession();
} else {
setError(`Authentication failed: ${responseData}`);
}
} catch (error) {
console.error('Error signing in with key:', error);
setError(`Error: ${error instanceof Error ? error.message : String(error)}`);
} finally {
setIsLoading(false);
}
};
return (
<div className="p-4 border rounded-md">
<h2 className="text-lg font-bold mb-4">Direct Key Authentication</h2>
<div className="mb-4">
<p className="text-amber-600 text-sm mb-2">
This component uses a local private key to sign messages directly, bypassing the wallet UI.
</p>
<Button
onClick={signInWithKey}
disabled={isLoading}
className="mb-2"
>
{isLoading ? 'Authenticating...' : 'Sign In With Private Key'}
</Button>
</div>
<div className="mb-4">
<h3 className="text-md font-semibold mb-2">Backend Session</h3>
<p className="mb-2">
Status:
<span className={
sessionStatus === 'authenticated' ? "text-green-500" :
sessionStatus === 'unauthenticated' ? "text-red-500" :
"text-yellow-500"
}>
{' '}{sessionStatus}
</span>
</p>
{sessionData && (
<div className="p-2 bg-gray-800 text-white rounded mb-2">
<pre className="font-mono text-sm overflow-auto max-h-32">{JSON.stringify(sessionData, null, 2)}</pre>
</div>
)}
<div className="mt-2">
<Button variant="outline" onClick={checkSession}>Check Session</Button>
</div>
</div>
{error && (
<div className="mt-2 p-2 bg-red-100 border border-red-300 text-red-800 rounded">
{error}
</div>
)}
</div>
)
}