Skip to content

fix: hash refresh tokens at rest in the database#113

Merged
GitAddRemote merged 5 commits intomainfrom
fix/ISSUE-96-hash-refresh-tokens
Apr 13, 2026
Merged

fix: hash refresh tokens at rest in the database#113
GitAddRemote merged 5 commits intomainfrom
fix/ISSUE-96-hash-refresh-tokens

Conversation

@GitAddRemote
Copy link
Copy Markdown
Owner

@GitAddRemote GitAddRemote commented Apr 11, 2026

Summary

  • Tokens are now stored as `SHA-256(raw_token)` in the `refresh_tokens` table (`token` column is `varchar` with a unique constraint)
  • The raw value is only ever returned to the caller at issue time — it is never persisted
  • All DB lookups (`refreshAccessToken`, `revokeRefreshToken`) hash the incoming token before querying
  • Existing plaintext tokens in the DB become invalid after deploy; users must re-login (acceptable per spec)

Why SHA-256 (not bcrypt)

Input has 256 bits of entropy so rainbow tables are infeasible. bcrypt is intentionally slow — this is a hot lookup path and the entropy argument makes the slowness unnecessary.

Test plan

  • Login issues a raw token to the client, DB shows its hash
  • Token refresh succeeds with the raw token
  • Logout revokes the token; subsequent refresh with the same token returns 401
  • Manually inspect `refresh_tokens` table — no plaintext tokens present

Closes #96

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the auth refresh-token flow so refresh tokens are never stored in plaintext, reducing blast radius if the refresh_tokens table is compromised.

Changes:

  • Store refresh tokens as sha256(raw_token) in refresh_tokens.token while returning only the raw token to the caller at issuance time.
  • Hash incoming refresh tokens before DB lookups in refreshAccessToken() and revokeRefreshToken().
  • Introduce a local hashToken() helper to centralize the hashing logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/modules/auth/auth.service.ts
Comment thread backend/src/modules/auth/auth.service.ts
@GitAddRemote GitAddRemote self-assigned this Apr 12, 2026
Store SHA-256(token) in the database instead of the raw token value.
The raw token is returned to the caller only; the hash is used for all
DB lookups and updates. Existing plaintext tokens become invalid on
deploy — users must re-login (acceptable per spec).

- Add private hashToken() helper using crypto.sha256
- generateRefreshToken: saves hash, returns raw value
- refreshAccessToken: hashes incoming token before lookup
- revokeRefreshToken: hashes incoming token before update

Closes #96
@GitAddRemote GitAddRemote force-pushed the fix/ISSUE-96-hash-refresh-tokens branch from 45d05d7 to 2a4659d Compare April 13, 2026 00:24
Covers three contracts:
- generateRefreshToken() returns the raw token and persists only its hash
- refreshAccessToken() queries by hash, rejects tokens with no hash match
- revokeRefreshToken() passes the hash (not the raw token) to the update call
Copilot AI review requested due to automatic review settings April 13, 2026 14:03
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/modules/auth/auth.service.spec.ts Outdated
setDate(+7) adds calendar days, not a fixed millisecond count, so
comparing against 7 * 24h would be flaky across DST transitions.
Mirror the implementation by computing expected via setDate(+7).
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/modules/auth/auth.service.spec.ts
The suite now covers password reset, change password, and refresh token
hashing — scoping it to "Password Reset" was misleading.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/src/modules/auth/auth.service.spec.ts
RefreshToken.id is @PrimaryGeneratedColumn('uuid') — a string, not a
number. Using id: 1 in the mock fixture misrepresents the entity shape
and could mask bugs in code paths that call update(storedToken.id, ...).
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@GitAddRemote GitAddRemote merged commit fbda05f into main Apr 13, 2026
13 checks passed
@GitAddRemote GitAddRemote deleted the fix/ISSUE-96-hash-refresh-tokens branch April 13, 2026 14:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tech Story: Hash refresh tokens at rest in the database

2 participants