This document outlines all security measures implemented in the PhonkDrift Admin Dashboard. The application follows industry best practices to protect admin credentials, prevent unauthorized access, and secure all data transmissions.
localStorage is vulnerable to XSS attacks. If malicious JavaScript runs on the page, it can access and steal tokens from localStorage.
HttpOnly cookies are secure because:
- ✅ JavaScript cannot access them via
document.cookie - ✅ Browser automatically includes them in requests
- ✅ Cannot be stolen by XSS attacks
- ✅ Server can only read them server-side
// client: POST /api/auth/login
// server: app/api/auth/login/route.ts
const response = NextResponse.json({ success: true })
response.cookies.set({
name: 'admin_token',
value: data.session.access_token,
httpOnly: true, // ✅ JS cannot read this
secure: production, // ✅ HTTPS only in production
sameSite: 'strict', // ✅ CSRF protection
maxAge: 86400, // ✅ 24 hours
path: '/',
})
return response// Include credentials: 'include' to send HttpOnly cookies
fetch('/api/auth/login', {
method: 'POST',
credentials: 'include', // ✅ Automatically send HttpOnly cookies
})
// Your Go backend receives the token in the Cookie header
// Cookie: admin_token=<jwt>An attacker tricks your browser into making requests to PhonkDrift while you're logged in.
response.cookies.set({
sameSite: 'strict', // ✅ Cookie only sent to same-site requests
})SameSite=Strict prevents cookies from being sent to cross-site requests:
- ✅ Request from
phonkdrift.com→ Cookie is sent - ✅ Request from
evil.comtophonkdrift.com→ Cookie is NOT sent
File: middleware.ts
export function middleware(req: NextRequest) {
const token = req.cookies.get('admin_token')?.value
// If no token, redirect to login
if (!token) {
return NextResponse.redirect(new URL('/login', req.url))
}
return NextResponse.next()
}
// Protect all /dashboard and /api/admin routes
export const config = {
matcher: ['/dashboard/:path*', '/api/admin/:path*'],
}What this does:
- ✅ Checks for valid token before accessing protected routes
- ✅ Redirects unauthenticated users to login
- ✅ Runs on every request (server-side, cannot be bypassed)
File: app/api/auth/login/route.ts
const adminEmail = process.env.NEXT_PUBLIC_ADMIN_EMAIL
if (email !== adminEmail) {
console.error(`[Auth] Unauthorized login attempt from: ${email}`)
return NextResponse.json(
{ error: 'Access denied - invalid credentials' },
{ status: 403 }
)
}Security Notes:
- ✅ Only the email in
NEXT_PUBLIC_ADMIN_EMAILcan access the dashboard - ✅ Failed attempts are logged
- ✅ Response is generic ("invalid credentials") to not reveal if email exists
The password is NEVER stored in your Next.js app. Instead:
- Client sends email + password to
/api/auth/login - Server verifies with Supabase Auth (password hashing done by Supabase)
- Supabase returns JWT token (never sent to client)
- Server stores JWT in HttpOnly cookie
Benefits:
- ✅ Passwords are hashed using industry-standard algorithms (bcrypt)
- ✅ Password never reaches your backend
- ✅ Supabase handles security updates
In production, ensure:**
// Cookies are HTTPS-only
secure: process.env.NODE_ENV === 'production' // ✅ true in production
// Environment variable in .env.production
NEXT_PUBLIC_BACKEND_URL=https://yourdomain.comWhy HTTPS?
- ✅ Encrypts all traffic between client and server
- ✅ Prevents man-in-the-middle attacks
- ✅ Cookie is not sent over unencrypted HTTP
Setup on Vercel:
- Deploy to Vercel (HTTPS automatic)
- Set
NODE_ENV=production - All cookies use
secure: trueflag
Your Go backend must be configured to accept requests ONLY from your frontend domain:
// Go backend example
cors := handlers.CORS(
handlers.AllowedOrigins([]string{"https://yourdomain.com"}),
handlers.AllowedMethods([]string{"GET", "POST", "PUT", "DELETE"}),
handlers.AllowedHeaders([]string{"Content-Type", "Authorization"}),
handlers.AllowCredentials(),
)
router.Use(cors)What this does:
- ✅ Backend only accepts requests from your frontend domain
- ✅ Prevents attackers from calling your API directly
- ✅ Credentials (cookies) only sent to your domain
In your Go backend:
func VerifyAdminToken(token string) (bool, error) {
// Parse and validate JWT
claims := jwt.MapClaims{}
parsedToken, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
return []byte(os.Getenv("JWT_SECRET")), nil
})
if err != nil || !parsedToken.Valid {
return false, errors.New("invalid token")
}
// ✅ Token is valid, process request
return true, nil
}Never trust client-side validation. Always verify on the server.
Add rate limiting to your backend:
// Go rate limit example using github.com/go-chi/chi/middleware
import "github.com/go-chi/chi/middleware"
router.Use(middleware.Throttle(100)) // 100 requests per second
// Or use a dedicated service
// - Vercel Edge Middleware
// - Cloudflare
// - AWS WAFFile: app/api/auth/login/route.ts
All auth events are logged:
console.log(`[Auth] Successful login for: ${email}`)
console.error(`[Auth] Unauthorized login attempt from: ${email}`)
console.error(`[Auth] Login failed for ${email}: ${error.message}`)Logs should include:
- ✅ Timestamp
- ✅ Username/Email
- ✅ Success/Failure
- ✅ IP Address (add later if needed)
DO NOT include these in git:
# ❌ WRONG: Hardcoded in code
const ADMIN_EMAIL = "admin@example.com"
# ✅ RIGHT: Environment variable
NEXT_PUBLIC_ADMIN_EMAIL=admin@example.com- Go to Settings → Environment Variables
- Add:
NEXT_PUBLIC_ADMIN_EMAIL=your-email@example.comNEXT_PUBLIC_AUTH_SUPABASE_URL=your-supabase-urlNEXT_PUBLIC_AUTH_SUPABASE_ANON_KEY=your-keyNEXT_PUBLIC_BACKEND_URL=https://backend.example.com
- Variables sync across all deployments
File: app/api/auth/logout/route.ts
response.cookies.set({
name: 'admin_token',
value: '', // ✅ Clear the value
maxAge: 0, // ✅ Expires immediately
})Logout Process:
- User clicks "Logout"
- Client calls
POST /api/auth/logout - Server clears cookies
- Client redirects to
/login
Before deploying to production, ensure:
- All environment variables are set in Vercel
-
NEXT_PUBLIC_ADMIN_EMAILis configured - Backend is HTTPS only
- Backend CORS allows only your frontend domain
- Backend verifies JWT tokens on every request
- Cookies use
secure: true(automatic with NODE_ENV=production) - HTTPS certificate is valid
- Rate limiting is configured on backend
- Logging/monitoring is enabled
- Regular security audits scheduled
- Database backups are automated
- Incident response plan is documented
Add to next.config.mjs:
const securityHeaders = [
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'",
},
]
module.exports = {
headers: async () => [{ source: '/:path*', headers: securityHeaders }],
}{
key: 'X-Frame-Options',
value: 'DENY', // Prevent page from being embedded in iframes
}{
key: 'X-Content-Type-Options',
value: 'nosniff', // Prevent MIME sniffing
}Complete next.config.mjs:
/** @type {import('next').NextConfig} */
const nextConfig = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-XSS-Protection',
value: '1; mode=block',
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
],
},
]
},
}
export default nextConfig-
Immediately revoke all sessions:
- Change JWT secret
- All existing tokens become invalid
- Users must re-login
-
Audit logs:
- Check who accessed what data
- Identify the scope of breach
-
Notify users:
- Send security alert email
- Force password reset
- Enable 2FA (if applicable)
-
Fix the vulnerability:
- Identify root cause
- Implement fix
- Test thoroughly
- Deploy
-
Monitor for reoccurrence:
- Set up alerts
- Regular security audits
- Penetration testing
- Review auth logs
- Check for failed login attempts
- Monitor for unusual activity
- Update dependencies
- Run security audits (
npm audit) - Review access logs
- Penetration testing
- Security review with team
- Update security headers
- Full security audit
- Update security policies
- Compliance check (SOC 2, etc.)
- OWASP Authentication Cheat Sheet
- MDN: HTTP Cookies
- OAuth 2.0 Security Best Current Practice
- Supabase Auth Documentation
- Next.js Security Best Practices
For security issues, contact: security@phonkdrift.com (do not publicly disclose)
For general questions, open an issue in the repository.
Last Updated: June 2026 Status: Production Ready ✅