Skip to content

Tech Story: Hash refresh tokens at rest in the database #96

@GitAddRemote

Description

@GitAddRemote

Tech Story

As a platform engineer, I want refresh tokens stored as hashes in the database so that a database breach does not yield usable tokens that could be used to impersonate users.

Context

Refresh tokens are currently stored as plaintext crypto.randomBytes(32) hex strings. If the refresh_tokens table is compromised (SQL injection, backup leak, rogue DB access), every active user session can be hijacked. Storing the SHA-256 hash of the token instead means the raw token is never persisted — only the bearer of the original token can produce the correct hash.

Acceptance Criteria

  • generateRefreshToken() stores sha256(token) in the DB, returns the raw token to the caller
  • refreshAccessToken() hashes the incoming token before looking it up in the DB
  • revokeRefreshToken() hashes the incoming token before the update query
  • No raw token value ever written to the database or application logs
  • Existing plaintext tokens in DB become invalid after deploy (users must re-login — acceptable)
  • No migration required — column type unchanged (text), only stored value changes

Technical Elaboration

function hashToken(raw: string): string {
  return crypto.createHash('sha256').update(raw).digest('hex');
}
  • Call hashToken(token) before every save and findOne/update involving the token column
  • The entity column name stays token; no schema migration needed
  • Add a unique index on refresh_tokens.token if one does not already exist (the hash is effectively a unique identifier)

Notes

  • SHA-256 is appropriate here (not bcrypt) because: the input has high entropy (32 random bytes) so rainbow tables are infeasible, and lookup performance matters (bcrypt is intentionally slow)
  • Do not log the raw token at any point in the token lifecycle

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendBackend services and logicdatabaseSchema, migrations, indexingsecuritySecurity, auth, and permissionstech-storyTechnical implementation story

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions