---
title: "09 — Password Hashing and Federation — Storing Secrets, Delegating Identity"
uid: password-hashing-and-federation
tags: ["sso", "security", "roadmap:backend", "scrypt", "saml", "md5", "sha", "bcrypt", "oauth"]
excerpt: "Hashing is a one-way race the server must deliberately lose slowly; OAuth and SAML are two dialects of one idea — delegate identity to a party both sides already trust."
date: 2026-08-13T03:28:26+0000
source: https://www.aveshina.my.id/en/blog/password-hashing-and-federation
---

MD5, SHA, scrypt, bcrypt, OAuth, SAML — these looked like unrelated trivia scattered through the caching section of the roadmap. Writing them down split them into two clean ideas: **password hashing is a one-way race the server must deliberately lose slowly, and OAuth/SAML are two dialects of one idea — delegate identity to a party both sides already trust.** [1]

The two halves sound unrelated but they answer the same question from opposite directions: *how do I prove who I am without handing over the secret?* Hashing answers it for passwords you store yourself — never store the password, store a fingerprint you can verify but can't reverse. Federation answers it by refusing to handle the secret at all — let someone else verify the user and hand you a token. Both keep the actual secret out of your database.

## Hashing: one-way, deterministic, and the slow kind

A **hash function** takes any input and produces a fixed-size digest — MD5 produces 128 bits, SHA-256 produces 256 bits — such that the same input always yields the same digest, but the digest cannot be reversed back to the input [3][4]. Two properties define a cryptographic hash:

- **One-way (preimage-resistant).** Given a digest, you cannot compute the input.
- **Collision-resistant.** You cannot find two different inputs that hash to the same digest.

For password storage, the use is direct: instead of storing password123, store hash(password123). On login, hash what the user typed and compare digests. If the database leaks, the attacker has hashes, not passwords.

Here's the part I had to internalize: **not all hash functions are suitable for passwords.** General-purpose hashes (MD5, SHA-256) are designed to be *fast* — that's their job for integrity checking. But for passwords, fast is the enemy. An attacker with a leaked hash will try billions of candidate passwords per second; a fast hash means billions of guesses per second per GPU.

**MD5 is broken** — collision vulnerabilities mean it's unsuitable for anything security-critical, and it's far too fast for passwords [2]. Never use MD5 for passwords.

**SHA-2 / SHA-3** are still cryptographically sound for integrity and signatures, but they're also too fast for password storage. SHA is not a password hash.

For passwords, you want **password hashes** — functions deliberately designed to be slow and memory-hard:

- **bcrypt** — based on the Blowfish cipher, with a tunable cost factor and built-in salt [5]. The cost factor doubles the work each time you raise it, so you can keep pace with hardware. bcrypt is the battle-tested default for password storage.
- **scrypt** — a memory-hard KDF (key-derivation function) designed to resist GPUs and ASICs by requiring large amounts of memory per hash, making hardware attacks expensive [6]. Where bcrypt raises CPU cost, scrypt raises memory cost.

The way of thinking: **a password hash must be slow on purpose.** A login taking 250ms is fine for a human and devastating for an attacker brute-forcing billions of guesses. The server must *lose* the race to compute the hash, slowly, on purpose.

## Salt: defeating rainbow tables

A **salt** is random bytes added to the password before hashing, stored alongside the hash. Its job is to ensure that two users with the same password get different hashes — defeating **rainbow tables** (precomputed tables of hash→password mappings).

Without salt, password123 hashes to the same digest for every user, so an attacker who leaks the DB instantly finds every user sharing the most common password. With a unique salt per user, each hash is unique even for identical passwords, and the attacker has to brute-force each one independently. bcrypt and scrypt build the salt in; with raw SHA you'd have to handle it yourself (another reason not to use raw SHA).

## OAuth 2.0: delegation of authorization

**OAuth 2.0** is the authorization protocol that lets a user grant a third-party application limited access to their resources on another service, without handing the application their password [7]. The classic flow: "Allow this app to read your GitHub repos." The user authenticates with GitHub (the authorization server), GitHub issues the app an **access token**, and the app uses that token to call GitHub's API on the user's behalf.

The key idea is **delegated authorization via tokens**. The app never sees the user's GitHub password; it sees a token that says "GitHub authorized this app to do these specific things." Tokens can be scoped (read-only, one repo) and revoked (the user can kill the app's access without changing their password).

OAuth 2.0 is authorization, not authentication — it answers "what can this app do?", not "who is the user?" That's a common confusion. Authentication on top of OAuth (confirming who the user is) is what **OpenID Connect** adds.

