Skip to content

feat: per-user API key for REST API authentication#252

Draft
coopernetes wants to merge 1 commit into
mainfrom
feat/api-key-auth
Draft

feat: per-user API key for REST API authentication#252
coopernetes wants to merge 1 commit into
mainfrom
feat/api-key-auth

Conversation

@coopernetes
Copy link
Copy Markdown
Member

Summary

  • Adds per-user proxy-native API keys so users with SELF_CERTIFY can authenticate REST API calls (e.g. self-certify endpoint) from automated pipelines without a browser session
  • Keys are identified via X-Api-Key header — the same header as the existing operator break-glass key, now resolved per-user
  • The key is shown exactly once on generation and never stored in plaintext; SHA-256 hash stored in proxy_users.api_key_hash

What this is: REST API credential for automation (self-certify, etc.)
What this is not: git push authentication — pushes continue to use SCM PATs as before

Changes

  • V6__api_key.sql: adds nullable api_key_hash column to proxy_users
  • UserStore / JdbcUserStore / MongoUserStore / CompositeUserStore: setApiKey, revokeApiKey, findByApiKey, hasApiKey
  • UserApiKeyAuthFilter: Spring OncePerRequestFilter that resolves X-Api-Key to a full Authentication with the user's actual DB roles; wired after the operator ApiKeyAuthFilter in SecurityConfig
  • ProfileController: POST /api/me/api-key (SELF_CERTIFY gated) and DELETE /api/me/api-key
  • AuthController: hasApiKey boolean in /api/me response
  • Profile.tsx: API key section visible to SELF_CERTIFY users — generate, copy-once display, revoke

Test plan

  • 5 new JdbcUserStoreIntegrationTest cases covering set/find/revoke/replace/no-op
  • All existing tests pass, coverage threshold holds
  • Manual: log in as a SELF_CERTIFY user, generate key from Profile page, use X-Api-Key: <key> to call POST /api/push/{id}/authorise — verify it resolves the correct user and roles
  • Manual: revoke key, verify subsequent X-Api-Key requests return 401
  • Manual: non-SELF_CERTIFY user — verify API key section is not shown in Profile

Closes #185

- Add api_key_hash column to proxy_users (V6 migration)
- Add setApiKey/revokeApiKey/findByApiKey/hasApiKey to UserStore
  (JDBC, Mongo, and Composite implementations)
- Add UserApiKeyAuthFilter: resolves X-Api-Key header to a DB user
  via SHA-256 hash, sets full Spring Authentication with actual roles
- Register filter before UsernamePasswordAuthenticationFilter so it
  works with local, LDAP, and OIDC auth providers
- Add POST /api/me/api-key and DELETE /api/me/api-key endpoints
  (key generation gated on ROLE_SELF_CERTIFY; shown once on creation)
- Add hasApiKey flag to GET /api/me response
- Rename operator-key principal from "api-key" to "operator-api-key"
  for clearer audit records
- Resolve reviewerEmail server-side in PushController: prefer locked
  (IdP-sourced) email, fall back to any registered email, null for
  local-auth-no-email and operator key
- Frontend: API key section in Profile, visible only to SELF_CERTIFY
  users; three states: no key, just generated (show once), key active

closes #185

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coopernetes coopernetes marked this pull request as draft May 13, 2026 05:34
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.

feat: user API key for automation (self-certify persona)

1 participant