Skip to content

fix: cap access key token lifetime at 1 year, remove never-expire#1141

Merged
jeremyeder merged 7 commits intoambient-code:mainfrom
jeremyeder:fix/access-key-token-lifetime-max-1year
Apr 2, 2026
Merged

fix: cap access key token lifetime at 1 year, remove never-expire#1141
jeremyeder merged 7 commits intoambient-code:mainfrom
jeremyeder:fix/access-key-token-lifetime-max-1year

Conversation

@jeremyeder
Copy link
Copy Markdown
Contributor

@jeremyeder jeremyeder commented Apr 1, 2026

Summary

Re-fixes #1084. Kubernetes TokenRequest does not support non-expiring tokens — the API server silently caps ExpirationSeconds, so tokens expire regardless. The original PR offered a "No expiration" option that was misleading.

  • Backend: Require expirationSeconds (reject nil/≤0), reject values exceeding 1 year (31536000s)
  • Frontend: Remove "No expiration" option, extract shared EXPIRATION_OPTIONS to lib/constants.ts, use shadcn Select component
  • Tests: 6 backend Ginkgo tests for expiration validation

Changes

  • components/backend/handlers/permissions.go — validate and enforce max 1 year
  • components/backend/handlers/permissions_test.go — expiration validation tests
  • components/frontend/src/app/projects/[name]/keys/page.tsx — use shared constants, shadcn Select
  • components/frontend/src/lib/constants.ts — shared EXPIRATION_OPTIONS and DEFAULT_EXPIRATION
  • .gitignore — add .worktrees/ and .claude/worktrees/

Test plan

  • Frontend unit tests pass (613 passed)
  • Backend unit tests pass (all packages)
  • Backend expiration validation: rejects missing, zero, negative, >1yr; accepts 1yr and 90d
  • Local kind cluster deployed and manually verified
  • tsc --noEmit clean, all pre-commit hooks pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Project keys now require an expiration time.
    • Predefined token lifetime options added with a 90-day default.
  • Improvements

    • Maximum token lifetime capped at 1 year.
    • Token lifetime selector is disabled while creating a key.
    • “No expiration” option and related helper text removed.
  • Tests

    • Added validation tests covering expiration presence, bounds, and accepted values.
  • Chores

    • Minor config wording and workflow tweaks; gitignore updated.

jeremyeder and others added 4 commits April 1, 2026 14:26
Kubernetes TokenRequest does not support non-expiring tokens — the API
server silently caps ExpirationSeconds and tokens expire regardless of
what is requested. Remove the misleading "No expiration" option and
enforce a maximum of 1 year (31536000s) in the backend.

Frontend changes:
- Add Token Lifetime selector (1d, 7d, 30d, 90d, 1y) defaulting to 90d
- Wire expirationSeconds into CreateKeyRequest

Backend changes:
- Require expirationSeconds (reject nil or <= 0)
- Reject values exceeding 1 year with a descriptive error

Also adds .worktrees/ and .claude/worktrees/ to .gitignore.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend tests (Ginkgo, permissions label):
- Reject missing, zero, negative expirationSeconds (400)
- Reject expirationSeconds exceeding 1 year (400)
- Accept exactly 1 year and 90-day values (pass validation)

Frontend tests (Vitest):
- Verify default 90-day expirationSeconds is sent in create mutation
- Verify "No expiration" option is not rendered

Also fixes keys-section.tsx (workspace variant):
- Remove "No expiration" from EXPIRATION_OPTIONS
- Always send expirationSeconds as number (no more undefined)
- Update helper text to mention 1 year max

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extract EXPIRATION_OPTIONS and DEFAULT_EXPIRATION to lib/constants.ts
- Import shared constants in both page.tsx and keys-section.tsx
- Replace raw <select> in page.tsx with shadcn Select component
  (matches keys-section.tsx pattern)

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

coderabbitai bot commented Apr 1, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b3996f37-2e22-48c0-bacc-ae79a876cf04

📥 Commits

Reviewing files that changed from the base of the PR and between d61a8a4 and 019a0f7.

📒 Files selected for processing (1)
  • .github/workflows/daily-sdk-update.yml

📝 Walkthrough

Walkthrough

