A web app in OCaml follow-up: sessions and deployment

Over the past few weeks, I spent a little bit of time working on my little pomodoro app; my next goal was to deploy this somewhere. After all, the point of this detour is to build up my intuition with OCaml, and that includes the tooling just as much as the language itself.

I wanted to make two updates before I felt ready to deploy: adding some kind of authentication so I could hide the app behind a login form, and bundling static assets into the final binary. Neither is strictly necessary to deploy, but, again, I'm trying to learn something here. What follows is a short description of the two new features and a brief mention of the deployment process.

adding authentication

One thing I knew I wanted before deploying was the ability to restrict who could see what. This isn't really necessary, since I'm not posing any sensitive information and also there's some amount of "security by obfuscation", but auth is something I've always felt uncomfortable about. This seemed like about as low-stakes as an app could get for me, so I think this makes for a good testbed for trying some stuff out.

All I really know about authentication is "store hashed passwords". Searching for more guidance on storing passwords led me to this OWASP cheat sheet on password storage which recommends Argon2 as a password hashing algorithm. At some point during a different line of searching, I stumbled across this post about using Dream and the application referenced in that post also uses Argon2, so I ended up copying their hashing functions pretty much verbatim. After putting together a little form and manually creating a username and password, I had a way to log into my application. As a hacky little way of enforcing authentication, when someone successfully logs in I add their username to the session and check for that field on each request. If there's no username in the session, the server responds with a redirect to the login page. It's not the most robust way of enforcing authentication, but it's something I can poke at and expand on and that's enough for now.

bundling assets

The other change I wanted to make was bundling everything into a single binary. Yet again, Dream has an example for this and it worked perfectly.

Using ocaml-crunch, I was able to compile my static asset (just one CSS file right now) into an OCaml module and use that to serve static assets. I initially settled on building this module outside of the build process since I don't really understand rules in dune. I think some of this confusion was from my assumption that the dune rule would generate a file that I treated as any other part of my code; it turns out that this isn't the case. I haven't gone too deep here, but from what I can tell dune is generating that module on the fly before compiling the rest of my code. By the time the compiler needs to know about the use of Static.read in bin/main.ml, everything is where it needs to be. My editor also stopped complaining that Static doesn't exist. I can see this tripping me up over the course of my using OCaml, but at least now I know it's a pattern and will hopefully remember to look out for it when reading code in the future.

deploying to fly.io

Fly.io is so nice. Once again, deploying to fly.io was utterly unremarkable. The only thing that made this process difficult is that I hadn't bothered getting my new computer set up to run Docker without sudo, which messed up the build process. Aside from that, I wrote a Dockerfile to copy my local assets (the sqlite database I've been working with and the binary built by dune) into an image and ran fly launch. I didn't bother actually building the project in Docker because I, uh, didn't feel like it. If there ends up being another post in this blog series, I imagine there will be a word or two about building things in a smarter way.

conclusion

I want to thank the lovely community at the Recurse Center for helping me understand authentication at a level beyond "let someone else do it". Once again, the Dream examples helped me out a great deal; I almost certainly would not have kept working on web apps in OCaml without those resources. I've also learned a bunch from this series on full-stack web dev with OCaml. It's always helpful to see how others approach similar things. I hope by writing these posts that I can provide one more data point for future Dreamers.