firebase-hs: Firebase Auth, Firestore, and Servant integration for Haskell

[ authentication, bsd3, database, library, web ] [ Propose Tags ] [ Report a vulnerability ]

Firebase Authentication (JWT verification), Firestore REST API client (CRUD, queries, transactions), and optional WAI middleware and Servant auth combinator. Verify ID tokens against Google's public keys, read and write Firestore documents, and protect any Haskell web server with Firebase auth, all from pure, composable Haskell.


[Skip to Readme]

Flags

Manual Flags

NameDescriptionDefault
wai

Enable WAI auth middleware (Firebase.Auth.WAI)

Disabled
servant

Enable Servant auth combinator (Firebase.Servant)

Disabled

Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees

Candidates

  • No Candidates
Versions [RSS] 0.1.0.0, 0.1.1.0, 0.2.0.0, 0.3.0.0
Change log CHANGELOG.md
Dependencies aeson (>=2.0 && <2.4), base (>=4.18 && <5), base64-bytestring (>=1.2 && <1.3), bytestring (>=0.11 && <0.13), containers (>=0.6 && <0.9), crypton (>=0.31 && <2), http-client (>=0.7.13 && <0.8), http-client-tls (>=0.3 && <0.5), http-types (>=0.12 && <0.13), text (>=1.2 && <2.2), time (>=1.12 && <1.17), transformers (>=0.5 && <0.7) [details]
Tested with ghc >=9.6 && <9.7 || >=9.8 && <9.9 || >=9.10 && <9.11 || >=9.12 && <9.13
License BSD-3-Clause
Copyright 2026 Gondola Bros Entertainment
Author Devon Tomlin
Maintainer devon.tomlin@novavero.ai
Uploaded by aoinoikaz at 2026-07-29T13:57:55Z
Category Web, Authentication, Database
Home page https://github.com/Gondola-Bros-Entertainment/firebase-hs
Bug tracker https://github.com/Gondola-Bros-Entertainment/firebase-hs/issues
Source repo head: git clone https://github.com/Gondola-Bros-Entertainment/firebase-hs -b main
Distributions Stackage:0.3.0.0
Downloads 52 total (11 in the last 30 days)
Rating 2.0 (votes: 1) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2026-07-29 [all 1 reports]

Readme for firebase-hs-0.3.0.0

[back to package description]

firebase-hs

CI Hackage License

Firebase for Haskell:

  • Auth: Firebase ID token (JWT) verification against Google's public keys, with RS256 via crypton and automatic key caching
  • Firestore: CRUD, structured queries, and atomic transactions over the REST API
  • WAI / Servant: auth middleware and an auth combinator, each behind an optional cabal flag

Full API documentation lives on Hackage.

Install

build-depends: firebase-hs

The web integrations are off by default; enable the ones you use:

cabal build -f wai      # Firebase.Auth.WAI
cabal build -f servant  # Firebase.Servant

Auth

import Firebase.Auth

main :: IO ()
main = do
  cache <- newTlsKeyCache
  let cfg = defaultFirebaseConfig "my-project-id"
  result <- verifyIdTokenCached cache cfg tokenBytes
  case result of
    Left err   -> putStrLn ("Auth failed: " ++ show err)
    Right user -> putStrLn ("UID: " ++ show (fuUid user))

Build one KeyCache at startup and share it across threads; keys refresh automatically per Google's Cache-Control header.

A token is accepted only if every one of these holds:

Check Rule
Algorithm RS256 only
Signature Must match a Google public key
Issuer https://securetoken.google.com/<projectId>
Audience Must equal your Firebase project ID
Expiry / issued-at exp in the future, iat in the past, within clock skew
Subject sub non-empty (becomes the Firebase UID)

Roles live in custom claims, set by the Admin SDK's setCustomUserClaims:

if hasClaim "admin" user then handleAdmin user else refuse

Firestore

import qualified Data.Map.Strict as Map
import Firebase.Firestore

main :: IO ()
main = do
  fs <- newFirestore (ProjectId "my-project") (AccessToken "ya29...")

  let path = DocumentPath (CollectionPath "users") (DocumentId "alice")
  _ <- createDocument fs (CollectionPath "users") (DocumentId "alice")
         (Map.fromList [("name", StringValue "Alice"), ("age", IntegerValue 30)])
  _ <- updateDocument fs path ["age"] (Map.fromList [("age", IntegerValue 31)])
  doc <- getDocument fs path
  print doc

Build one Firestore handle and share it; it holds a pooled connection manager. Access tokens expire, so swap in a fresh one with withToken rather than rebuilding the handle.

Queries compose with (&); subcollections are addressed by path:

import Data.Function ((&))

result <- runQuery fs $
  query (CollectionPath "users")
    & where_ (fieldFilter "age" OpGreaterThan (IntegerValue 18))
    & orderBy "age" Ascending
    & limit 10

Transactions read with the transaction ID and return the writes to commit. On any failure the transaction is rolled back, and losing a contention race surfaces as TransactionAborted:

result <- runTransaction fs ReadWrite $ \txnId -> runExceptT $ do
  doc <- ExceptT (getDocumentInTransaction fs txnId path)
  pure [mkUpdateWrite (fsProject fs) path (applyDebit 100 (docFields doc))]

WAI

import Firebase.Auth (newTlsKeyCache, defaultFirebaseConfig)
import Firebase.Auth.WAI (requireAuth)
import Network.Wai.Handler.Warp (run)

main :: IO ()
main = do
  cache <- newTlsKeyCache
  let cfg = defaultFirebaseConfig "my-project-id"
  run 3000 (requireAuth cache cfg myApp)

firebaseAuth additionally stores the verified FirebaseUser in the request vault for lookupFirebaseUser to read downstream.

Servant

import Firebase.Auth (newTlsKeyCache, defaultFirebaseConfig)
import Firebase.Servant (firebaseAuthHandler)
import Servant.Server (Context (..))

main :: IO ()
main = do
  cache <- newTlsKeyCache
  let cfg = defaultFirebaseConfig "my-project-id"
      ctx = firebaseAuthHandler cache cfg :. EmptyContext
  runSettings defaultSettings (serveWithContext api ctx server)

Build and Test

cabal build all -f wai -f servant --enable-tests --ghc-options="-Werror"
cabal test

BSD-3-Clause. Maintained by Gondola Bros Entertainment.