160 lines
4.8 KiB
TypeScript
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>
|
|
)
|
|
} |