feat(token): support inline JWT private key via JWT_PRIVATE_KEY_PEM#175
feat(token): support inline JWT private key via JWT_PRIVATE_KEY_PEM#175
Conversation
- 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
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
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 refactorsLoadSigningKeyto reuse the shared parsing logic. - Extends configuration (
Config) and validation to acceptJWT_PRIVATE_KEY_PEMfor 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.
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>
There was a problem hiding this comment.
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>
Summary
JWT_PRIVATE_KEY_PEMenvironment variable so RS256/ES256 deployments can supply the PEM content directly (K8s Secrets, GitHub Actions, Fly.io, Cloud Run). ExistingJWT_PRIVATE_KEY_PATHbehavior is unchanged.LoadSigningKeyinto a thin wrapper around a new exportedParseSigningKey(data []byte)so both sources share the same PEM parsing logic (PKCS#1 / PKCS#8 / SEC1, multi-block support).JWT_PRIVATE_KEY_PATHorJWT_PRIVATE_KEY_PEMwhen algorithm is RS256/ES256..env.exampleanddocs/CONFIGURATION.mdwith GitHub Actions and KubernetessecretKeyRefexamples.Test plan
TestParseSigningKey_*covering RSA PKCS#1/PKCS#8, ECDSA SEC1/PKCS#8, empty input, invalid PEM, unsupported format, and multi-block input.TestConfig_Validate_JWTSigningAlgorithmwith cases for PEM-only, path+PEM (both set), and the updated error message.make fmt/make lintclean.go test ./internal/token/... ./internal/config/... ./internal/bootstrap/...all pass.🤖 Generated with Claude Code