Auth

When you own the users, Std.Auth is the default: bcrypt password hashing, JWT cookies, and database-backed register/login — no separate auth service, no hand-rolled crypto.

import Std.Auth as Auth

Register and log in

register and login touch the database, so they're Tasks. If you don't have a users table, register creates one (id, email, password_hash, role, created_at):

signUp : Db -> Task Error String
signUp db =
    Auth.register db "alice@example.com" "correct horse battery staple"
        |> Task.andThen (\_ -> Auth.login db "alice@example.com" "correct horse battery staple")
        |> Task.andThen
            (\user ->
                -- issue a signed session token, valid 24h
                case Auth.signToken sessionSecret user 86400 of
                    Ok token -> Task.succeed token
                    Err e -> Task.fail e
            )

Hashing and verifying directly

If you manage your own user rows, the primitives are pure-fallible (Result), so they're easy to test:

-- Auth.hashPassword   : String -> Result Error String       (bcrypt)
-- Auth.verifyPassword : String -> String -> Result Error Bool (constant-time)

checkLogin : String -> String -> Bool
checkLogin password storedHash =
    Result.withDefault False (Auth.verifyPassword password storedHash)

Tokens

signToken and verifyToken are an HMAC-SHA256 JWT pair. signToken takes the secret, your claims (a record or dict), and an expiry in seconds; verifyToken decodes back into whatever type the call site annotates:

-- Auth.signToken   : String -> a -> Int -> Result Error String
-- Auth.verifyToken : String -> String -> Result Error a

Two rules

For OAuth (Google/GitHub) or an external provider (Auth0/Clerk), reach past Std.Auth only when the product needs it — otherwise the built-in module is the secure, reviewed default. Full surface: the Std.Auth guide.

Next → Deploying