Frontend adds token lifetime selection and always sends expirationSeconds; backend now requires and validates expirationSeconds (must be >0 and ≤31536000) and includes it when issuing tokens. Minor .gitignore and .coderabbit.yaml whitespace updates; tests added for backend validation.

Changes

Cohort / File(s) Summary
Configuration & Ignores
/.coderabbit.yaml, /.gitignore
Removed trailing space in .coderabbit.yaml instructions; added .claude/worktrees/ to .gitignore.
Backend Validation & Tests
components/backend/handlers/permissions.go, components/backend/handlers/permissions_test.go
CreateProjectKey now requires expirationSeconds, rejects nil/≤0, enforces max 31536000; always sets ExpirationSeconds on token requests. Added tests covering missing, zero/negative, >31536000, and valid boundary cases.
Frontend Constants
components/frontend/src/lib/constants.ts
Added exported EXPIRATION_OPTIONS (values 86400, 604800, 2592000, 7776000, 31536000) and DEFAULT_EXPIRATION ('7776000').
Frontend UI
components/frontend/src/app/projects/[name]/keys/page.tsx
Switched to imported constants; always sends expirationSeconds = Number(newKeyExpiration) (removed "no expiration" omission); disabled Token Lifetime while mutation is pending; removed helper text.

Sequence Diagram

sequenceDiagram
    actor User
    participant Frontend as Frontend UI
    participant API as Backend API
    participant Auth as Auth/Token Service

    User->>Frontend: Select token lifetime and click Create
    Frontend->>Frontend: expirationSeconds = Number(selected)
    Frontend->>API: POST /projects/:name/keys { expirationSeconds }
    API->>API: Validate expirationSeconds (>0 and ≤31536000)
    alt valid
        API->>Auth: Issue token request with ExpirationSeconds
        Auth-->>API: token response
        API-->>Frontend: 200 OK + key data
        Frontend-->>User: Display new key
    else invalid
        API-->>Frontend: 400 Bad Request (validation error)
        Frontend-->>User: Show validation error
    end
