Skip to content

feat(token): support inline JWT private key via JWT_PRIVATE_KEY_PEM#175

Merged
appleboy merged 4 commits intomainfrom
worktree-jwt
Apr 23, 2026
Merged

feat(token): support inline JWT private key via JWT_PRIVATE_KEY_PEM#175
appleboy merged 4 commits intomainfrom
worktree-jwt

Conversation

@appleboy
Copy link
Copy Markdown
Member

Summary

  • Add JWT_PRIVATE_KEY_PEM environment variable so RS256/ES256 deployments can supply the PEM content directly (K8s Secrets, GitHub Actions, Fly.io, Cloud Run). Existing JWT_PRIVATE_KEY_PATH behavior is unchanged.
  • When both variables are set, inline PEM wins and a warning is logged — matches the 12-factor principle of env overriding image-baked files.
  • Refactor LoadSigningKey into a thin wrapper around a new exported ParseSigningKey(data []byte) so both sources share the same PEM parsing logic (PKCS#1 / PKCS#8 / SEC1, multi-block support).
  • Update config validation to require either JWT_PRIVATE_KEY_PATH or JWT_PRIVATE_KEY_PEM when algorithm is RS256/ES256.
  • Document the new variable in .env.example and docs/CONFIGURATION.md with GitHub Actions and Kubernetes secretKeyRef examples.

Test plan

  • Added TestParseSigningKey_* covering RSA PKCS#1/PKCS#8, ECDSA SEC1/PKCS#8, empty input, invalid PEM, unsupported format, and multi-block input.
  • Extended TestConfig_Validate_JWTSigningAlgorithm with cases for PEM-only, path+PEM (both set), and the updated error message.
  • make fmt / make lint clean.
  • go test ./internal/token/... ./internal/config/... ./internal/bootstrap/... all pass.
  • Manual smoke test across all four startup scenarios (PATH only, PEM only, both set → expect warning, neither set → expect validation error) after merge to a staging env.

🤖 Generated with Claude Code

- Add JWT_PRIVATE_KEY_PEM env var accepting PEM content directly
- Extract ParseSigningKey helper so PEM parsing works for both file and env sources
- Prefer inline PEM over JWT_PRIVATE_KEY_PATH when both are set, with a warning log
- Update config validation to require either path or PEM for RS256/ES256
- Document K8s Secret and GitHub Actions deployment examples
Copilot AI review requested due to automatic review settings April 23, 2026 11:09
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

❌ Patch coverage is 45.71429% with 19 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/bootstrap/providers.go 0.00% 19 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

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

Adds support for providing asymmetric JWT signing keys via inline PEM content (JWT_PRIVATE_KEY_PEM) in addition to the existing file-path-based configuration, with PEM taking precedence when both are set.

Changes:

  • Introduces token.ParseSigningKey([]byte) and refactors LoadSigningKey to reuse the shared parsing logic.
  • Extends configuration (Config) and validation to accept JWT_PRIVATE_KEY_PEM for RS256/ES256.
  • Updates bootstrap key-loading behavior (PEM preferred), tests, and documentation/examples.

Reviewed changes

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

Show a summary per file
File Description
internal/token/key.go Adds ParseSigningKey and refactors LoadSigningKey to wrap file reading + parsing.
internal/token/key_test.go Adds unit tests covering ParseSigningKey across RSA/ECDSA formats and multi-block PEM.
internal/config/config.go Adds JWTPrivateKeyPEM, loads env var, and updates RS256/ES256 validation rules.
internal/config/config_test.go Extends validation tests for PEM-only and path+PEM scenarios and new error message.
internal/bootstrap/providers.go Prefers JWT_PRIVATE_KEY_PEM over JWT_PRIVATE_KEY_PATH with a startup warning when both are set.
docs/CONFIGURATION.md / .env.example Documents the new env var and provides GitHub Actions + Kubernetes examples.

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

Comment thread .env.example Outdated
Comment thread docs/CONFIGURATION.md Outdated
appleboy and others added 2 commits April 23, 2026 21:34
Drop the switch/var-block/source-string indirection in favor of a
straight if/else with inlined fatal messages per branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The phrasing "via ONE of" read as mutually exclusive, but the code
allows setting both with JWT_PRIVATE_KEY_PEM taking precedence.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

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 7 out of 7 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.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@appleboy appleboy merged commit 5f22002 into main Apr 23, 2026
15 checks passed
@appleboy appleboy deleted the worktree-jwt branch April 23, 2026 13:53
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.

2 participants