Two credentials, two threat models: auth for a content API A developer building a headless content API called Depot explains the need for two distinct authentication strategies: slow bcrypt hashing for low-entropy human passwords and fast SHA-256 hashing for high-entropy machine tokens. The project uses session cookies for admin users and bearer tokens for delivery endpoints, with the rule of thumb being 'bcrypt for what humans choose, SHA-256 for what you generate.' A headless content API has two kinds of callers, and it's tempting to secure them the same way. That's the mistake. There's a human logging into an admin UI to edit content, and there's a machine — a website, a build step — pulling published content through a delivery endpoint. They authenticate with different credentials, and those credentials have opposite properties. Treat them identically and you either make the machine path painfully slow or the human path dangerously weak. I built a small headless content API Depot https://depot.vitaliipopov.dev/ partly to get this boundary right. Here's the reasoning. A session proves "this human is logged in." Short-lived, rides in an httpOnly cookie, checked on management routes. A delivery token proves "this machine may read this account's published content." Long-lived, sent as a Bearer header, checked on every public read. Two auth surfaces, kept explicit: / - requireUser — admin session httpOnly JWT cookie for the management API. - requireToken — a depot … bearer token for the public delivery API. / Here's the part people get wrong. Both credentials get stored as hashes — never plaintext — but not the same kind of hash , and the reason is entropy. Passwords are low-entropy. Humans pick summer2024 . An attacker who steals your DB will brute-force guesses against the stored hashes, so you want hashing to be deliberately slow — that's exactly what bcrypt's cost factor buys you: python import bcrypt from "bcryptjs"; const ROUNDS = 10; // deliberately slow — the point is to resist brute force export function hashPassword plain: string : Promise