Skip to content

feat(auth): JWT key rotation with key ids and an overlap window - #242

Merged
afeezorobsco-cyber merged 5 commits into
DocForum:mainfrom
Ezekiel146:fix/issue-106
Oct 27, 2026
Merged

afeezorobsco-cyber merged 5 commits into
DocForum:mainfrom
Ezekiel146:fix/issue-106

Conversation

@Ezekiel146

Copy link
Copy Markdown
Contributor

Closes #106

What changed

  • backend/src/config/index.ts: JWT signing keyrings for access and refresh tokens. The current key is JWT_*_SECRET with an optional JWT_*_KID. An optional JWT_*_SECRET_PREVIOUS stays in the keyring during the overlap window.
  • backend/src/modules/auth/services/tokens.ts: sign with the current key and write its kid into the token header. Verify with the key named by the header's kid. Tokens with an unknown kid are rejected. Tokens without a kid (issued before this change) are tried against each key.
  • backend/tests/unit/tokens.test.ts: rotation tests. A new token records the current kid. A previous-key token verifies during the overlap window and fails after the key is removed. An unknown kid is rejected. A legacy token still verifies.
  • docs/runbooks/secret-rotation.md: the rotation procedure and the env var table.
  • changelog.d/106-jwt-key-rotation.md.

Which ROADMAP.md item does this close/progress?

Phase 9, issue #106 (production secrets management and key rotation). The rotation logic and the runbook are done. Still open: loading secrets from a secrets manager (depends on the platform choice in #22), the who-can-read-what section of the runbook, and the staging rotation record.

Docs updated in this PR

  • ROADMAP.md: not edited, per the shared-docs rule. The maintainers update the status line.
  • ARCHITECTURE.md
  • ARCHITECTURE_ESSENTIALS.md
  • PRD.md
  • changelog.d/ entry and docs/runbooks/secret-rotation.md added

Validation

  • Not run, per the requester's instruction to skip builds and tests for speed. Run npm run test in backend/ before merging.
  • Diff scanned for token-like strings and private keys: none found.

🤖 Generated with Claude Code

@afeezorobsco-cyber

Copy link
Copy Markdown
Contributor

@Ezekiel146 CI is failing on this branch against the latest main, so it can't be merged yet. The errors from the latest run:

Backend (typecheck + test + build)

src/modules/auth/services/tokens.ts(44,46): error TS2769: No overload matches this call.
tests/unit/tokens.test.ts(59,16): error TS2540: Cannot assign to 'accessKeys' because it is a read-only property.

tokens.ts:44 passes an options object that doesn't match any jwt.sign overload. On main, expiresIn is typed as the ms string type, so cast it or use the config's literal type. In tests/unit/tokens.test.ts:59, accessKeys is read-only, so stub it with vi.spyOn/vi.mock instead of assigning to it.

Ezekiel146 and others added 3 commits October 23, 2026 17:16
Access and refresh tokens are signed with the current key and carry its
kid. Verification selects the key by kid and accepts a previous key during
an overlap window, so rotation does not log users out. Tokens with an
unknown kid are rejected. Adds the rotation runbook.

Refs DocForum#106

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the needs-rebase Has merge conflicts with main; the author needs to rebase label Oct 24, 2026
@github-actions

Copy link
Copy Markdown

@Ezekiel146 This PR now has merge conflicts with main, most likely because #152 (Feat/verified wallet links for patients and facilities) was just merged, so it can't be merged until it's rebased:

git fetch upstream            # upstream = https://github.com/DocForum/docforum-core.git
git rebase upstream/main
# resolve the conflicts, re-run the tests, then:
git push --force-with-lease

The usual culprits are shared docs like ROADMAP.md and ARCHITECTURE*.md. If the conflict is in the ROADMAP.md changelog, move your entry into a new file under changelog.d/ (see changelog.d/README.md) rather than resolving it in place, so it can't conflict again. CI re-runs when you push, and the needs-rebase label is removed automatically once the conflicts are resolved.

Automated message. It won't repeat unless the PR conflicts again after your next push.

@afeezorobsco-cyber

Copy link
Copy Markdown
Contributor

@Ezekiel146 The only CI failure on your latest run is the Audit production dependencies step. That was caused by new axios/zod/body-parser advisories, not by your change, and it's fixed on main now (#249).

This PR also conflicts with main, so please rebase. That brings in the fix, and CI will rerun:

git fetch upstream
git rebase upstream/main
# fix the conflicts, then
git push --force-with-lease

@github-actions github-actions Bot removed the needs-rebase Has merge conflicts with main; the author needs to rebase label Oct 25, 2026
Keep both the keyring helpers and upstream's wallet-link cutoff parser in
config. Adapt to upstream's `as const` config and @types/jsonwebtoken
9.0.10: type signWithKeyring's TTL as SignOptions['expiresIn'] and swap the
keyring in the rotation test through a mutable view.

@afeezorobsco-cyber afeezorobsco-cyber left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sweep: malware scan clean, CI green.

@afeezorobsco-cyber afeezorobsco-cyber left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sweep: malware scan clean, CI green.

@afeezorobsco-cyber
afeezorobsco-cyber merged commit dd2685d into DocForum:main Oct 27, 2026
2 checks passed
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.

[Phase 9] Production secrets management and key rotation runbook

3 participants