## SAML: enterprise federation in XML

**SAML** (Security Assertion Markup Language) is the older, XML-based federation standard common in enterprise [8]. Where OAuth/OIDC is the modern web's federation story (JSON, JWTs, REST), SAML dominates enterprise single sign-on — the "log in with your corporate identity" flow at universities and large companies.

A SAML flow: the user tries to access a service provider (SP); the SP redirects to an identity provider (IdP); the user authenticates at the IdP; the IdP posts a signed XML **assertion** back to the SP asserting who the user is. The SP trusts the assertion because it trusts the IdP's signing certificate.

SAML and OAuth/OIDC solve overlapping problems — both let you delegate identity/authorization to a trusted third party — but in different ecosystems. SAML is XML, enterprise, browser-redirect-based, and predates the modern web. OAuth/OIDC is JSON, API-first, and what new federations use. The roadmap lists both because both exist in production; which one I meet depends on whether the integration partner is an enterprise (SAML) or a modern service (OAuth).

```figure
<svg viewBox="0 0 740 260" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="Two side-by-side federation flows. Left: OAuth 2.0 — user → authorization server (issues access token) → resource server (accepts token). Right: SAML — user → identity provider (signs XML assertion) → service provider (verifies assertion). Both labelled 'delegate identity to a trusted third party'.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- LEFT: OAuth -->
    <text x="185" y="28" font-size="13" font-weight="700" fill="#1e1b4b" text-anchor="middle">OAuth 2.0 — JSON, modern web</text>
    <rect x="40" y="50" width="90" height="40" rx="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="85" y="74" font-size="11" font-weight="700" fill="#1e1b4b" text-anchor="middle">user</text>
    <rect x="180" y="50" width="120" height="40" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="240" y="70" font-size="10" font-weight="700" fill="#422006" text-anchor="middle">authorization</text>
    <text x="240" y="82" font-size="10" fill="#422006" text-anchor="middle">server → token</text>
    <rect x="350" y="50" width="100" height="40" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="400" y="74" font-size="10" font-weight="700" fill="#052e16" text-anchor="middle">resource API</text>
    <line x1="130" y1="70" x2="178" y2="70" stroke="#64748b" stroke-width="1.5"/>
    <line x1="300" y1="70" x2="348" y2="70" stroke="#64748b" stroke-width="1.5"/>

    <!-- RIGHT: SAML -->
    <text x="595" y="28" font-size="13" font-weight="700" fill="#500724" text-anchor="middle">SAML — XML, enterprise</text>
    <rect x="450" y="50" width="90" height="40" rx="8" fill="#fce7f3" stroke="#db2777" stroke-width="1.5"/>
    <text x="495" y="74" font-size="11" font-weight="700" fill="#500724" text-anchor="middle">user</text>
    <rect x="560" y="50" width="100" height="40" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="610" y="70" font-size="10" font-weight="700" fill="#422006" text-anchor="middle">identity</text>
    <text x="610" y="82" font-size="10" fill="#422006" text-anchor="middle">provider → XML</text>
    <rect x="680" y="50" width="50" height="40" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="705" y="74" font-size="10" font-weight="700" fill="#052e16" text-anchor="middle">SP</text>
    <line x1="540" y1="70" x2="558" y2="70" stroke="#64748b" stroke-width="1.5"/>
    <line x1="660" y1="70" x2="678" y2="70" stroke="#64748b" stroke-width="1.5"/>

    <!-- common caption -->
    <rect x="120" y="150" width="500" height="60" rx="10" fill="#f8fafc" stroke="#cbd5e1" stroke-width="1.5"/>
    <text x="370" y="175" font-size="12" font-weight="700" fill="#1e293b" text-anchor="middle">Both: delegate identity to a trusted third party</text>
    <text x="370" y="195" font-size="10" fill="#64748b" text-anchor="middle">the app never sees the user's password — only a signed token/assertion it can verify</text>

    <line x1="240" y1="90" x2="290" y2="150" stroke="#cbd5e1" stroke-width="1"/>
    <line x1="610" y1="90" x2="450" y2="150" stroke="#cbd5e1" stroke-width="1"/>
  </g>
</svg>
```

## The two halves, together

What unifies the hashing half and the federation half is the principle: **the secret never lives where it can be leaked.** Password hashing keeps the plaintext out of the database (the DB can leak and passwords survive). Federation keeps the plaintext out of your system entirely (you never see it; the IdP does). Both are defenses against the same risk — the database breach — approached from two different starting points.

The decision shape:

