Skip to content

fix: remove apiId from verifyKey request#3666

Merged
Flo4604 merged 2 commits intomainfrom
remove-api-id-param
Jul 28, 2025
Merged

fix: remove apiId from verifyKey request#3666
Flo4604 merged 2 commits intomainfrom
remove-api-id-param

Conversation

@Flo4604
Copy link
Member

@Flo4604 Flo4604 commented Jul 28, 2025

What does this PR do?

removes apiId from key verifciation and
Fixes #3538
Fixes #3611
If there is not an issue for this, please create one first. This is used to tracking purposes and also helps use understand why this PR exists

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • Test A
  • Test B

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

Summary by CodeRabbit

  • New Features
    • Updated key verification API to use versioned response types for improved clarity.
  • Bug Fixes
    • Adjusted error handling so that insufficient permissions now return a 200 OK response with a NOTFOUND code, preventing key existence leakage.
  • Documentation
    • Added a note to the API docs advising users to check root key permissions if they encounter a NOT_FOUND error.
  • Refactor
    • Removed the requirement for the apiId field in key verification requests and updated related schemas and tests accordingly.
    • Renamed and updated response data types and constants to versioned equivalents throughout the API and documentation.
  • Tests
    • Updated and simplified test cases to reflect the removal of the apiId field and revised error codes.
    • Added new tests verifying root key permission enforcement across different APIs and permission scopes.

@vercel
Copy link

vercel bot commented Jul 28, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

2 Skipped Deployments
Name Status Preview Comments Updated (UTC)
dashboard ⬜️ Ignored (Inspect) Visit Preview Jul 28, 2025 10:16am
engineering ⬜️ Ignored (Inspect) Visit Preview Jul 28, 2025 10:16am

@changeset-bot
Copy link

changeset-bot bot commented Jul 28, 2025

⚠️ No Changeset found

Latest commit: 8deea52

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 28, 2025

📝 Walkthrough

Walkthrough

This change removes the apiId field from the V2 verify key request and all related code, types, and OpenAPI schemas. It introduces new versioned response data types, updates all request/response handling, and adjusts the permission-checking logic to prevent leaking the existence of keys by ensuring permission checks occur before key existence is revealed. All relevant tests and service logic are updated accordingly.

Changes

