Frontend Next Steps
Frontend Implementation - Security Integration
Status
Backend: ✅ Complete Frontend: 🔄 Needs Integration
The backend security layers are fully implemented and ready. The frontend needs these updates to work with the new security system.
Required Frontend Changes
Protected Endpoints That Need CSRF Tokens
The following endpoints now require CSRF tokens:
- Registration:
/api/v1/auth/register(also has honeypot field) - Login:
/api/v1/auth/login(also has rate limiting: 10/minute) - Lead Submission:
/api/v1/leads(also has honeypot, rate limiting: 5/hour, email verification)
1. Fetch CSRF Tokens (15 minutes)
File: frontend/html/app.js
Add this code:
// Global CSRF token storage
window.csrfToken = null;
// Fetch CSRF token on page load
async function initializeForm() {
try {
const response = await fetch('/api/v1/csrf-token', {
credentials: 'include' // Important: include cookies
});
const data = await response.json();
window.csrfToken = data.csrf_token;
console.log('CSRF token loaded');
} catch (error) {
console.error('Failed to load CSRF token:', error);
}
}
// Call on page load
window.addEventListener('DOMContentLoaded', initializeForm);
Note: This function should be called on ALL pages that have forms (registration, login, lead submission).
2. Include CSRF Token in ALL Form Submissions (20 minutes)
File: frontend/html/app.js
Update your registration, login, and lead submission functions:
Registration Function
async function registerBookkeeper(formData) {
// Ensure CSRF token is loaded
if (!window.csrfToken) {
throw new Error('Security token not loaded. Please refresh the page.');
}
const response = await fetch('/api/v1/auth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include', // Important: include cookies
body: JSON.stringify({
email: formData.email,
password: formData.password,
business_name: formData.business_name,
phone: formData.phone,
website: formData.website,
service_area: formData.service_area,
certifications: formData.certifications,
years_experience: formData.years_experience,
specializations: formData.specializations,
_csrf_token: window.csrfToken // ← Add this line
// Note: DO NOT include website_url (honeypot field)
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Registration failed');
}
return response.json();
}
Login Function
async function login(formData) {
// Ensure CSRF token is loaded
if (!window.csrfToken) {
throw new Error('Security token not loaded. Please refresh the page.');
}
const response = await fetch('/api/v1/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include', // Important: include cookies
body: JSON.stringify({
email: formData.email,
password: formData.password,
_csrf_token: window.csrfToken // ← Add this line
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Login failed');
}
return response.json();
}
Lead Submission Function
async function submitLead(formData) {
// Ensure CSRF token is loaded
if (!window.csrfToken) {
throw new Error('Security token not loaded. Please refresh the page.');
}
const response = await fetch('/api/v1/leads', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include', // Important: include cookies
body: JSON.stringify({
business_name: formData.business_name,
contact_name: formData.contact_name,
email: formData.email,
phone: formData.phone,
business_type: formData.business_type,
industry: formData.industry,
monthly_revenue_range: formData.monthly_revenue_range,
current_bookkeeping_situation: formData.current_bookkeeping_situation,
urgency: formData.urgency,
_csrf_token: window.csrfToken // ← Add this line
// Note: DO NOT include website (honeypot field)
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.detail || 'Submission failed');
}
return response.json();
}
3. Add Honeypot Fields to Forms (10 minutes)
Two forms need honeypot fields:
Registration Form
File: frontend/html/bookkeeper-landing.html
<!-- Honeypot field - hidden from users, catches bots -->
<input
type="text"
name="website_url"
id="website_url"
value=""
style="position: absolute; left: -9999px; width: 1px; height: 1px;"
tabindex="-1"
autocomplete="off"
aria-hidden="true"
>
Important: Make sure this field is NOT included in your registration JavaScript (don't read its value). The backend will reject registration if this field has any value.
Lead Submission Form
File: frontend/html/business-owner-form.html
<!-- Honeypot field - hidden from users, catches bots -->
<input
type="text"
name="website"
id="website"
value=""
style="position: absolute; left: -9999px; width: 1px; height: 1px;"
tabindex="-1"
autocomplete="off"
aria-hidden="true"
>
Important: Make sure this field is NOT included in your lead submission JavaScript (don't read its value). The backend will reject the submission if this field has any value.
4. Create Email Verification Page (30 minutes)
File: frontend/html/verify-lead.html (NEW)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Verify Email - Bookkeeper Platform</title>
<link rel="stylesheet" href="template/template.css">
<link rel="stylesheet" href="app.css">
</head>
<body>
<div class="container">
<div class="card card-large">
<h1>Email Verification</h1>
<div id="verification-message">
<p>Verifying your email address...</p>
</div>
</div>
</div>
<script>
// Get token from URL parameter
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');
if (!token) {
document.getElementById('verification-message').innerHTML = `
<p class="error">Invalid verification link.</p>
`;
} else {
// Verify email
fetch(\`/api/v1/leads/verify-email?token=\${token}\`)
.then(response => {
if (!response.ok) {
throw new Error('Verification failed');
}
return response.json();
})
.then(data => {
document.getElementById('verification-message').innerHTML = `
<p class="success">✓ Email verified successfully!</p>
<p>Your lead for <strong>\${data.business_name}</strong> is now pending admin review.</p>
<p>We'll contact you shortly with next steps.</p>
`;
})
.catch(error => {
document.getElementById('verification-message').innerHTML = `
<p class="error">Verification failed. The link may be expired or invalid.</p>
<p>Please submit your lead again if needed.</p>
`;
});
}
</script>
</body>
</html>
5. Update Form Submission Success Message (10 minutes)
File: frontend/html/app.js
Update the success message after lead submission to mention email verification:
// After successful lead submission
if (emailVerificationEnabled) { // You can check with config or feature flag
showSuccessMessage(`
Thank you for submitting your lead!
We've sent a verification email to ${email}.
Please check your inbox and click the link to verify your email address.
Your lead will be reviewed by our team after verification.
`);
} else {
showSuccessMessage(`
Thank you for submitting your lead!
Your request is now pending review by our team.
We'll be in touch shortly!
`);
}
Testing Your Implementation
Test 1: CSRF Token Works
- Open browser DevTools → Network tab
- Load the form page
- Check for request to
/api/v1/csrf-token - Verify
Set-Cookieheader withcsrf_token - Submit form
- Check request body includes
_csrf_token
Test 2: Honeypot Catches Bots
- Open browser console
- Manually fill the honeypot field:
document.getElementById('website').value = 'spam' - Try to submit form
- Should get error: "Invalid submission"
Test 3: Rate Limiting
- Submit lead form 5 times quickly
- 6th submission should fail with "Rate limit exceeded"
- Wait 1 hour or test from different IP
Test 4: Email Verification (If Enabled)
- Submit lead with real email
- Check email for verification link
- Click link → should redirect to
/verify-lead?token=... - Verify page shows success message
- Check admin dashboard → lead now appears
Configuration Toggles
Disable CSRF Protection (Development Mode)
To test with Swagger UI or disable CSRF:
# backend/.env
REQUIRE_CSRF=false
Then restart backend:
cd backend
docker-compose restart backend
Use Cases:
- Development/testing with Swagger UI at
/docs - Direct API testing with Postman/curl
- Should be set to
truein production
Disable Email Verification (Testing)
To disable email verification for testing:
# backend/.env
REQUIRE_EMAIL_VERIFICATION=false
Then restart backend:
cd backend
docker-compose restart backend
Error Handling
Add proper error messages for security failures:
async function submitLead(formData) {
try {
const response = await fetch('/api/v1/leads', { /* ... */ });
if (!response.ok) {
const error = await response.json();
// Handle specific security errors
if (error.detail.includes('CSRF')) {
alert('Security token expired. Please refresh the page and try again.');
window.location.reload();
return;
}
if (error.detail.includes('Rate limit')) {
alert('Too many submissions. Please try again later.');
return;
}
if (error.detail.includes('Invalid submission')) {
console.error('Honeypot triggered - possible bot');
return; // Don't show error to bot
}
throw new Error(error.detail);
}
return response.json();
} catch (error) {
console.error('Submission error:', error);
throw error;
}
}
Implementation Checklist
CSRF Integration
- Add CSRF token fetching on page load (all form pages)
- Include
_csrf_tokenin registration requests - Include
_csrf_tokenin login requests - Include
_csrf_tokenin lead submission requests
Honeypot Fields
- Add
website_urlhoneypot to registration form - Add
websitehoneypot to lead submission form - Ensure honeypot fields are NOT included in JavaScript submission
Email Verification
- Create email verification page (
verify-lead.html) - Update success messages to mention email verification
Error Handling
- Add error handling for CSRF failures
- Add error handling for rate limiting
- Add error handling for honeypot detection
Testing
- Test CSRF protection works (with REQUIRE_CSRF=true)
- Test Swagger UI works (with REQUIRE_CSRF=false)
- Test honeypot catches bot submissions
- Test rate limiting blocks excessive requests
- Test email verification flow (if enabled)
- Update user-facing documentation
Estimated Time
- CSRF Integration (3 endpoints): 35 minutes
- Honeypot Fields (2 forms): 10 minutes
- Email Verification Page: 30 minutes
- Error Handling: 15 minutes
- Testing: 45 minutes
Total: ~2.5 hours
Need Help?
- Backend security docs:
backend/SECURITY.md - API documentation:
http://localhost:8000/docs(when running) - Check browser console for CSRF token errors
- Check backend logs for security rejections
Next Steps After Frontend
- n8n Workflow: Create email verification workflow
- Cron Job: Schedule cleanup tasks (
app/tasks/cleanup.py) - Monitoring: Track spam attempts and adjust rate limits
- Production: Set
secure=Truefor CSRF cookies (HTTPS only)