Hi all, apologies for the slightly provocative title.
I’m working on an application (eventually OSS) whose backend is written in Haskell. The app will deal with very sensitive user data, so one of my main design goals is security-first. That means conforming to modern security best practices. I don’t have a lot of experience with Auth in any language, and it feels like one of those things that will be easy to mess up if I try to roll my own, at any level.
Another core design goal is flexibility in deployment. I want to (eventually) support a wide range of deployment scenarios and app clients, such as:
Deployment cases:
- self-hosted locally, and potentially offline
- self-hosted in the cloud, either single- or mult-user;
- entirely cloud-hosted, SaaS style
Possible clients:
- browser-based UI
- CLI and TUI for power users/scripting
- mobile app (eventually).
Ideally I’d like to support all of these using one build, with only configuration tweaks (“build once, deploy anywhere”). Servant, Haskell, Nix and Docker are all gifts from the gods when it comes to that flexibility.
Since authentication and authorisation are vital to practically any application, I hoped it wouldn’t be difficult to find a “plug-and-play” solution that would fit my needs of a modern security-first project:
- Login + registration
- Secure session cookies (HTTP-only, SameSite, CSRF protection)
- JWTs
- Refresh tokens
- Role-based access control
- Integration with an Identity Provider (e.g. Keycloak or Authentik)
- Support for OAuth2, OIDC, SSO, LDAP etc.
I’ve been surveying the Haskell ecosystem (and the ecosystem of IAM/IDP providers) for the last week, but surprisingly I haven’t found what I’m looking for. I’ve also found very few complete examples or tutorials for building a modern secure auth flow in Haskell. Here are some of the options I looked at, and their issues as I see them:
-
servant-auth-server
- Quite low level
- It gives you JWT and cookie primitives, but not much else
- Doesn’t seem to support refresh tokens
- Automatically sends new cookies after a user accesses a protected route, with minimal configuration options. It also seems to use the expiry time from the
CookieSettings
in theContext
, which is only passed once at the time when the server is started. - I haven’t found documentation or guidance for how to use the library to build a secure, production-ready system
-
OAuth2/OIDC support (
hoauth2
,openid-connect
,oidc-client
)- Sparse documentation
- Only support one or two authorisation flows
- Don’t seem to be very actively maintained
- Not clear how to integrate with Servant or other libraries
-
yesod-auth
- Seems to be the most actively maintained, but probably impossible to use without committing to Yesod entirely
-
Reverse proxy setups (e.g. Traefik + oauth2-proxy + Keycloak)
- Not in Haskell-land any more, but I can live with that
- No documentation for use with Haskell backend, nor any Haskell libraries or sdks
- It’s unclear how I could integrate such setups cleanly with e.g. Servant backends
So, for now I’m a bit stuck. If you’ve built something that involves any of the above, I’d love to know:
- Are there any open-source Haskell projects I can learn from, which conform to modern security best practices?
- How do you structure secure login/session/token logic in your Haskell apps?
- If you’re using Keycloak/Auth0/etc., how do you interface with it from your Haskell backend?
- Is anyone using
hoauth2
,oidc-client
, oropenid-connect
in production? - Am I missing a “standard” Haskell approach to this problem?
For the record: as I mentioned, I’ve only been looking into this for the last week or so. I would love to be proven wrong; for someone to say “no, idiot, there’s a brilliant batteries-included production-ready library that fits your needs perfectly”. If there’s an aeson
or optparse-applicative
equivalent for Auth in the Haskell ecosystem that for some reason isn’t showing up in my Google results, then I’d love to hear about it.
Thanks in advance!