Cohort / File(s) Change Summary
OpenAPI Data Types and Constants Update
go/apps/api/openapi/gen.go
Removed old response data types/constants; introduced V2-prefixed types and constants; updated request/response structs.
OpenAPI Spec and YAMLs
go/apps/api/openapi/openapi-generated.yaml,
go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyRequestBody.yaml,
go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyResponseBody.yaml,
go/apps/api/openapi/spec/paths/v2/keys/verifyKey/index.yaml
Removed apiId from request schema and examples; updated response schema references to new types; added NOT_FOUND error handling note.
Handler Logic
go/apps/api/routes/v2_keys_verify_key/handler.go
Updated to use new response types; removed use of ApiId in request and verification options; adjusted permission check logic to prevent key existence leakage.
Test Updates
go/apps/api/routes/v2_keys_verify_key/200_test.go,
400_test.go,
401_test.go,
403_test.go,
404_test.go,
412_test.go,
multilimit_test.go,
ratelimit_response_test.go
Removed ApiId from all test request payloads; deleted/renamed tests related to ApiId; updated expected error codes and logic for permission errors.
Key Service Option Removal
go/internal/services/keys/options.go
Removed apiID from verifyConfig and deleted WithApiID option function.
Key Service Signature/Logic Update
go/internal/services/keys/status.go,
verifier.go
Updated status mapping to use new response code type; removed logic using apiID in verification.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant APIHandler
    participant KeyService

    Client->>APIHandler: POST /v2/keys/verifyKey { key }
    APIHandler->>KeyService: VerifyKey(key, options)
    KeyService->>APIHandler: Permission check (before key lookup)
    alt Permission denied
        APIHandler-->>Client: 200 OK { code: NOTFOUND, valid: false }
    else Permission granted
        KeyService->>APIHandler: Lookup key
        alt Key found and valid
            APIHandler-->>Client: 200 OK { code: OK, valid: true }
        else Key not found/invalid
            APIHandler-->>Client: 200 OK { code: NOTFOUND, valid: false }
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
Prevent leaking key existence by verifying root key permissions before key lookup (#3538)

Suggested labels

Bug

Suggested reviewers

  • perkinsjr
  • mcstepp
  • MichaelUnkey
  • ogzhanolguncu
  • chronark

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa363da and 8deea52.

📒 Files selected for processing (4)
  • go/apps/api/openapi/openapi-generated.yaml (4 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyResponseData.yaml (1 hunks)
  • go/apps/api/routes/v2_keys_verify_key/200_test.go (15 hunks)
  • go/apps/api/routes/v2_keys_verify_key/403_test.go (0 hunks)
💤 Files with no reviewable changes (1)
  • go/apps/api/routes/v2_keys_verify_key/403_test.go
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: chronark
PR: unkeyed/unkey#3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via `\s`. Do not flag this as an error in future reviews.
Learnt from: chronark
PR: unkeyed/unkey#2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In `apps/api/src/routes/v1_keys_updateKey.ts`, the code intentionally handles `externalId` and `ownerId` separately for clarity. The `ownerId` field will be removed in the future, simplifying the code.
Learnt from: MichaelUnkey
PR: unkeyed/unkey#2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the `v1/keys.updateKey` endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.
Learnt from: Flo4604
PR: unkeyed/unkey#2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.
Learnt from: Flo4604
PR: unkeyed/unkey#3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as `description`) alongside `$ref` in schema objects. Do not flag this as an error in future reviews.
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.
Learnt from: Flo4604
PR: unkeyed/unkey#3151
File: go/apps/api/openapi/gen.go:221-233
Timestamp: 2025-04-18T20:01:33.812Z
Learning: For identity deletion operations in the Unkey API, identityId takes precedence over externalId when both are provided in the request body.
go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyResponseData.yaml (4)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via \s. Do not flag this as an error in future reviews.

Learnt from: Flo4604
PR: #3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as description) alongside $ref in schema objects. Do not flag this as an error in future reviews.

Learnt from: Flo4604
PR: #2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.

Learnt from: MichaelUnkey
PR: #2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the v1/keys.updateKey endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.

go/apps/api/openapi/openapi-generated.yaml (9)

Learnt from: Flo4604
PR: #3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as description) alongside $ref in schema objects. Do not flag this as an error in future reviews.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via \s. Do not flag this as an error in future reviews.

Learnt from: Flo4604
PR: #2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : Do not remove AIDEV-*s without explicit human instruction.

Learnt from: CR
PR: unkeyed/unkey#0
File: go/deploy/CLAUDE.md:0-0
Timestamp: 2025-07-21T18:05:58.236Z
Learning: Applies to go/deploy/**/*.{go,js,ts,tsx,py,sh,md,txt,json,yaml,yml,ini,env,conf,html,css,scss,xml,c,h,cpp,java,rb,rs,php,pl,sql} : Use AIDEV-NOTE:, AIDEV-TODO:, AIDEV-BUSINESS_RULE:, or AIDEV-QUESTION: (all-caps prefix) as anchor comments aimed at AI and developers.

Learnt from: Flo4604
PR: #3647
File: go/apps/api/openapi/openapi-generated.yaml:3569-3575
Timestamp: 2025-07-22T18:09:41.800Z
Learning: In the Unkey codebase, using non-standard HTTP status code 529 for internal-only endpoints is acceptable and should not be flagged as an issue in future reviews.

Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.

go/apps/api/routes/v2_keys_verify_key/200_test.go (7)

Learnt from: MichaelUnkey
PR: #2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the v1/keys.updateKey endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via \s. Do not flag this as an error in future reviews.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.

Learnt from: MichaelUnkey
PR: #3072
File: apps/dashboard/app/(app)/apis/[apiId]/settings/components/update-ip-whitelist.tsx:58-60
Timestamp: 2025-04-14T13:13:13.421Z
Learning: The IP address validation for the UpdateIpWhitelist component is handled in the TRPC route on the server side, not in the client-side component.

Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.

Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.

🧬 Code Graph Analysis (1)
go/apps/api/routes/v2_keys_verify_key/200_test.go (5)
go/pkg/testutil/seed/seed.go (1)
  • CreateApiRequest (78-86)
go/pkg/rbac/permissions.go (1)
  • CreateKey (49-49)
go/pkg/testutil/http.go (1)
  • CallRoute (257-291)
go/apps/api/openapi/gen.go (1)
  • NOTFOUND (38-38)
go/pkg/codes/unkey_data.go (1)
  • Data (87-128)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Build / Build
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Packages / Test
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyResponseData.yaml (1)

2-2: Tightening the schema is good, but double-check downstream writers

additionalProperties: false strictly forbids the backend from returning any field that is not explicitly enumerated.
Before merging, confirm that every code path that serialises a V2KeysVerifyKeyResponseData object has been updated – otherwise previously harmless extra fields (e.g. debug data or legacy fields) will now cause validation failures and 5xx responses when the OpenAPI middleware rejects the payload.

go/apps/api/openapi/openapi-generated.yaml (4)

1-5: Generated-file header looks good

Timestamp/metadata updates are expected for a regenerated bundle.
No action needed.


2775-2780: Good call adding additionalProperties: false

This locks down the wire contract for V2KeysVerifyKeyResponseData, preventing stray fields.
While you’re here, double-check that every field clients must rely on (e.g. valid, any status enum) is listed in a required array so that empty objects are rejected at validation time.


4964-4969: Description tweak is clear and helpful

The extra guidance about NOT_FOUND vs. missing permissions makes the endpoint usage clearer. Thanks for adding it.


1456-1461: No action needed—data is already required
The V2KeysVerifyKeyResponseBody schema already includes both meta and data under its required array. No changes are necessary.

go/apps/api/routes/v2_keys_verify_key/200_test.go (4)

53-53: LGTM! Consistent removal of ApiId field aligns with PR objectives.

The removal of the ApiId field from all handler.Request struct initializations is consistent throughout the test file and aligns with the PR objective to prevent key existence leakage during verification.

Also applies to: 70-70, 95-95, 113-113, 132-132, 151-151, 173-173, 195-195, 218-218, 399-399, 432-432, 457-457, 500-500, 557-557


214-214: LGTM! Correct API assignment for IP whitelist test.

Using ipWhitelistApi.KeyAuthID.String instead of api.KeyAuthID.String ensures this test properly validates the IP whitelist functionality against the correct API that has IP restrictions configured.


571-591: LGTM! Test correctly validates cross-API permission enforcement.

This test verifies that a root key with permissions for one API (api.ID) cannot verify a key from a different API (api2), returning NOT_FOUND instead of a permission error. This aligns with the security objective to prevent key existence leakage.


593-615: LGTM! Test validates insufficient permission handling.

This test ensures that a root key with insufficient permissions (api.*.read instead of api.*.verify_key) returns NOT_FOUND rather than revealing key existence through permission errors. This is consistent with the security enhancement to prevent information leakage.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch remove-api-id-param

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link
Contributor

github-actions bot commented Jul 28, 2025

Thank you for following the naming conventions for pull request titles! 🙏

@graphite-app
Copy link

graphite-app bot commented Jul 28, 2025

Graphite Automations

"Notify author when CI fails" took an action on this PR • (07/28/25)

1 teammate was notified to this PR based on Andreas Thomas's automation.

"Post a GIF when PR approved" took an action on this PR • (07/28/25)

1 gif was posted to this PR based on Andreas Thomas's automation.

Copy link
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

🔭 Outside diff range comments (1)
go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyRequestBody.yaml (1)

4-16: Examples still include removed apiId → schema + examples are now inconsistent

additionalProperties: false forbids unknown fields, yet both example objects still contain apiId, which was deleted from the schema. This causes OpenAPI validation failures and misleads clients reading the docs.

@@
       apiId: api_1234abcd
       key: sk_1234abcdef
@@
       apiId: api_1234abcd
       key: sk_1234abcdef

Remove the two apiId lines (and adjust any accompanying description text) so the examples align with the new contract.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8eab750 and fa363da.

📒 Files selected for processing (17)
  • go/apps/api/openapi/gen.go (2 hunks)
  • go/apps/api/openapi/openapi-generated.yaml (4 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyRequestBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyResponseBody.yaml (1 hunks)
  • go/apps/api/openapi/spec/paths/v2/keys/verifyKey/index.yaml (1 hunks)
  • go/apps/api/routes/v2_keys_verify_key/200_test.go (14 hunks)
  • go/apps/api/routes/v2_keys_verify_key/400_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_verify_key/401_test.go (1 hunks)
  • go/apps/api/routes/v2_keys_verify_key/403_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_verify_key/404_test.go (2 hunks)
  • go/apps/api/routes/v2_keys_verify_key/412_test.go (3 hunks)
  • go/apps/api/routes/v2_keys_verify_key/handler.go (4 hunks)
  • go/apps/api/routes/v2_keys_verify_key/multilimit_test.go (13 hunks)
  • go/apps/api/routes/v2_keys_verify_key/ratelimit_response_test.go (3 hunks)
  • go/internal/services/keys/options.go (0 hunks)
  • go/internal/services/keys/status.go (1 hunks)
  • go/internal/services/keys/verifier.go (0 hunks)
💤 Files with no reviewable changes (2)
  • go/internal/services/keys/verifier.go
  • go/internal/services/keys/options.go
🧰 Additional context used
🧠 Learnings (15)
📓 Common learnings
Learnt from: chronark
PR: unkeyed/unkey#3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via `\s`. Do not flag this as an error in future reviews.
Learnt from: chronark
PR: unkeyed/unkey#2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In `apps/api/src/routes/v1_keys_updateKey.ts`, the code intentionally handles `externalId` and `ownerId` separately for clarity. The `ownerId` field will be removed in the future, simplifying the code.
Learnt from: MichaelUnkey
PR: unkeyed/unkey#2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the `v1/keys.updateKey` endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.
Learnt from: Flo4604
PR: unkeyed/unkey#2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.
Learnt from: Flo4604
PR: unkeyed/unkey#3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as `description`) alongside `$ref` in schema objects. Do not flag this as an error in future reviews.
Learnt from: chronark
PR: unkeyed/unkey#3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.
Learnt from: Flo4604
PR: unkeyed/unkey#3151
File: go/apps/api/openapi/gen.go:221-233
Timestamp: 2025-04-18T20:01:33.812Z
Learning: For identity deletion operations in the Unkey API, identityId takes precedence over externalId when both are provided in the request body.
go/apps/api/openapi/spec/paths/v2/keys/verifyKey/index.yaml (3)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via \s. Do not flag this as an error in future reviews.

Learnt from: Flo4604
PR: #3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as description) alongside $ref in schema objects. Do not flag this as an error in future reviews.

Learnt from: MichaelUnkey
PR: #2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the v1/keys.updateKey endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.

go/apps/api/routes/v2_keys_verify_key/401_test.go (2)

Learnt from: MichaelUnkey
PR: #2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the v1/keys.updateKey endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

go/apps/api/routes/v2_keys_verify_key/multilimit_test.go (6)

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.

Learnt from: chronark
PR: #2126
File: apps/api/src/routes/v1_ratelimit_getOverride.happy.test.ts:36-36
Timestamp: 2024-11-13T19:06:36.786Z
Learning: In the rate limit test files (e.g., apps/api/src/routes/v1_ratelimit_getOverride.happy.test.ts), URL parameters like namespaceId and identifier do not need to be URL-encoded in the test code because the values used are always considered safe within the test environment.

Learnt from: MichaelUnkey
PR: #2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the v1/keys.updateKey endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.

Learnt from: chronark
PR: #3474
File: go/apps/api/routes/v2_keys_verify_key/200_test.go:380-380
Timestamp: 2025-07-14T08:15:56.747Z
Learning: In the Unkey codebase, there is a mechanism to set the server's time via a header for test control, which helps make ratelimit tests deterministic instead of time-dependent. This is useful for preventing flaky tests where the second request might hit a new ratelimit window.

Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.

go/apps/api/routes/v2_keys_verify_key/412_test.go (4)

Learnt from: MichaelUnkey
PR: #2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the v1/keys.updateKey endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.

Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.

go/apps/api/routes/v2_keys_verify_key/404_test.go (2)

Learnt from: MichaelUnkey
PR: #2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the v1/keys.updateKey endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

go/apps/api/routes/v2_keys_verify_key/ratelimit_response_test.go (5)

Learnt from: chronark
PR: #2126
File: apps/api/src/routes/v1_ratelimit_getOverride.happy.test.ts:36-36
Timestamp: 2024-11-13T19:06:36.786Z
Learning: In the rate limit test files (e.g., apps/api/src/routes/v1_ratelimit_getOverride.happy.test.ts), URL parameters like namespaceId and identifier do not need to be URL-encoded in the test code because the values used are always considered safe within the test environment.

Learnt from: chronark
PR: #3474
File: go/apps/api/routes/v2_keys_verify_key/200_test.go:380-380
Timestamp: 2025-07-14T08:15:56.747Z
Learning: In the Unkey codebase, there is a mechanism to set the server's time via a header for test control, which helps make ratelimit tests deterministic instead of time-dependent. This is useful for preventing flaky tests where the second request might hit a new ratelimit window.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.

Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.

go/apps/api/routes/v2_keys_verify_key/400_test.go (5)

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: MichaelUnkey
PR: #2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the v1/keys.updateKey endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via \s. Do not flag this as an error in future reviews.

Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.

Learnt from: Flo4604
PR: #2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.

go/apps/api/routes/v2_keys_verify_key/200_test.go (6)

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: MichaelUnkey
PR: #2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the v1/keys.updateKey endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via \s. Do not flag this as an error in future reviews.

Learnt from: Flo4604
PR: #2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.

Learnt from: MichaelUnkey
PR: #3072
File: apps/dashboard/app/(app)/apis/[apiId]/settings/components/update-ip-whitelist.tsx:58-60
Timestamp: 2025-04-14T13:13:13.421Z
Learning: The IP address validation for the UpdateIpWhitelist component is handled in the TRPC route on the server side, not in the client-side component.

Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.

go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyRequestBody.yaml (7)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via \s. Do not flag this as an error in future reviews.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: Flo4604
PR: #3151
File: go/apps/api/openapi/gen.go:221-233
Timestamp: 2025-04-18T20:01:33.812Z
Learning: For identity deletion operations in the Unkey API, identityId takes precedence over externalId when both are provided in the request body.

Learnt from: Flo4604
PR: #3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as description) alongside $ref in schema objects. Do not flag this as an error in future reviews.

Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.

Learnt from: Flo4604
PR: #2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.

Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:468-581
Timestamp: 2025-07-15T14:47:20.490Z
Learning: In the Unkey codebase, role and permission names are validated at the OpenAPI schema layer with strict regex patterns: role names must match "^[a-zA-Z][a-zA-Z0-9_-]*$" (start with letter, followed by letters/numbers/underscores/hyphens) and permission names must match "^[a-zA-Z0-9_]+$" (letters, numbers, underscores only). This validation occurs during zen.BindBody call before handlers run, preventing malicious or improperly formatted names from reaching auto-creation logic.

go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyResponseBody.yaml (2)

Learnt from: Flo4604
PR: #3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as description) alongside $ref in schema objects. Do not flag this as an error in future reviews.

Learnt from: MichaelUnkey
PR: #2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the v1/keys.updateKey endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.

go/apps/api/routes/v2_keys_verify_key/handler.go (1)

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via \s. Do not flag this as an error in future reviews.

go/apps/api/routes/v2_keys_verify_key/403_test.go (1)

Learnt from: MichaelUnkey
PR: #2114
File: apps/api/src/routes/v1_keys_updateKey.error.test.ts:0-0
Timestamp: 2024-09-27T15:20:05.475Z
Learning: In the v1/keys.updateKey endpoint, the server validates the refill configuration before checking if the key exists. Therefore, tests can assert validation errors without needing to create the key first.

go/apps/api/openapi/openapi-generated.yaml (5)

Learnt from: Flo4604
PR: #3421
File: go/apps/api/openapi/openapi.yaml:196-200
Timestamp: 2025-07-03T05:58:10.699Z
Learning: In the Unkey codebase, OpenAPI 3.1 is used, which allows sibling keys (such as description) alongside $ref in schema objects. Do not flag this as an error in future reviews.

Learnt from: chronark
PR: #3617
File: go/apps/api/openapi/openapi.yaml:3309-3312
Timestamp: 2025-07-16T17:51:57.297Z
Learning: In the Unkey API OpenAPI schema, the permissions query regex for the verifyKey endpoint intentionally allows all whitespace characters (including tabs and newlines) via \s. Do not flag this as an error in future reviews.

Learnt from: Flo4604
PR: #2955
File: go/apps/api/routes/v2_identities_create_identity/handler.go:162-202
Timestamp: 2025-03-19T09:25:59.751Z
Learning: In the Unkey codebase, input validation for API endpoints is primarily handled through OpenAPI schema validation, which occurs before requests reach the handler code. For example, in the identities.createIdentity endpoint, minimum values for ratelimit duration and limit are defined in the OpenAPI schema rather than duplicating these checks in the handler.

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

Learnt from: chronark
PR: #3560
File: go/apps/api/routes/v2_keys_create_key/handler.go:353-466
Timestamp: 2025-07-15T14:25:05.608Z
Learning: In the Unkey codebase, input validation for API endpoints is handled at the OpenAPI schema layer, which validates request fields like permission slugs (pattern: "^[a-zA-Z0-9_]+$", length: 1-100 characters) before requests reach the handler code. This validation occurs during the zen.BindBody call in handlers.

go/apps/api/openapi/gen.go (1)

Learnt from: chronark
PR: #2693
File: apps/api/src/routes/v1_keys_updateKey.ts:350-368
Timestamp: 2024-11-29T15:15:47.308Z
Learning: In apps/api/src/routes/v1_keys_updateKey.ts, the code intentionally handles externalId and ownerId separately for clarity. The ownerId field will be removed in the future, simplifying the code.

🧬 Code Graph Analysis (1)
go/apps/api/openapi/gen.go (1)
go/pkg/codes/unkey_data.go (1)
  • Data (87-128)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Build / Build
  • GitHub Check: Test Packages / Test
  • GitHub Check: autofix
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (23)
go/apps/api/openapi/spec/paths/v2/keys/verifyKey/index.yaml (1)

22-23: Excellent documentation improvement for the security fix.

This note clearly guides users on how to troubleshoot NOT_FOUND errors, which is especially important since the API now returns NOT_FOUND instead of other error codes to prevent key existence leakage. This aligns perfectly with the security objectives of PR #3666.

go/apps/api/routes/v2_keys_verify_key/404_test.go (1)

40-40: LGTM! Test updates correctly reflect API contract changes.

The removal of ApiId from the request structures aligns with the broader refactor to remove apiId from the verifyKey endpoint. The test logic and assertions remain appropriate for the updated API schema.

Also applies to: 57-57

go/apps/api/routes/v2_keys_verify_key/412_test.go (1)

59-59: LGTM! Consistent test updates across all precondition failed scenarios.

The removal of ApiId from all request structures correctly reflects the updated API schema. The test coverage for ratelimit scenarios remains comprehensive and appropriate.

Also applies to: 92-92, 115-115

go/apps/api/routes/v2_keys_verify_key/401_test.go (1)

35-35: LGTM! Test update aligns with API contract changes.

The removal of ApiId from the request structure is consistent with the broader refactor. The unauthorized access test scenarios remain comprehensive and properly structured.

go/apps/api/routes/v2_keys_verify_key/400_test.go (1)

59-59: LGTM! Comprehensive test updates maintain good coverage.

The removal of ApiId from request structures and the elimination of the "missing apiId" test case correctly reflect the updated API contract. The remaining test cases provide excellent coverage for validation scenarios, including the detailed permissions query syntax testing.

Also applies to: 83-83

go/apps/api/routes/v2_keys_verify_key/multilimit_test.go (1)

59-60: LGTM! Consistent removal of ApiId field from test requests.

The removal of the ApiId field from all handler.Request struct initializations is correct and aligns with the PR objective to remove apiId from the verifyKey request. The test coverage remains comprehensive while adapting to the simplified request structure.

Also applies to: 94-95, 147-149, 196-198, 252-254, 266-268, 310-312, 325-327, 339-341, 370-371, 418-419, 433-434, 448-449

go/apps/api/routes/v2_keys_verify_key/200_test.go (1)

52-53: LGTM! Systematic removal of ApiId field from all test cases.

The removal of the ApiId field from all handler.Request struct initializations is consistent and correct. The test scenarios maintain their comprehensive coverage while adapting to the simplified request structure that no longer requires apiId. The change on line 214 to use ipWhitelistApi.KeyAuthID.String is appropriate for the IP whitelist test scenario.

Also applies to: 69-70, 94-95, 112-113, 131-132, 150-151, 172-173, 194-195, 217-218, 245-248, 269-272, 318-321, 371-373, 398-399, 430-434, 455-459, 498-502, 556-557

go/apps/api/routes/v2_keys_verify_key/ratelimit_response_test.go (1)

54-55: LGTM! Consistent ApiId field removal from rate limit tests.

The removal of the ApiId field from all handler.Request struct initializations maintains consistency with the broader refactor. The rate limit response validation logic remains intact and comprehensive.

Also applies to: 94-95, 124-125

go/internal/services/keys/status.go (1)

123-123: LGTM! Updated return type for versioned OpenAPI response codes.

The update from openapi.KeysVerifyKeyResponseDataCode to openapi.V2KeysVerifyKeyResponseDataCode correctly aligns with the migration to versioned response data types. The internal mapping logic remains unchanged, ensuring behavioral consistency while adapting to the new type system.

go/apps/api/openapi/spec/paths/v2/keys/verifyKey/V2KeysVerifyKeyResponseBody.yaml (1)

9-9: LGTM! Updated schema reference for versioned response data.

The update from KeysVerifyKeyResponseData.yaml to V2KeysVerifyKeyResponseData.yaml correctly aligns the OpenAPI schema with the migration to versioned response data types. This ensures consistency between the API specification and the generated code.

go/apps/api/openapi/openapi-generated.yaml (3)

1-5: Double-check committing generated artifacts

openapi-generated.yaml is produced by generate_bundle.go; committing it can cause noisy diffs and merge conflicts. Confirm that keeping the generated bundle under VCS is still the desired workflow for this repo.


1456-1460: Schema reference correctly updated to versioned type

The response now points to V2KeysVerifyKeyResponseData, matching the new versioned model. ✅


4963-4968: Description Formatting Verified – No Errors

Ran npx @redocly/cli lint go/apps/api/openapi/openapi-generated.yaml; the file validates successfully (no errors, 15 non-blocking warnings). The description: is correctly emitted as a single YAML scalar. No further changes required.

go/apps/api/routes/v2_keys_verify_key/handler.go (4)

75-75: LGTM: Consistent response type migration to versioned API.

The migration from openapi.KeysVerifyKeyResponseData to openapi.V2KeysVerifyKeyResponseData is consistent across all response locations and follows proper API versioning conventions.

Also applies to: 89-89, 116-116, 162-162


109-121: Excellent security improvement: Prevents key existence leakage.

The change to return a 200 OK response with NOTFOUND code instead of a direct error response effectively prevents attackers from distinguishing between "key doesn't exist" and "insufficient permissions" scenarios. This eliminates a potential information disclosure vulnerability.


123-123: LGTM: Correctly removes apiId from verification options.

The removal of keys.WithApiID(req.ApiId) from the verification options aligns with the PR objective to eliminate apiId from the verification process while preserving other relevant options.


96-107: Security model correctly preserved despite apiId removal.

The permission verification logic correctly uses the key's actual API ID from the database (line 104) rather than relying on user-provided input. This maintains proper authorization while eliminating the attack vector for key existence probing.

go/apps/api/routes/v2_keys_verify_key/403_test.go (3)

36-42: LGTM: Test accurately reflects the new permission model.

The test rename and restructuring correctly simulate a scenario where a root key has permissions for one API but attempts to verify a key from a different API. This provides better test coverage of the permission boundary enforcement.


44-46: LGTM: Request structure correctly updated.

The removal of ApiId from the request structures aligns with the updated API contract and handler implementation.

Also applies to: 62-64


54-54: LGTM: Test expectations correctly updated for security improvement.

The change from expecting FORBIDDEN to NOTFOUND response codes correctly validates the new behavior that prevents key existence leakage by returning consistent responses for both non-existent keys and permission failures.

Also applies to: 74-74

go/apps/api/openapi/gen.go (3)

31-42: LGTM! Well-defined response status codes.

The V2KeysVerifyKeyResponseDataCode constants provide comprehensive coverage of all possible verification states. The naming is consistent and descriptive, making it clear what each status represents.


1478-1538: LGTM! Comprehensive response data structure with excellent documentation.

The V2KeysVerifyKeyResponseData struct is well-designed with:

  • Clear field documentation explaining the purpose and behavior of each field
  • Proper use of pointer types for optional fields
  • Comprehensive coverage of all verification scenarios
  • Good balance between providing necessary information and avoiding key existence leakage

The structure effectively supports the PR's objective of preventing information leakage while maintaining a rich response format for legitimate verification scenarios.


1470-1476: LGTM! Proper response body structure using versioned types.

The V2KeysVerifyKeyResponseBody correctly uses the new V2KeysVerifyKeyResponseData type, maintaining consistency with the versioned approach for this endpoint.

Copy link
Collaborator

@chronark chronark left a comment

Choose a reason for hiding this comment

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

let's address the rabbit comment and merge

@Flo4604 Flo4604 enabled auto-merge July 28, 2025 10:15
@Flo4604 Flo4604 requested a review from chronark July 28, 2025 10:15
@graphite-app
Copy link

graphite-app bot commented Jul 28, 2025

TV gif. Mary Kate or Ashley Olsen as Michelle on Full House, giving a thumbs up and saying 'you got it dude.' (Added via Giphy)

@Flo4604 Flo4604 added this pull request to the merge queue Jul 28, 2025
Merged via the queue into main with commit 1a97c5c Jul 28, 2025
25 of 26 checks passed
@Flo4604 Flo4604 deleted the remove-api-id-param branch July 28, 2025 12:09
@coderabbitai coderabbitai bot mentioned this pull request Jul 28, 2025
18 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

/v2/keys/verifyKey api selection Don't leak key existence by verifying root key permissions.

2 participants