Skip to content

fix(api): allow bearer API keys for POST /api/secrets#514

Open
viprapp wants to merge 2 commits intoHemmeligOrg:v7from
viprapp:fix/api-key-secret-create-auth
Open

fix(api): allow bearer API keys for POST /api/secrets#514
viprapp wants to merge 2 commits intoHemmeligOrg:v7from
viprapp:fix/api-key-secret-create-auth

Conversation

@viprapp
Copy link
Copy Markdown

@viprapp viprapp commented Mar 23, 2026

Summary

This fixes an inconsistency in the secrets API where bearer API keys worked for listing secrets, but not for creating them when requireRegisteredUser=true.

Before this change, POST /api/secrets only relied on the existing session user in the request context. That meant browser-based requests could succeed thanks to a session cookie, while bearer-only clients received Only registered users can create secrets even with a valid API key.

What changed

  • extracted API key header authentication into shared helpers in api/middlewares/auth.ts
  • added optionalApiKeyOrAuthMiddleware
  • applied optional auth to POST /api/secrets
  • updated OpenAPI and docs to reflect the actual supported auth modes
  • documented the expected JSON-serialized Uint8Array payload format for secret and title
  • added Hurl coverage for:
    • valid bearer auth
    • no auth with requireRegisteredUser=true
    • malformed bearer auth
    • well-formed but invalid bearer auth
    • anonymous creation when requireRegisteredUser=false
    • existing GET /api/secrets bearer behavior

Why this approach

Using the existing strict middleware directly on POST /api/secrets would have broken anonymous creation entirely. The new optional middleware preserves current behavior:

  • if anonymous creation is allowed, requests without auth still work
  • if requireRegisteredUser=true, a valid session or bearer API key satisfies the requirement
  • invalid bearer headers still fail with 401

Result

POST /api/secrets now behaves consistently with the rest of the API and works properly for bearer-based automation clients, while keeping anonymous creation intact when enabled.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added API key authentication via Bearer tokens for programmatic API access.
    • Introduced optional authentication for secret creation and listing endpoints.
  • Bug Fixes

    • Improved error handling for authentication failures and duplicate secret creation conflicts.
  • Tests

    • Added comprehensive test coverage for API key authentication scenarios.
  • Documentation

    • Updated API specifications and examples to reflect Bearer token authentication support.

Add optional API key auth for POST /api/secrets so bearer tokens can satisfy
requireRegisteredUser without breaking anonymous creation when it is disabled.

- extract API key authentication into shared helpers
- add optionalApiKeyOrAuthMiddleware for auth-optional routes
- apply optional auth to secret creation
- document Uint8Array payload format
- add Hurl coverage for bearer, anonymous, and invalid auth cases
@viprapp viprapp requested a review from bjarneo as a code owner March 23, 2026 18:15
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 23, 2026

📝 Walkthrough

Walkthrough

This PR introduces bearer token API-key authentication for secret creation and listing endpoints. The auth middleware is refactored to extract shared API-key validation logic and adds optional authentication support. OpenAPI documentation is updated to reflect bearer token support and request body schema changes to Uint8Array format. Comprehensive tests and documentation examples demonstrate the new authentication flow.

Changes

Cohort / File(s) Summary
Authentication Middleware
api/middlewares/auth.ts
Extracted API-key validation and header authentication helpers (authenticateApiKeyHeader(), setApiKeyUserFromHeader()). Refactored apiKeyOrAuthMiddleware to use new helpers. Added new optionalApiKeyOrAuthMiddleware that attempts session first, then API-key auth only if Authorization header exists.
API Specification
api/openapi.ts
Updated GET /secrets and POST /secrets security schemes to include bearer token authentication (bearerAuth). Modified POST /secrets request schema to represent secret and title as JSON-serialized Uint8Array objects (type: 'object' with integer properties 0–255) instead of strings.
Secrets Route
api/routes/secrets.ts
Updated POST / to use optionalApiKeyOrAuthMiddleware, refactored handler structure. Added explicit Prisma P2002 unique-constraint error handling (returns HTTP 409). Standardized non-constraint error responses to HTTP 500.
Tests & Documentation
api/tests/secrets-create-auth.hurl, docs/api.md
Added comprehensive Hurl test covering bearer token auth scenarios (valid key, invalid format, malformed token, anonymous creation). Updated API documentation with bearer token header example and Uint8Array serialization format notes.