Loading
🚥 Pre-merge checks | ✅ 7 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (7 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title follows Conventional Commits format (fix: scope, description) and clearly describes the main change: capping token lifetime and removing never-expire option.
Linked Issues check ✅ Passed The PR implements expiration validation and selector requirements from #1084, but removes the 'no expiration' option due to Kubernetes TokenRequest behavior and enforces 1-year max at backend.
Out of Scope Changes check ✅ Passed All changes (backend validation, frontend selector refactor, .gitignore update, workflow fix) align with either #1084 objectives or necessary supporting infrastructure.
Performance And Algorithmic Complexity ✅ Passed PR introduces no performance regressions or algorithmic complexity concerns. Backend validation is O(1) with single Kubernetes API calls; frontend table rendering uses standard O(n) iteration with proper React hook dependencies.
Security And Secret Handling ✅ Passed PR implements secure secret handling with consistent authentication enforcement, proper token exposure control, input validation via sanitizeName(), and no sensitive data leakage.
Kubernetes Resource Safety ✅ Passed Kubernetes Resource Safety check not applicable; PR contains only application source code and configuration files with no Kubernetes resource manifests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/backend/handlers/permissions.go`:
- Around line 437-450: Move the expirationSeconds validation (the block that
checks req.ExpirationSeconds against maxExpirationSeconds and returns
BadRequest) to run before creating the ServiceAccount and RoleBinding so failed
validation doesn't leave orphaned resources; specifically, perform the
req.ExpirationSeconds nil/<=0 and >maxExpirationSeconds checks prior to the
ServiceAccount creation logic and RoleBinding creation, then remove the
duplicate validation block that remains later in the handler. Ensure you
reference the same symbols (req.ExpirationSeconds and maxExpirationSeconds) when
relocating the checks so behavior and error messages stay identical.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5e84e7a6-4664-4cae-9106-9bba569eac3f

📥 Commits

Reviewing files that changed from the base of the PR and between e4021d2 and 09b16f2.

📒 Files selected for processing (6)
  • .coderabbit.yaml
  • .gitignore
  • components/backend/handlers/permissions.go
  • components/backend/handlers/permissions_test.go
  • components/frontend/src/app/projects/[name]/keys/page.tsx
  • components/frontend/src/lib/constants.ts

Comment thread components/backend/handlers/permissions.go Outdated
- Move token expiration validation before ServiceAccount/RoleBinding creation
- Prevents orphaned Kubernetes resources if validation fails
- Validates expirationSeconds immediately after user identity check

Co-Authored-By: Claude <noreply@anthropic.com>
@ambient-code
Copy link
Copy Markdown
Contributor

ambient-code bot commented Apr 1, 2026

PR Fix Report

Summary

Fixed validation ordering issue identified by CodeRabbit review.

Changes Made

Backend validation ordering fix (components/backend/handlers/permissions.go):

  • Moved expirationSeconds validation to execute before ServiceAccount and RoleBinding creation
  • Prevents orphaned Kubernetes resources if validation fails
  • Validation now runs immediately after user identity check (line 392)

Reviewer Feedback Addressed

CodeRabbit inline comment (components/backend/handlers/permissions.go:450):

  • Fixed: Moved validation earlier to prevent resource orphaning
  • 💬 Responded: Confirmed fix in inline comment reply

Code Review

Ran /simplify review - all checks passed:

  • ✅ No code reuse opportunities (validation is unique, uses standard patterns)
  • ✅ Code quality clean (straightforward refactoring, appropriate comments)
  • ✅ Efficiency improvement (fail-fast prevents wasted K8s API calls)

CI Status

  • ✅ All 28 checks passing before fix
  • ⏳ Waiting for CI to run on new commit

Commits Pushed

1 commit pushed to jeremyeder:fix/access-key-token-lifetime-max-1year:

d61a8a4 fix: move expirationSeconds validation before resource creation

Diff: Moved 15 lines of validation code earlier in the function (from line 437 → line 392)


🤖 Fixed by Ambient Code

@ambient-code ambient-code bot added this to the Review Queue milestone Apr 2, 2026
jeremyeder and others added 2 commits April 2, 2026 11:08
The cron-based daily-sdk-update workflow was being triggered as a
phantom failure on fork PRs due to a GitHub Actions quirk. Add a
job-level guard to skip on pull_request events. Also fix heredoc
syntax that caused check-yaml pre-commit hook failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jeremyeder jeremyeder merged commit f50505a into ambient-code:main Apr 2, 2026
28 checks passed
Gkrumbach07 pushed a commit that referenced this pull request Apr 2, 2026
)

## Summary

Re-fixes #1084. Kubernetes `TokenRequest` does not support non-expiring
tokens — the API server silently caps `ExpirationSeconds`, so tokens
expire regardless. The original PR offered a "No expiration" option that
was misleading.

- **Backend**: Require `expirationSeconds` (reject nil/≤0), reject
values exceeding 1 year (31536000s)
- **Frontend**: Remove "No expiration" option, extract shared
`EXPIRATION_OPTIONS` to `lib/constants.ts`, use shadcn `Select`
component
- **Tests**: 6 backend Ginkgo tests for expiration validation

## Changes

- `components/backend/handlers/permissions.go` — validate and enforce
max 1 year
- `components/backend/handlers/permissions_test.go` — expiration
validation tests
- `components/frontend/src/app/projects/[name]/keys/page.tsx` — use
shared constants, shadcn Select
- `components/frontend/src/lib/constants.ts` — shared
`EXPIRATION_OPTIONS` and `DEFAULT_EXPIRATION`
- `.gitignore` — add `.worktrees/` and `.claude/worktrees/`

## Test plan

- [x] Frontend unit tests pass (613 passed)
- [x] Backend unit tests pass (all packages)
- [x] Backend expiration validation: rejects missing, zero, negative,
>1yr; accepts 1yr and 90d
- [x] Local kind cluster deployed and manually verified
- [x] `tsc --noEmit` clean, all pre-commit hooks pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Project keys now require an expiration time.
  * Predefined token lifetime options added with a 90-day default.

* **Improvements**
  * Maximum token lifetime capped at 1 year.
  * Token lifetime selector is disabled while creating a key.
  * “No expiration” option and related helper text removed.

* **Tests**
* Added validation tests covering expiration presence, bounds, and
accepted values.

* **Chores**
  * Minor config wording and workflow tweaks; gitignore updated.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: ambient-code[bot] <ambient-code[bot]@users.noreply.github.com>
@ambient-code ambient-code bot removed this from the Review Queue milestone Apr 7, 2026
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