- **You store users and passwords → hash them with bcrypt (or scrypt/argon2).** Never MD5, never raw SHA, always salted.
- **You want "sign in with X" → OAuth 2.0 / OpenID Connect.** The modern web default.
- **Enterprise partner mandates SSO → SAML.** Common in corporate/education integrations.

## How I use this

Two habits capture the practice. For password storage, I never roll my own hashing — I use the platform's bcrypt/argon2 implementation and I set the cost factor so a single hash takes ~250ms on production hardware, re-tuning every couple of years as hardware speeds up. For identity, I default to OAuth via a provider (Supabase, Auth0, the platform's built-in) rather than building custom password flows, because federation removes the password database as an attack surface entirely. The portfolio uses Google OAuth through Supabase for exactly this reason — the passwords I don't store are passwords I can't leak.

The framing — *hashing loses slowly, federation delegates* — is what keeps me from reaching for the wrong tool. MD5 is fast and broken; SHA is fast and for integrity; bcrypt is slow and for passwords. OAuth is for the modern web; SAML is for the enterprise. Picking by purpose, not by familiarity, is the whole skill.

## References

[1] roadmap.sh, "Backend Roadmap — Security nodes." [Online]. Available: [https://roadmap.sh/backend](https://roadmap.sh/backend)

[2] "MD5," Wikipedia. [Online]. Available: [https://en.wikipedia.org/wiki/MD5](https://en.wikipedia.org/wiki/MD5)

[3] "What is SHA?," Encryption Consulting. [Online]. Available: [https://www.encryptionconsulting.com/education-center/what-is-sha/](https://www.encryptionconsulting.com/education-center/what-is-sha/)

[4] "Why is MD5 not safe?," InfoSec Scout. [Online]. Available: [https://infosecscout.com/why-md5-is-not-safe/](https://infosecscout.com/why-md5-is-not-safe/)

[5] "Understanding bcrypt," Auth0. [Online]. Available: [https://auth0.com/blog/hashing-in-action-understanding-bcrypt/](https://auth0.com/blog/hashing-in-action-understanding-bcrypt/)

[6] "scrypt," Wikipedia. [Online]. Available: [https://en.wikipedia.org/wiki/Scrypt](https://en.wikipedia.org/wiki/Scrypt)

[7] "An Introduction to OAuth 2," DigitalOcean. [Online]. Available: [https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2](https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2)

[8] "SAML Explained in Plain English," OneLogin. [Online]. Available: [https://www.onelogin.com/learn/saml](https://www.onelogin.com/learn/saml)

```quiz
Q: Why are MD5 and raw SHA-256 unsuitable for password storage?
- They are too fast — an attacker with a GPU can try billions of guesses per second
- They produce output that is too short to be unique
correct: 0
explain: MD5 and SHA are designed to be fast, which is catastrophic for password hashing — fast means billions of brute-force guesses per second on modern GPUs. Password hashing requires deliberately slow, memory-hard functions (bcrypt, scrypt, argon2).

Q: What is the purpose of a salt in password hashing?
- To ensure two users with the same password get different hashes, defeating rainbow tables
- To encrypt the password so it can be decrypted later
correct: 0
explain: A salt is random bytes added per-user before hashing so identical passwords yield different digests. This defeats precomputed rainbow tables. Hashing is one-way — the password is never decryptable, only verifiable.

Q: bcrypt and scrypt are designed to resist different attack vectors. What does each raise to make attacks expensive?
- bcrypt raises CPU cost (tunable); scrypt raises memory cost (memory-hard)
- Both raise only CPU cost identically
correct: 0
explain: bcrypt's adaptive cost factor increases CPU work over time. scrypt additionally requires large amounts of memory per hash, making GPU/ASIC attacks prohibitively expensive. Both are deliberately slow on purpose.

Q: OAuth 2.0 is primarily an authorization protocol. What question does it answer, and what does the app never see?
- It answers 'what can this app do?'; the app never sees the user's password
- It answers 'who is the user?'; the app sees the user's password
correct: 0
explain: OAuth 2.0 grants scoped, revocable access tokens. The user authenticates with the authorization server; the app gets a token, never the password. Authentication ('who is the user?') is the separate OpenID Connect layer.

Q: What do SAML and OAuth/OpenID Connect have in common, despite their format differences?
- Both delegate identity to a trusted third party and keep the secret out of the relying party's system
- Both require the app to store the user's password
correct: 0
explain: SAML (XML, enterprise) and OAuth/OIDC (JSON, modern web) are dialects of the same idea: a trusted third party authenticates the user and hands the app a signed assertion/token. The app never holds the password. The difference is ecosystem, not principle.
```
