feat(auth,app): ES256 JWT signing + AWS Secrets Manager key resolver#58
Merged
Conversation
Implements the MVP scope approved by the owner on 2026-05-14 (ADR-005):
P-256 ES256 plus an AWS Secrets Manager resolver. Explicit non-goals
deferred to a follow-up: P-384/ES512/Ed25519, GCP Secret Manager,
Azure Key Vault, HashiCorp Vault.
ES256 (pure stdlib, no new dependency):
- pkg/auth: ES256 SigningAlgorithm + SigningKey.ECDSAPrivate. The
validate / signingMethod / signMaterial / verifyMaterial / toJWK
switches each gain an ES256 case. validate rejects any curve other
than P-256. JWK gains Crv/X/Y fields; toJWK emits kty:EC, crv:P-256
with RFC 7518 fixed-length (32-byte, left-padded) coordinates.
- pkg/app/jwt_setup.go: ES256 case in loadJWTKey; parseECDSAPrivateKey
accepts SEC1 and PKCS#8, rejects non-P-256. Shared PEM plumbing
(loadPEMBytes, decodeSinglePEMBlock) factored out.
AWS Secrets Manager resolver:
- New package pkg/auth/secrets: a Resolver interface, EnvResolver
(zero-dep, resolves env:NAME and bare names), AWSSecretsManagerResolver
(aws-sm:<id> and aws-sm:<id>#<json-key>), and a Chain that routes by
scheme. The AWS SDK sits behind a one-method secretsManagerAPI
interface; every constructor returns the framework's own Resolver
interface — no AWS SDK type reaches a stable pkg/* signature.
- jwt_setup.go: secret_env / pem_env are now resolver references. A
bare name or env:NAME reads the environment (unchanged behaviour);
aws-sm:<id> reads AWS Secrets Manager. The AWS SDK client is built
lazily — only when a jwt_keys[] entry uses the aws-sm: scheme — so
non-AWS deployments never touch AWS credential resolution.
- buildJWTManager now takes a context.Context for the lazy resolver
construction; App.New passes context.Background().
Dependency: adds github.com/aws/aws-sdk-go-v2/{config,service/
secretsmanager}. First cloud-vendor SDK in the tree, gated entirely to
pkg/auth/secrets. dependency-impact review recorded at
docs/reports/dependency_impact_aws_sdk_2026-05-14.md (verdict:
ACCEPT-WITH-NOTE — firewall clean, Apache-2.0, ~3-5MB link delta).
Tests: pkg/auth/jwt_es256_test.go (sign/validate, JWKS EC shape,
P-256 enforcement, coordinate padding), pkg/auth/secrets/* (EnvResolver,
AWSSecretsManagerResolver with a fake SDK client, Chain routing),
pkg/app/jwt_setup_es256_test.go (PEM loading SEC1+PKCS#8, JWKS
auto-mount), plus resolver-chain wiring tests. Full go test ./...
and contract freeze green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 task
jcsvwinston
added a commit
that referenced
this pull request
May 14, 2026
Session End Protocol for the iteration that swept the post-ADR-004 queue, released v0.7.0, and landed the ES256 + AWS Secrets Manager MVP (PRs #56, #57, #58). - Archive the iteration at docs/iterations/2026-05-14-v0.7.0-release-and-es256.md. - Reset CURRENT_ITERATION.md to an empty slate with a ranked candidate-next-steps list (CSRF hardening is the top item). - Refresh HANDOFF.md: main @ e53f72b, v0.7.0 tagged, no active iteration, open housekeeping noted. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the MVP scope the owner approved on 2026-05-14 — ES256 (P-256) JWT signing and an AWS Secrets Manager resolver for JWT key material. Designed and justified in ADR-005.
Lands on top of
v0.7.0(PR #57). Explicit non-goals, deferred to a follow-up: P-384/ES512/Ed25519, GCP Secret Manager, Azure Key Vault, HashiCorp Vault.ES256 — pure stdlib, no new dependency
pkg/auth:ES256SigningAlgorithm+SigningKey.ECDSAPrivate. Thevalidate/signingMethod/signMaterial/verifyMaterial/toJWKswitches each gain an ES256 case.validaterejects any curve other than P-256 — a P-384 key withalgorithm: ES256fails fast atApp.New.JWKgainsCrv/X/Y;toJWKemitskty: EC,crv: P-256with RFC 7518 §6.2 fixed-length (32-byte, left-padded) coordinates.pkg/app/jwt_setup.go:ES256case inloadJWTKey;parseECDSAPrivateKeyaccepts SEC1 and PKCS#8, rejects non-P-256. Shared PEM plumbing factored out (loadPEMBytes,decodeSinglePEMBlock).AWS Secrets Manager resolver
pkg/auth/secrets: aResolverinterface,EnvResolver(zero-dependency —env:NAMEand bare names),AWSSecretsManagerResolver(aws-sm:<id>andaws-sm:<id>#<json-key>), and aChainthat routes by scheme. The AWS SDK sits behind a one-methodsecretsManagerAPIinterface; every constructor returns the framework's ownResolverinterface — no AWS SDK type reaches a stablepkg/*signature.jwt_setup.go:secret_env/pem_envare now resolver references. A bare name orenv:NAMEreads the environment (unchanged behaviour);aws-sm:<id>reads AWS Secrets Manager. The AWS SDK client is built lazily — only when ajwt_keys[]entry uses theaws-sm:scheme — so non-AWS deployments never touch AWS credential resolution.buildJWTManagernow takes acontext.Contextfor lazy resolver construction.Dependency
Adds
github.com/aws/aws-sdk-go-v2/configand.../service/secretsmanager— the first cloud-vendor SDK in the tree, gated entirely topkg/auth/secrets.dependency-impactreview recorded atdocs/reports/dependency_impact_aws_sdk_2026-05-14.md— verdict ACCEPT-WITH-NOTE: firewall clean, Apache-2.0, ~3-5 MB link delta, two cosmetic/Phase-4 follow-ups noted.Test plan
pkg/auth/jwt_es256_test.go— sign/validate roundtrip, JWKS EC key shape, P-256 enforcement (rejects P-384), coordinate left-padding invariant, alg-vs-kid mismatch rejection.pkg/auth/secrets/*_test.go—EnvResolver(bare +env:forms),AWSSecretsManagerResolveragainst a fake SDK client (plain secret, JSON-key fragment, missing secret, binary-secret rejection, SDK-error propagation),Chainscheme routing, compile-time proof the real*secretsmanager.Clientsatisfies the narrowed interface.pkg/app/jwt_setup_es256_test.go— ES256 PEM loading (SEC1 + PKCS#8), non-P-256 rejection, RSA-material rejection, JWKS auto-mount via ES256.env:prefix acceptance, lazy AWS construction (env-only keyset never builds the AWS resolver).go test ./...— clean.bash scripts/ci/check_contract_freeze.sh— PASS (ES256 additions are additive; firewall test confirms no AWS type leak).Known follow-ups (from the dependency-impact review)
go mod tidyonce the pre-existingadmin/protoreplace-directive issue is unblocked, so the AWS modules carry correct// directannotations.pkg/appcurrently importspkg/auth/secretsunconditionally.🤖 Generated with Claude Code