Skip to content

feat(cli): support caller-supplied extra JWT claims#37

Merged
appleboy merged 5 commits intomainfrom
feat/extra-claims
Apr 26, 2026
Merged

feat(cli): support caller-supplied extra JWT claims#37
appleboy merged 5 commits intomainfrom
feat/extra-claims

Conversation

@appleboy
Copy link
Copy Markdown
Member

@appleboy appleboy commented Apr 26, 2026

Summary

  • Adds --extra-claims key=value (repeatable) and --extra-claims-file <env-file> flags so the CLI can attach caller-supplied JWT claims (project codes, trace IDs, routing hints) to every OAuth token request.
  • Wires the merged map as the extra_claims form parameter on all three grants the CLI uses: authorization code, device code, and refresh token. Refresh re-supplies the claims since the server doesn't persist them.
  • Pairs with the AuthGate server change in go-authgate/authgate#178, which validates reserved keys, size limits, and admin-managed overrides server-side.

Why key=value, not raw JSON

Hand-rolling JSON on a shell prompt is fiddly (quoting, escaping, file size). The flag accepts key=value and infers types: count=42 → number, enabled=true → bool, tags=["a","b"] → array; everything else stays a plain string. The file uses the same .env-style format so users don't have to think in JSON terms either.

authgate-cli \
  --extra-claims project=acme-prod \
  --extra-claims trace_id=req-42 \
  --extra-claims count=7

# or, from a file
authgate-cli --extra-claims-file ./claims.env

File entries merge first; flag entries override on conflicting keys.

Notable correctness / safety choices

  • Numeric precision — uses json.Decoder + UseNumber() so IDs above 2^53 round-trip exactly (mirrors the existing JWT decoder in token_cmd.go).
  • Memory bound on env-file reads — wraps the file in io.LimitReader at 64 KiB so a malicious 2 GB env file can't OOM the CLI before the server's much smaller raw-payload limit fires.
  • Secret hygiene in errors — malformed --extra-claims pairs are referenced by 1-based index (--extra-claims #2: …), not echoed verbatim, so a value the user mistakenly typed (e.g. a password) doesn't land in stderr or CI logs.
  • Server is authoritative — the CLI doesn't replicate reserved-key or size validation; the server returns descriptive invalid_request errors which the CLI surfaces as-is. Avoids drift.

Test plan

  • make fmt — clean
  • make lint — 0 issues
  • make test — both packages green
  • New tests cover: pair/file parsing, value-type inference, large-integer precision (2^53+1 round-trip), URL-encoding of &/=/+/multibyte values, file-size cap, malformed env-file syntax, secret-redaction in errors, and per-grant extra_claims set/unset symmetry across all three flows
  • Manual smoke against an AuthGate server with PR #178 merged: authgate-cli --extra-claims project=acme then authgate-cli token decode and confirm project appears in the JWT payload
  • Manual smoke: confirm refresh flow re-sends extra_claims (run with an expired access token + valid refresh token + --extra-claims trace_id=foo, confirm new token carries trace_id)

🤖 Generated with Claude Code

- Add --extra-claims key=value (repeatable) and --extra-claims-file
  flags resolved into cfg.ExtraClaims at config load
- Send the merged claims as the extra_claims form parameter on the
  authorization_code, device_code, and refresh_token grants
- Preserve large-integer precision via json.Decoder UseNumber so IDs
  above 2^53 round-trip exactly
- Cap env-file reads at 64 KiB to bound godotenv memory use
- Reference malformed --extra-claims pairs by index so values that
  look like secrets aren't echoed to stderr
- Document the new flags, trust model, and per-process scope in
  README.md and .env.example

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 26, 2026 03:18
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 26, 2026

Codecov Report

❌ Patch coverage is 80.88235% with 13 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
config.go 79.03% 11 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown

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 CLI support for caller-supplied “extra JWT claims” that are merged from repeatable --extra-claims key=value and an optional --extra-claims-file, then sent as the extra_claims form parameter on all token grant requests (auth code, device code, refresh).

Changes:

  • Introduces parsing/merging logic for extra-claims (flag + env-file) and stores normalized JSON on AppConfig.ExtraClaims.
  • Wires extra_claims into authorization-code, device-code, and refresh-token grant requests when set.
  • Documents the new flags/feature and adds a new test suite covering parsing and wire-level behavior.

Reviewed changes

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

Show a summary per file
File Description
config.go Adds flags, config field, and parsing/merging utilities for extra claims; central constant for extra_claims.
auth.go Re-sends extra_claims on refresh-token grant when configured.
browser_flow.go Sends extra_claims on authorization-code exchange when configured.
device_flow.go Sends extra_claims on device-code exchange when configured.
extra_claims_test.go New tests for claim parsing/merging and wire-level posting to the token endpoint.
README.md Documents new flags and provides usage/examples + security/trust-model note.
.env.example Adds notes/examples pointing users to the new flags / file-based claims.

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

Comment thread config.go
Comment thread config.go Outdated
Comment thread extra_claims_test.go
Comment thread extra_claims_test.go
Comment thread extra_claims_test.go Outdated
- Reject trailing non-whitespace after a parsed value in
  parseClaimValue so e.g. "42 extra" stays a string instead of
  decoding as 42
- loadExtraClaimsFile now reads limit+1 bytes and returns an
  explicit "file too large" error instead of silently truncating
  via io.LimitReader
- Replace TestLoadExtraClaimsFile_RejectsOversizedInput with
  unique-keyed lines and assert the new error; add a companion
  test confirming files at the size limit succeed
- Rename TestParseClaimValue numeric cases to reflect json.Number,
  add concrete-type assertions, and cover the new trailing-data
  fallback paths
- equalAny now returns false on json.Marshal errors so unsupported
  types don't silently compare equal

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

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 1 comment.


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

Comment thread config.go Outdated
Use %q so paths containing spaces or other special characters render
unambiguously and remain easy to copy/paste into a shell.

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

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 1 comment.


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

Comment thread extra_claims_test.go Outdated
Match the 3s convention used by callback_test.go's select-based async
assertions to reduce flakiness on slow CI runners.

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

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.

Normalize column widths in the env-vars and CLI-flags tables so the
new --extra-claims and --extra-claims-file rows line up cleanly with
the rest. No content changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@appleboy appleboy merged commit 52a5549 into main Apr 26, 2026
14 of 15 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.

3 participants