API Security in Depth · Authentication & Authorization for APIs

API Keys Done Right

Identification, hashing, prefixes, scopes and rotation.

API keys are simple long-lived bearer secrets for server-to-server access by customers. Do them well: generate high-entropy keys with a recognizable prefix (sk_live_…) for secret scanning, store only a hash (show the key once), attach scopes, an owner and expiry, support multiple active keys for zero-downtime rotation, record last used, and register the prefix with GitHub secret scanning so leaked keys are reported. Never use API keys from browsers or mobile apps — they can't be kept secret there.

Server-to-server credentials

Criterion API key OAuth client credentials mTLS client cert
Setup for customers Easiest Moderate Hardest
Secret type Static bearer Short-lived tokens from secret/key Private key (never sent)
Theft impact Valid until revoked Until token expiry + credential revoked Needs private key
Scopes Key metadata Token scopes Cert → identity mapping
Best for Developer APIs, simple integrations Enterprise / partner integrations High-assurance B2B, internal services
import hashlib, hmac, secrets

def create_key(env: str = "live") -> tuple[str, str]:
    raw = f"sk_{env}_{secrets.token_urlsafe(32)}"
    digest = hashlib.sha256(raw.encode()).hexdigest()   # high-entropy → fast hash is fine
    return raw, digest                                   # show raw once; store digest + last4

async def authenticate(header: str) -> ApiKey:
    digest = hashlib.sha256(header.encode()).hexdigest()
    key = await keys.find_by_digest(digest)
    if not key or key.revoked_at or (key.expires_at and key.expires_at < utcnow()):
        raise Unauthorized()
    await keys.touch_last_used(key.id)                   # async/batched in production
    return key

Common pitfalls

  • Storing keys in plaintext.
  • One key per account (no rotation without downtime).
  • Keys in query strings (logged everywhere).

Key points

  • Prefix + high entropy + hash at rest
  • Scopes, expiry and multiple active keys
  • Header only; secret scanning partnership

Further reading: GitHub secret scanning partner program

Practice questions

Why is a plain SHA-256 acceptable for API keys but not passwords?

Answer

API keys are long random values, so brute-forcing the hash is infeasible; passwords are low-entropy and need slow hashes like Argon2/bcrypt.

Why give API keys a recognizable prefix?

Answer

So secret scanners can detect leaked keys in code and logs and so humans can tell key types and environments apart.

Open in the app to mark lessons complete, take notes and track your progress.