Stage 7 · Master
Authentication and Authorization
Password Hashing and Issuing JWTs
The conceptual core of backend authentication: why passwords must be stored as slow hashes, how JWTs turn expensive login into cheap request-time verification, and how Meridian's planned identity-service/Auth0 strategy should be understood without pretending it already exists in code.
Authentication Before Authorization
Authentication answers 'who is the caller?' Authorization answers 'what may that caller do?' The order matters. Permissions only make sense after identity is established. This seems simple, but many immature systems blur the two and end up with handlers that half-verify a token, half-query a membership table, and cannot explain which piece actually establishes trust. A clean backend separates these concerns on purpose.
A login flow is just a credential-exchange protocol. The user proves possession of a secret or identity assertion once, and the server returns a cheaper credential for ordinary requests. In first-party authentication that secret is often a password. In federated authentication the external identity provider may verify the primary secret instead. The backend engineer still needs to understand both, because the system must decide what it trusts and what claims it issues downstream.
Workflow
Submit: The caller presents a password or delegated identity assertion to begin authentication.
Step 1 / 4 — Submit
Why Password Hashing Exists
Passwords must be stored as slow, one-way hashes because database breaches are normal engineering assumptions, not science fiction. If plaintext or reversibly encrypted passwords are stored, a read-only database leak becomes an instant credential leak. If a fast general-purpose hash is stored, the leak becomes an offline brute-force contest the attacker is likely to win. Slow password hashing exists to make each guess expensive and therefore make the breach materially less useful.
| Storage choice | Why teams reach for it | Why it is wrong or right |
|---|---|---|
| Plaintext | No extra code | Catastrophic: a dump is an immediate credential compromise |
| Fast hash such as SHA-256 | Easy to implement | Wrong: offline guessing is far too cheap |
| bcrypt or Argon2id | Requires explicit auth policy | Chosen because the cost of each guess is deliberately high |
Good login flows also avoid becoming account-enumeration or timing oracles. Returning 'unknown email' for one failure and 'wrong password' for another leaks information. A better design returns one generic invalid-credentials outcome and keeps the precise internal reason for logs and metrics if needed. This is a recurring security theme: the system may know more than it safely reveals.
JWT Issuance and Claims
After expensive credential verification succeeds, the backend usually issues a cheaper request credential: an access token. JWT is a common format because it lets downstream services verify integrity locally and read claims without a database lookup on every request. That does not make JWT magically secure. The security comes from short lifetimes, careful signing-key management, claim discipline, and rejecting invalid or unexpected tokens consistently.
A JWT should be treated as a signed statement, not as a session database. Only include claims that downstream services actually need and can safely trust for the token lifetime. If permissions are encoded into the token, the lifetime should stay short so membership or role changes do not remain stale for hours. If the token is too long-lived, revocation and authorization freshness become operational problems instead of neat diagrams.
How This Fits Meridian
Meridian's current repository comments already point to the intended direction: apps/identity-service/internal/config/config.go says Auth0 issuer and audience settings will be added when auth verification is implemented. That means the near-term production direction is federated token verification in identity-service rather than a fully implemented first-party password flow today. This chapter therefore teaches the generic concepts and then places Meridian within them honestly.
| Mode | Who verifies the primary secret | What Meridian still needs to do |
|---|---|---|
| First-party login | identity-service verifies the password hash | Implement password storage, login handlers, token issuer, refresh flow |
| Auth0 or another external IdP | The external provider verifies the primary credential | Verify the provider's JWT, map subject to Meridian memberships, issue or trust tenant-scoped claims |
identity-service currently has only /healthz and an empty internal/domain package. This lesson is a design and fundamentals chapter grounded in the repository's declared auth direction, not a narration of already-checked-in login code.
- Store passwords only as slow one-way hashes if the backend owns credential verification.
- Collapse invalid-login outcomes into one public failure to avoid leaking account information.
- Issue short-lived JWTs with only the claims downstream services genuinely need.
- Understand first-party hashing and federated token verification as two forms of the same authentication principle: establish identity once, verify cheaply on requests.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.