Secrets, authentication, and plugins

Last month I released Biff v0.6.0, which primarily changed the way Biff does secrets management. The system map now includes a function, stored under the :biff/secret key, which takes a keyword – like :biff/jwt-secret, or :postmark/api-key – and returns the associated secret. This makes secrets management pluggable, since you can define the :biff/secret function however you like. It also makes it easier to not shoot yourself in the foot, since previously if you accidentally e.g. logged your system map or rendered it in one of your HTML templates, it would've exposed all your secrets.

The default :biff/secret implementation uses environment variables. Your config.edn file stores the name of the environment variable, like :postmark/api-key "POSTMARK_API_KEY", so a call to (secret :postmark/api-key) returns the value of the POSTMARK_API_KEY environment variable.

It might be an interesting exercise to modify Biff's server setup script so that it loads secrets from Doppler. Though if you're deploying to a regular VM/DigitalOcean droplet, I don't think that would actually provide any security benefits over storing the secrets in the secrets.env file. You still have to store the Doppler key on the filesystem somewhere. I think Doppler is mostly useful if you're deploying to a platform that can inject secrets into your environment at runtime (?). 


As of a few minutes ago, I have also released Biff v0.7.0. This one adds support for six-digit signin codes, and it restructures Biff's authentication code in general. Previously the authentication code was all included within the example project, in an auth.clj file that was copied into new projects. 

However, Biff already has a concept of feature maps, where you can define Reitit routes, scheduled tasks and such:

(def features
  {:routes [["/" {:get home-page}]
            ...]})

For a while I've thought it might be interesting to provide library functions that return these feature maps, with some configuration options as needed.

That's what I now do for authentication. There's a com.biffweb/authentication-plugin function which returns the relevant backend routes and schema. Now your project code only needs to define signin/signup forms and email templates (new projects come with default implementations).

(def features
  [app/features
   (biff/authentication-plugin {})
   home/features
   schema/features
   worker/features])

Aside: "feature maps" is an awkward term and I've decided it would make a lot more sense to just call these "plugins," since that's what they are. Coming soon in... Biff v0.7.1, perhaps.

Biff plugins are analogous to Firebase extensions. They're "pre-packaged solutions"/library functions that can define HTTP routes, store things in the database, create scheduled tasks and transaction listeners, etc. They're "large building blocks" that you can drop into your application.

I'm using the word "they" in a somewhat hypothetical sense, since there's currently only one Biff plugin that's defined as a library function. It could be interesting to write more! (I like the word "interesting" since it doesn't necessarily imply "useful," though it of course doesn't imply the absence of usefulness either.)

One possibility that immediately comes to mind is an RSS sync plugin. I've already written RSS functionality for Yakread, but wouldn't it be nifty if you could just add (my-rss-plugin {}) to your app, and then subscribe to feeds by writing a document to your database? Perhaps you create a {:xt/id ..., :com.myrssplugin/feed-url "https://example.com/feed.xml", ...} document, and then the plugin creates separate documents for all the feed items. Delete the feed document to unsubscribe.


Next on the docket, after I spend a few weeks on Yakread, I'll be working on Biff's deployment story. I'm going to create a 1-click install image for DigitalOcean, so you can deploy using that instead of running the server setup script yourself. I'd also like to write a how-to guide for deploying to DigitalOcean's container-based app platform. I still find all these "Heroku, but cheaper" solutions to be uncomfortably expensive compared to using a plain VM or two, but they probably make a lot more sense when you're scaling beyond a single developer/single web server. In any case, a how-to guide would be useful for anyone who wants to deploy Biff via containers, whether you're using DO's app platform or something else.

Thanks for reading, and thanks to everyone who sponsors Biff. Be sure to hop on #biff over at Clojurians Slack if you haven't already, and hit me up if you'd like to use Biff in your company.

Published by Jacob O'Bryant on 18 Feb 2023

Sign up for Biff: The Newsletter
Announcements, blog posts, et cetera et cetera.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.