Authentication
1. Back-end authorization challenges
The biggest challenge on the back end was separating authentication from authorization and making sure I enforced authorization everywhere it mattered. Logging users in and generating tokens wasn't too bad, but I had to be careful to check that only the user who created a book or author could edit or delete it. This meant explicitly comparing the logged-in user's ID with the stored user_id on each resource, which was easy to miss at first and required some cleanup once I noticed gaps.
2. Front-end authorization challenges
On the front end, the main challenge was handling authorization without relying on the UI for security. I used the username stored in cookies to conditionally show edit and delete buttons, but I still had to assume users could hit endpoints directly. Because of that, I focused on handling authorization errors coming back from the server instead of assuming requests would always succeed.
Deployment
1. Deploying the app publicly
Deployment was the most challenging part of the assignment. The biggest hurdle was realizing that a working app on my laptop didn't mean it would automatically work on a public server. I had to build the React app for production, move the compiled files into the back end, and serve them with Express instead of using the Vite dev server.
React Router caused issues with page refreshes at first, since direct navigation to routes returned 404s. Fixing this required adding a wildcard route to always serve the same index.html file. Setting up Caddy as a reverse proxy and dealing with DNS and HTTPS configuration also took some trial and error, especially when certificate issues came up because of missing subdomain records.
Security Audit
1. XSS
I didn't run into any XSS issues in this app mainly because everything is rendered through React, which escapes values by default. I never needed to render raw HTML or use dangerouslySetInnerHTML, so user input is always treated as plain text. Because of that, there wasn't much extra work needed here beyond being careful about how data is rendered.
2. CSRF
Since authentication is handled with cookies, CSRF was something I had to think about. I didn't add explicit CSRF tokens, but I did reduce the risk by using sameSite cookies and only accepting JSON requests for actions that modify data. For the scope of this app, that felt sufficient, but I can see how CSRF tokens would be necessary in a more production-level setup.
3. Rate limiting
I didn't add rate limiting for this assignment. At the time, it felt outside the core goals of the project, but I did look into how it could be added either with middleware like express-rate-limit or at the server level using firewall tools. That's something I would consider if this app were exposed to heavier traffic.
4. HTTP headers
I didn't explicitly configure custom security headers beyond what Express provides by default. While working on the deployment, I learned more about headers like Content-Security-Policy and X-Content-Type-Options and how they help prevent common attacks. These would be easy additions if I were hardening the app further.
5. Other security considerations
Beyond that, I focused on keeping the basics solid. All input is validated with Zod, request body sizes are limited, passwords are hashed with Argon2, and I avoided storing sensitive information in local storage. These choices helped keep the app reasonably secure without adding unnecessary complexity.