Skip to content

Authentication and Authorization

Authentication (AuthN) answers “who are you?” — it verifies identity.

Authorization (AuthZ) answers “what can you do?” — it enforces permissions.

These are distinct concerns that are often conflated. A user can be authenticated (their identity is Verified) but not authorized (they lack permission for a specific action). Conversely, a system Might authorize a request without authentication (anonymous access).

AspectAuthenticationAuthorization
QuestionWho are you?What are you allowed to do?
MechanismPasswords, MFA, certificatesRBAC, ABAC, ACLs, policies
Failure modeAuthentication failedAccess denied / Forbidden
HTTP status401 Unauthorized403 Forbidden
FrequencyOnce per session ()Every request
RevocationInvalidate session/tokenUpdate permissions/policies
MethodWhy It Fails
PlaintextImmediate compromise on any data breach
MD5Fast, 128-bit output, no salt, broken
SHA-1Fast, 160-bit output, collision broken
SHA-256 without saltFast, no salt, vulnerable to rainbow tables
Base64 encodingNot hashing at all — encoding is not encryption
Custom encryptionKey management problem shifts the attack surface

Use a dedicated password hashing function with a unique random salt per password:

# Argon2id (recommended)
from argon2 import PasswordHasher
ph = PasswordHasher(time_cost=3, memory_cost=65536, parallelism=4)
hash = ph.hash("user_password") # $argon2id$v=19$m=65536,t=3,p=4$...
# bcrypt (widely supported)
import bcrypt
hash = bcrypt.hashpw(b"user_password", bcrypt.gensalt(rounds=12))
# scrypt (memory-hard alternative)
import hashlib
hash = hashlib.scrypt(b"user_password", salt=os.urandom(16), n=2**14, r=8, p=1, dklen=64)

A password hash must contain:

  1. Algorithm identifier: Which function was used (allows migration)
  2. Parameters: Cost factor, memory, parallelism (allows increasing work factor)
  3. Salt: Unique per password (prevents rainbow tables and identical password detection)
  4. Hash output: The actual derived key

Example formats:

# Argon2id
$argon2id$v=19$m=65536,t=3,p=4$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG
# bcrypt
$2b$12$R9h/cIPz0gi.YJJFsRyuPOYfGJTCGijPylCPvmFbzXuKJ5Xo1GZ6u
# scrypt (PHC format)
$scrypt$ln=16,r=8,p=1$c2FsdHNhbHQ$E4JnHDMfM/4R5U6YbGzbVg==

When you need to upgrade from bcrypt to Argon2id:

  1. On login, verify the password against the existing hash
  2. If verification succeeds and the hash uses the old algorithm, re-hash with the new algorithm
  3. Store the new hash
  4. Over time, all active users migrate to the new algorithm

This avoids forcing a password reset for all users.

NIST SP 800-63B Recommendations (Revised 2023)

Section titled “NIST SP 800-63B Recommendations (Revised 2023)”

The NIST Digital Identity Guidelines represent the current best practice for password policies, and They contradict many traditional policies.

Do:

  • Require minimum 8 characters (15+ for higher security)
  • Allow all printable characters and spaces
  • Check passwords against breached password databases (HaveIBeenPwned API)
  • Use rate limiting to prevent brute-force attacks
  • Allow password managers and paste functionality
  • Implement secure password reset (time-limited, single-use tokens)

Do Not:

  • Force periodic password rotation (leads to predictable patterns: Password1!``Password2!…)
  • Require composition rules (uppercase + lowercase + digit + special character — users just capitalize the first letter and add 1!)
  • Require passwords to be changed after a breach unless compromise is confirmed
  • Use knowledge-based authentication (security questions are guessable)
  • Store password hints
  • Set maximum password length under 64 characters

Check passwords against known breached password databases at creation and authentication time:

import requests
import hashlib
import sys
def check_pwned_password(password):
"""Check if password appears in HaveIBeenPwned database using k-anonymity."""
sha1 = hashlib.sha1(password.encode()).hexdigest().upper()
prefix, suffix = sha1[:5], sha1[5:]
response = requests.get(f"https://api.pwnedpasswords.com/range/{prefix}")
for line in response.text.splitlines():
hash_suffix, count = line.split(":")
if hash_suffix == suffix:
return int(count)
return 0

MFA requires two or more independent factors from different categories:

Factor CategoryExamplesSecurity Level
KnowledgePasswords, PINs, security questionsLow (phishable)
PossessionTOTP apps, hardware keys, phone (SMS)Medium (varies)
InherenceBiometrics (fingerprint, face, iris)Medium (not revocable)
LocationIP address, geolocationLow (spoofable)

TOTP (RFC 6238) generates a 6-8 digit code based on a shared secret and the current time. The server And client both compute:

\mathrm{TOTP = \mathrm{Truncate\Big(\mathrm{HMAC-SHA-1(K, T)\Big)

Where KK is the shared secret and T = \lfloor \mathrm{current\_time / 30 \rfloor.

PropertyValue
Time step30 seconds
Code length6 digits (default)
Shared secret160-bit (Base32)
HashHMAC-SHA-1 (default), SHA-256, SHA-512

Limitations: TOTP codes are phishable. An attacker can proxy the login page and forward the TOTP Code to the real service in real time. TOTP is not a replacement for phishing-resistant MFA.

FIDO2 (Fast Identity Online 2) is the gold standard for phishing-resistant authentication. It uses Public-key cryptography with a hardware authenticator.

sequenceDiagram
    participant U as User
    participant C as Client (Browser)
    participant S as Server

    S->>C: Send challenge + allowed credentials
    C->>U: Prompt for biometric/PIN
    U->>C: Unlock authenticator
    C->>S: Send authenticator assertion (signature over challenge)
    S->>S: Verify signature with stored public key
    S->>C: Authentication success

Key properties:

  • Phishing-resistant: The authenticator binds to the relying party (origin), so a phishing site cannot replay the credential.
  • Public-key based: The server stores a public key, not a shared secret. Compromising the server does not allow impersonation.
  • Hardware-bound: Private key never leaves the authenticator (YubiKey, Touch ID, Windows Hello).
  • Multi-device: Passkeys (synced WebAuthn credentials) allow cloud-synced FIDO2 credentials.
Key ModelProtocol SupportConnectorPrice (approx.)
YubiKey 5FIDO2, U2F, OTP, PIVUSB-A/C, NFCUSD 45-55
YubiKey BioFIDO2 (biometric)USB-A/CUSD 80
Titan KeyFIDO2, U2FUSB-A/C, NFCUSD 30-40
SoloKeysFIDO2, U2FUSB-A/CUSD 25-50

Reference Standards: NIST SP 800-63B (Digital Identity — Authentication and Lifecycle Management), RFC 6749 (OAuth 2.0), RFC 7636 (PKCE), RFC 7519 (JWT), RFC 7515 (JWS), RFC 6238 (TOTP), RFC 8446 (TLS 1.3), OWASP Authentication Cheat Sheet, FIDO2 (W3C WebAuthn + CTAP2).

This topic covers the essential concepts and techniques related to authentication and authorization, including key principles and practical applications.

Key concepts include:

  • core concepts and definitions
  • key principles and frameworks
  • practical applications
  • common techniques and methods
  • evaluation and critical analysis

A thorough understanding of these concepts, combined with regular practice and review, is essential for mastery of this topic.

Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.