Need the #1 custom application developer in Brisbane?Click here →

Authentication: Passwords, Tokens, Sessions

10 min read

Authentication is proving the user is who they claim to be. Authorisation is controlling what they're allowed to do. This section covers authentication. The next covers authorisation.

Authentication is security-critical. A bug here exposes user accounts. Never build auth from scratch in production—use a battle-tested library or service. The attack surface is too large.

Password-Based Authentication

Users create a password. Your system stores a hash, never the plaintext. When the user logs in, you hash their input and compare it to the stored hash.

Hashing is one-way: you can't unhash a password to get the original. This means if your database is compromised, attackers don't immediately have plaintext passwords.

Hashing Functions: bcrypt, Argon2

Not all hashing functions are equal. MD5 and SHA1 are fast and broken. They're not suitable for passwords.

bcrypt is designed for passwords. It's slow (intentionally—to make brute-force attacks expensive), includes salt automatically, and has been battle-tested for 20+ years.

Argon2 is newer, designed specifically for password hashing with memory hardness (expensive to compute even with powerful hardware). It's better than bcrypt if available in your ecosystem.

Use bcrypt or Argon2. Never use MD5, SHA1, or unsalted hashes.

Salt: Making Rainbows Useless

A salt is random data added to each password before hashing. This prevents rainbow table attacks (precomputed tables of hash values). Without salt, the same password always produces the same hash. With salt, identical passwords produce different hashes.

bcrypt includes salt automatically. Argon2 as well. You don't need to manage salt explicitly—the library does it.

Sessions vs JWTs

After authentication, how do you maintain the user's logged-in state?

Sessions (Stateful)

The server stores session data (user ID, permissions, etc.) and sends the client a session ID (usually in a secure cookie). The client sends the session ID with each request. The server looks up the session and retrieves the user's information.

Advantages: easy to invalidate (delete the session, user is logged out immediately), simple mental model. Disadvantages: server-side storage required, doesn't scale horizontally without shared storage.

JWTs (Stateless)

The server encodes the user's information (claims) into a signed token. The client stores the token and sends it with each request. The server validates the signature and reads the claims.

Advantages: stateless (any server can validate), scales horizontally. Disadvantages: can't easily revoke (the token is valid until expiry), larger payloads per request.

Session vs JWT Tradeoffs

  • Revocation: Sessions are instantly revoked. JWTs stay valid until expiry. For logout, sessions are better.
  • Scalability: Sessions require shared storage (Redis, database). JWTs need nothing server-side. For large deployments, JWTs are better.
  • Simplicity: Sessions are simpler conceptually. JWTs require understanding signatures and token formats.
  • Payload: Sessions store data server-side. JWTs carry all data in the token (larger payload per request).

For browser-based apps, sessions are simpler and better. For APIs and microservices, JWTs are often better. Many modern applications use both: sessions for web, JWTs for APIs.

Warning
Don't build auth from scratch. Use Passport.js (Node.js), Auth.js/NextAuth (Next.js), Django's auth (Django), or a service like Clerk, Auth0, or Firebase Auth. The complexity and security requirements are too high for custom implementations.

Multi-Factor Authentication (MFA)

Password alone is insufficient for security. Users reuse passwords, passwords are guessed, phishing attacks steal credentials. MFA adds a second factor.

TOTP (Time-based One-Time Password): Apps like Google Authenticator generate 6-digit codes that change every 30 seconds. The user must provide the code along with the password. This requires physical possession of the device with the auth app.

SMS: Codes sent via SMS to the user's phone. Less secure than TOTP (SIM swap attacks, SMS interception) but better than password alone.

Email verification: A link sent via email that confirms the user controls that email. Less secure for login (email can be compromised) but good for account recovery.

Hardware keys: USB or NFC keys that authenticate cryptographically. Very secure but requires the user to carry the key.

For most applications, TOTP is the best MFA option. It's secure, user-friendly, and doesn't require infrastructure (unlike SMS).

Common Authentication Mistakes

  • Storing plaintext passwords: Unforgivable. Hash them.
  • Using weak hash functions: MD5, SHA1, unsalted hashes. Use bcrypt or Argon2.
  • Building from scratch: Custom auth implementations have vulnerabilities. Use libraries.
  • No HTTPS: Passwords and sessions are sent in the clear. HTTPS is non-negotiable.
  • Overly complex recovery: Users forget passwords. Make password reset simple and secure.
  • No account lockout: After 10 failed login attempts, lock the account to prevent brute-force attacks.

Password Reset Flow

Users forget passwords. A good reset flow:

  1. User requests password reset, providing email
  2. Server generates a secure random token (not guessable)
  3. Server sends email with reset link (containing the token)
  4. User clicks link, enters new password
  5. Server validates token (exists, not expired), updates password
  6. Token is invalidated so it can't be used again

Tokens should expire (1 hour is reasonable). Longer expiry times increase the window for attacks.

HTTPS: Non-Negotiable

Authentication must happen over HTTPS. Without it, passwords and sessions are sent in plain text. An attacker on the network intercepts them and impersonates the user.

HTTPS is not optional. It's required for any authentication or sensitive data.

The Reality of Authentication

Authentication is complex and security-critical. The silver lining: don't build it yourself. Libraries and services have solved this problem. Use them. Your job is integrating them correctly, not reimplementing authentication.

For internal applications or rapid prototyping, consider OAuth (Google/GitHub login). For products where you control the user experience, use Auth.js, Clerk, or similar. For enterprise, your organization probably has standards.

The goal is: secure, user-friendly authentication without reinventing the wheel. That's achievable by using proven solutions.