Sequence Diagram

sequenceDiagram
    participant Client
    participant PostRoute as POST /secrets Route
    participant OptAuthMW as optionalApiKeyOrAuthMiddleware
    participant AuthHelper as Auth Helper Functions
    participant Prisma as Prisma (DB)
    participant SecretHandler as Secret Creation Handler

    Client->>PostRoute: POST with Authorization: Bearer {key}
    PostRoute->>OptAuthMW: Check authentication
    
    OptAuthMW->>OptAuthMW: Check for session (absent)
    
    OptAuthMW->>OptAuthMW: Check Authorization header (present)
    OptAuthMW->>AuthHelper: authenticateApiKeyHeader()
    
    AuthHelper->>AuthHelper: Extract & validate key prefix (hemmelig_)
    AuthHelper->>AuthHelper: SHA-256 hash the key
    
    AuthHelper->>Prisma: Lookup by keyHash (include user)
    Prisma-->>AuthHelper: Return apiKey record with user
    
    AuthHelper->>Prisma: Update lastUsedAt asynchronously
    
    AuthHelper-->>OptAuthMW: Return { user }
    
    OptAuthMW->>OptAuthMW: Set c.set('user', user)
    OptAuthMW->>SecretHandler: Proceed to next() with user context
    
    SecretHandler->>SecretHandler: Validate body, check permissions
    SecretHandler->>Prisma: Create secret record
    Prisma-->>SecretHandler: Confirm creation
    
    SecretHandler-->>Client: 200 { id, ... }
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A bearer token hops through the gate,
Keys hashed with purpose, don't be late!
Optional auth lets both in to play—
Registered or free, either way!
Uint8Array secrets, sealed with care,
The API's now dancing through the air!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: enabling bearer API keys for the POST /api/secrets endpoint.

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

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

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

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.

🧹 Nitpick comments (1)
api/routes/secrets.ts (1)

281-295: Consider whether exposing constraint details is intentional.

The P2002 error handler returns details: prismaError.meta?.target, which exposes internal database field names to API consumers. While useful for debugging, this could leak schema information.

Given that the Secrets model only has the auto-generated UUID id as a unique constraint (per prisma/schema.prisma), this code path is effectively unreachable under normal operation. If kept for defensive purposes, consider removing the details field or logging it server-side only.

🔧 Optional: Remove internal details from error response
                 if (
                     error &&
                     typeof error === 'object' &&
                     'code' in error &&
                     error.code === 'P2002'
                 ) {
-                    const prismaError = error as { meta?: { target?: string } };
                     return c.json(
                         {
                             error: 'Could not create secrets',
-                            details: prismaError.meta?.target,
                         },
                         409
                     );
                 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/routes/secrets.ts` around lines 281 - 295, Remove the internal DB field
detail from the P2002 response in the P2002 error handler in
api/routes/secrets.ts: instead of returning prismaError.meta?.target to the
client, omit the "details" field from the JSON response (keep the 409 and the
public message) and log the prismaError.meta?.target server-side using your
existing logger (refer to the prismaError variable in that P2002 branch) so
schema info is not exposed to API consumers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@api/routes/secrets.ts`:
- Around line 281-295: Remove the internal DB field detail from the P2002
response in the P2002 error handler in api/routes/secrets.ts: instead of
returning prismaError.meta?.target to the client, omit the "details" field from
the JSON response (keep the 409 and the public message) and log the
prismaError.meta?.target server-side using your existing logger (refer to the
prismaError variable in that P2002 branch) so schema info is not exposed to API
consumers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fbca057e-cf6c-4430-90dd-f5fb133c8820

📥 Commits

Reviewing files that changed from the base of the PR and between 4f18ad9 and 0185b61.

📒 Files selected for processing (5)
  • api/middlewares/auth.ts
  • api/openapi.ts
  • api/routes/secrets.ts
  • api/tests/secrets-create-auth.hurl
  • docs/api.md

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