Skip to content

refactor(proof/apikey): readability + maintainability sweep#67

Merged
jmgilman merged 4 commits into
masterfrom
session-039/proof-apikey-sweep
May 28, 2026
Merged

refactor(proof/apikey): readability + maintainability sweep#67
jmgilman merged 4 commits into
masterfrom
session-039/proof-apikey-sweep

Conversation

@jmgilman
Copy link
Copy Markdown
Contributor

Summary

Seventh slice of the multi-session readability sweep (after access/ #61, authz/ #62, exchange/ #63, http/ #64, management/ #65, onboarding/ #66). First of three PRs covering proof/. Same 10-criteria bar.

Headline change: discharge the deferred Plaintext rename queued since PR #61.

Rename PlaintextToken

proof/apikey.IssuedToken.PlaintextToken. The field holds the full opaque token (ak_<id>_<secret>), not just the secret material. The package internally distinguishes the parsed inner secret component (parseToken, hashSecret, SecretHash) from the full assembled token — so the previously-proposed Secret rename would have collided. Token reads naturally as issued.Token and mirrors access/jwt.IssuedToken.JWT (PR #61), which named the field by what the value is.

Cascade lands atomically in this PR:

Other apikey changes

  • Godocs added to private items: options struct, defaultOptions(), formatToken, parseToken, hashSecret, unauthenticated.
  • parseToken's godoc names the separator-smuggling attack class its input-shape check defends against.
  • Inline security comments on the two security-critical branches in VerifyAPIToken:
    • ErrTokenNotFoundunauthenticated mapping (existence-leak defense).
    • subtle.ConstantTimeCompare branch (timing side-channel defense).
  • Test file (267 LOC) stays as one file — well-organized, below the per-domain split threshold.
  • TokenStore and TokenMetadataLister stay hand-rolled (package-local; convention since PR refactor(access): readability + maintainability sweep + introduce mockery #61).

Out of scope

  • The two remaining proof/ slices land as separate follow-up PRs: proof/oidc and proof/passkey + proof/passkey/session.
  • plaintext parameter in the OIDC paths (testkit/.../runtime.go:298, httpui/server.go:37) is unrelated to this rename and stays for the OIDC pass.

Commits

  1. refactor(proof/apikey): rename IssuedToken.Plaintext to Token
  2. refactor(exchange,management,testkit): cascade Plaintext to Token rename
  3. refactor(proof/apikey): godocs + inline security comments
  4. chore(proof/apikey): link errors.Is in godoc per godoclint

Test plan

  • moon run root:check --summary minimal — format, lint, build, unit, Testcontainers integration all green.
  • No remaining Plaintext references in API-token paths (only OIDC-path plaintext parameters survive, intentionally).

🤖 Generated with Claude Code

jmgilman and others added 4 commits May 27, 2026 17:44
"Plaintext" was misleading in a crypto context, and inside the package
already collided with the parsed inner `secret` component used by
formatToken/parseToken/hashSecret. Rename the field, the
VerifyAPIToken parameter, and the parseToken parameter to Token/token.

The field holds the full opaque token (ak_<id>_<secret>), so Token
reads naturally and mirrors access/jwt.IssuedToken.JWT (PR #61) which
named the field by what the value is.

Tests outside proof/apikey/ that reference Plaintext on this struct
will fail to compile after this commit and are fixed in the cascade
commit that follows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pick up the field-rename cascade that the apikey commit started. All three
consumer-side names follow the source of truth:

- exchange.APITokenRequest.Plaintext -> Token (deferred since PR #63)
- management.IssuedAPIToken.Plaintext -> Token (deferred since PR #65)
- exchange.APITokenVerifier.VerifyAPIToken parameter -> token
- testkit/internal/authflow.Runtime.ExchangeAPIToken parameter -> token
- testkit/internal/httpui.authRuntime.ExchangeAPIToken parameter -> token
- management testTokenSecret constant -> testToken (the value is the full
  ak_<id>_<secret> token, not just the inner secret)

Comments and test fixtures aligned with the new vocabulary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add godocs to private items the audit flagged as undocumented: the
options struct, defaultOptions, formatToken, parseToken, hashSecret,
and unauthenticated. parseToken's godoc names the attack class its
input-shape check defends against (separator-smuggling in the secret).

Annotate the two security-critical branches in VerifyAPIToken:

- The ErrTokenNotFound -> unauthenticated mapping is a deliberate
  existence-leak defense.
- The subtle.ConstantTimeCompare branch defends against timing
  side-channel attacks on the stored secret hash.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
godoclint flagged the unauthenticated() godoc for naming errors.Is
without the bracket syntax that links to the stdlib symbol.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jmgilman jmgilman merged commit e5476fe into master May 28, 2026
2 checks passed
@jmgilman jmgilman deleted the session-039/proof-apikey-sweep branch May 28, 2026 00:52
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.

1 participant