feat(auth): Integrate Cloudflare Turnstile bot protection and enhance swarm tracking
- Add Cloudflare Turnstile integration for login and registration forms - Create turnstile verification utility with server-side token validation - Add turnstile_site_key and turnstile_secret_key fields to nodes table - Implement admin panel UI for configuring Turnstile keys in settings - Update login and register API routes to verify Turnstile tokens - Expose turnstile_site_key via GET /api/node endpoint for frontend - Add admin endpoint to save and update Turnstile configuration - Create remote_likes and remote_reposts tables for federated interaction tracking - Add swarm reply metadata fields (swarm_reply_to_id, swarm_reply_to_content, swarm_reply_to_author) to posts table - Add comprehensive TURNSTILE_SETUP.md documentation with setup instructions and security notes - Create database migration 0007 with schema updates and indexes - Protects against bot registrations and automated attacks on federated nodes
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { db, nodes } from '@/db';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
export async function verifyTurnstileToken(token: string, ip?: string): Promise<boolean> {
|
||||
try {
|
||||
// Get node settings to check if Turnstile is enabled
|
||||
const domain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:3000';
|
||||
const node = await db.query.nodes.findFirst({
|
||||
where: eq(nodes.domain, domain),
|
||||
});
|
||||
|
||||
// If no secret key is configured, skip verification (Turnstile is disabled)
|
||||
if (!node?.turnstileSecretKey) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Verify the token with Cloudflare
|
||||
const formData = new FormData();
|
||||
formData.append('secret', node.turnstileSecretKey);
|
||||
formData.append('response', token);
|
||||
if (ip) {
|
||||
formData.append('remoteip', ip);
|
||||
}
|
||||
|
||||
const response = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return data.success === true;
|
||||
} catch (error) {
|
||||
console.error('Turnstile verification error:', error);
|
||||
// On error, fail closed (reject the request)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTurnstileSiteKey(): Promise<string | null> {
|
||||
try {
|
||||
const domain = process.env.NEXT_PUBLIC_NODE_DOMAIN || 'localhost:3000';
|
||||
const node = await db.query.nodes.findFirst({
|
||||
where: eq(nodes.domain, domain),
|
||||
});
|
||||
|
||||
return node?.turnstileSiteKey || null;
|
||||
} catch (error) {
|
||||
console.error('Error fetching Turnstile site key:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user