Add API Key support for WebBroker APIs#1988
Conversation
📝 WalkthroughAPI Key Support for WebBroker APIsThis PR adds API Key authentication support for WebBroker APIs, including management endpoints and policy handling updates. Documentation
API Key Management Endpoints
Policy Chain Refactoring
Code Generation and Configuration
WalkthroughThis PR adds WebBroker API key lifecycle management endpoints to the gateway controller and consolidates connection initialization policy handling in the event gateway. The API key feature introduces five new HTTP operations (create, list, regenerate, update, revoke) under Sequence Diagram(s)sequenceDiagram
participant Client
participant APIServer as APIServer Handler
participant APIKeyService as API Key Service
participant Storage
Client->>APIServer: POST /webbroker-apis/{id}/api-keys
APIServer->>APIKeyService: CreateAPIKey(ctx, Kind, Handle, CorrelationID)
APIKeyService->>Storage: Create record
Storage-->>APIKeyService: Result or error (404/409/500)
APIKeyService-->>APIServer: Response
APIServer-->>Client: HTTP 201/400/404/409/500
sequenceDiagram
participant Client
participant WebSocket as WebSocket Connector
participant Hub
participant Binding
Client->>WebSocket: WebSocket connect handshake
WebSocket->>Hub: ProcessConnectionInit(ctx, bindingName, msg)
Hub->>Binding: Resolve by name
Hub->>Hub: Apply on_connection_init policies via SubscribeChainKey
alt Short-circuit
Hub->>WebSocket: ImmediateResponse
WebSocket->>Client: Close or respond
else Allowed
Hub->>WebSocket: Modified message with policy headers
WebSocket->>Client: Continue handshake
end
Suggested reviewers
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
event-gateway/gateway-runtime/internal/hub/hub.go (1)
533-562: 💤 Low valueStale comment reference to removed concept.
Line 543 references "on_connection_init.request" but the request/response split has been removed. Consider updating the comment to reflect the unified model.
- // Apply connection_init request policies if present. - if binding.SubscribeChainKey != "" { // Reuse SubscribeChainKey for on_connection_init.request + // Apply on_connection_init policies if present. + if binding.SubscribeChainKey != "" { // Reuse SubscribeChainKey for on_connection_init🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@event-gateway/gateway-runtime/internal/hub/hub.go` around lines 533 - 562, The top-of-function comment for ProcessConnectionInit mentions "on_connection_init.request" and a request/response split that no longer exists; update that comment to describe the unified on_connection_init policy model and how this function applies the policy during connection handshake (used for WebSocket/SSE upgrades) and returns the (possibly mutated) message and short-circuit flag. Also remove or replace the inline comment that reuses SubscribeChainKey for "on_connection_init.request" and instead state that SubscribeChainKey is reused for on_connection_init policy execution so callers can locate the policy invocation (referencing ProcessConnectionInit, binding.SubscribeChainKey, and h.engine.ExecuteRequestHeaderPolicies).event-gateway/gateway-runtime/internal/connectors/receiver/websocket/broker_api_connector.go (1)
255-280: ⚡ Quick winConsider sending a WebSocket close frame with a reason.
When channel-specific
on_connection_initpolicies reject the connection, the code callsws.Close()directly. This results in an abrupt close without any reason being communicated to the client. Sending a close frame with a status code would improve client-side observability.if shortCircuited { slog.Warn("[3] Connection rejected by channel on_connection_init policy", "api", e.channel.Name, "channel", channelName) - ws.Close() + ws.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.ClosePolicyViolation, "connection rejected by policy")) + ws.Close() return }The same applies to the error case at lines 271-273.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@event-gateway/gateway-runtime/internal/connectors/receiver/websocket/broker_api_connector.go` around lines 255 - 280, The connection is closed abruptly by calling ws.Close() in the on_connection_init error and short-circuit branches; instead, send a WebSocket close frame with a status code and human-readable reason before closing. Update the branches around connInitChainKey handling (where e.processor.ProcessByChainKey is called) to construct a CloseMessage (e.g., using websocket.FormatCloseMessage with an appropriate code such as ClosePolicyViolation or CloseNormal and a brief reason that includes e.channel.Name and channelName), write that control message to the socket (e.g., via ws.WriteControl or WriteMessage), then call ws.Close(); apply this change in both the err and shortCircuited branches so clients receive a close frame and reason.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/rest-apis/gateway/webbroker-api-management.md`:
- Around line 801-809: The PUT example for
/webbroker-apis/{id}/api-keys/{apiKeyName} is incorrect: update payloads must
include an "apiKey" property when supplying a custom key and the response
example must show the update result shape/message (not a "generated
successfully" message or quota fields). Update the request example JSON to
include "apiKey": "<custom-value>" and adjust the response example to match the
update response schema used by the API (e.g., an update confirmation/message and
the updated key name/metadata), and apply the same fixes to the duplicate
examples in the 833–849 section so both request and response examples reflect
custom-key update semantics.
- Around line 910-913: Update the 404 response description for the revoke
operation to reflect actual behavior: change the text that currently reads
"WebBroker API or API key not found" to only indicate the WebBroker API is not
found (e.g., "WebBroker API not found"), because missing API keys are treated as
successful revokes; locate the revoke response row in the table (the line with
the 404 status under the revoke operation) and edit the description to mention
only the API resource, not the API key, unless you intend to change the service
behavior to return 404 for missing keys instead.
- Around line 680-697: The sample response shows a full API key in the "apiKeys"
array (object with name "my-production-key") but this endpoint returns masked
keys; update the "apiKey" field in that example to a masked format (keep the
"apip_" prefix then replace the majority of characters with asterisks and
optionally show trailing characters) so the example matches runtime behavior and
the masked-key contract.
---
Nitpick comments:
In
`@event-gateway/gateway-runtime/internal/connectors/receiver/websocket/broker_api_connector.go`:
- Around line 255-280: The connection is closed abruptly by calling ws.Close()
in the on_connection_init error and short-circuit branches; instead, send a
WebSocket close frame with a status code and human-readable reason before
closing. Update the branches around connInitChainKey handling (where
e.processor.ProcessByChainKey is called) to construct a CloseMessage (e.g.,
using websocket.FormatCloseMessage with an appropriate code such as
ClosePolicyViolation or CloseNormal and a brief reason that includes
e.channel.Name and channelName), write that control message to the socket (e.g.,
via ws.WriteControl or WriteMessage), then call ws.Close(); apply this change in
both the err and shortCircuited branches so clients receive a close frame and
reason.
In `@event-gateway/gateway-runtime/internal/hub/hub.go`:
- Around line 533-562: The top-of-function comment for ProcessConnectionInit
mentions "on_connection_init.request" and a request/response split that no
longer exists; update that comment to describe the unified on_connection_init
policy model and how this function applies the policy during connection
handshake (used for WebSocket/SSE upgrades) and returns the (possibly mutated)
message and short-circuit flag. Also remove or replace the inline comment that
reuses SubscribeChainKey for "on_connection_init.request" and instead state that
SubscribeChainKey is reused for on_connection_init policy execution so callers
can locate the policy invocation (referencing ProcessConnectionInit,
binding.SubscribeChainKey, and h.engine.ExecuteRequestHeaderPolicies).
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ed532505-e936-426a-8bab-ffb724149eeb
📒 Files selected for processing (14)
docs/rest-apis/gateway/README.mddocs/rest-apis/gateway/webbroker-api-management.mdevent-gateway/gateway-runtime/internal/binding/types.goevent-gateway/gateway-runtime/internal/connectors/receiver/websocket/broker_api_connector.goevent-gateway/gateway-runtime/internal/connectors/types.goevent-gateway/gateway-runtime/internal/hub/hub.goevent-gateway/gateway-runtime/internal/runtime/runtime.goevent-gateway/gateway-runtime/internal/xdsclient/handler.gogateway/gateway-controller/api/management-openapi.yamlgateway/gateway-controller/cmd/controller/main.gogateway/gateway-controller/pkg/api/handlers/resource_response.gogateway/gateway-controller/pkg/api/handlers/webbroker_api_handler.gogateway/gateway-controller/pkg/api/management/generated.gogateway/gateway-controller/pkg/utils/api_key.go
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
docs/rest-apis/gateway/webbroker-api-management.md (1)
834-850:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate example response message does not match the update operation.
At Line 837, the example says
"API key generated successfully"underPUT /webbroker-apis/{id}/api-keys/{apiKeyName}. This conflicts with the update semantics and the responses table at Line 857 (API key updated successfully).Suggested doc fix
{ "status": "success", - "message": "API key generated successfully", + "message": "API key updated successfully", "remainingApiKeyQuota": 9, "apiKey": {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/rest-apis/gateway/webbroker-api-management.md` around lines 834 - 850, The example JSON response for the PUT /webbroker-apis/{id}/api-keys/{apiKeyName} operation uses the wrong message text; change the "message" field value from "API key generated successfully" to "API key updated successfully" so it matches the update semantics and the responses table; locate the example JSON near the PUT /webbroker-apis/{id}/api-keys/{apiKeyName} heading and update only the "message" string accordingly (leave other fields like "apiKey", "remainingApiKeyQuota", and "status" unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/rest-apis/gateway/webbroker-api-management.md`:
- Line 623: The examples use two different apiId values; replace all instances
of "apiId": "reading-list-api-v1.0" with the consistent value "apiId":
"stock-trading-v1.0" so every WebBroker API example on the page uses the same
API identifier (also update the other occurrences of "reading-list-api-v1.0"
mentioned in the review). Locate and update the string occurrences (search for
"apiId" and "reading-list-api-v1.0") in the document so examples like the API
key snippets match the rest of the page's "stock-trading-v1.0" usage.
---
Duplicate comments:
In `@docs/rest-apis/gateway/webbroker-api-management.md`:
- Around line 834-850: The example JSON response for the PUT
/webbroker-apis/{id}/api-keys/{apiKeyName} operation uses the wrong message
text; change the "message" field value from "API key generated successfully" to
"API key updated successfully" so it matches the update semantics and the
responses table; locate the example JSON near the PUT
/webbroker-apis/{id}/api-keys/{apiKeyName} heading and update only the "message"
string accordingly (leave other fields like "apiKey", "remainingApiKeyQuota",
and "status" unchanged).
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1273cce2-a99f-4836-ae0a-f225f9d02c54
📒 Files selected for processing (2)
docs/rest-apis/gateway/webbroker-api-management.mdgateway/gateway-controller/api/management-openapi.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
- gateway/gateway-controller/api/management-openapi.yaml
Purpose
Engaging API Key auth as a policy is supported with this PR, either at allChannels level, or at a particular channel level.
Example 1: All Channels need API Key auth
{ "apiVersion": "gateway.api-platform.wso2.com/v1alpha1", "kind": "WebBrokerApi", "metadata": { "name": "stock-trading-v1.0" }, "spec": { "displayName": "Stock Trading WebBroker API", "version": "v1.0", "context": "/stock-trading/v1.0", "receiver": { "name": "websocket-receiver", "type": "websocket" }, "broker": { "name": "kafka-driver", "type": "kafka", "properties": { "brokers": [ "kafka:29092" ] } }, "allChannels": { "on_connection_init": { "policies": [ { "name": "api-key-auth", "version": "v1", "params": { "in": "header", "key": "X-API-Key" } } ] }, "on_produce": { "policies": [] }, "on_consume": { "policies": [] } }, "channels": { "prices": { "produceTo": { "topic": "stock.prices" }, "consumeFrom": { "topic": "dummy.prices" }, "on_connection_init": { "policies": [] }, "on_produce": { "policies": [] }, "on_consume": { "policies": [] } } } } }Example:
priceschannel doesn't need an API Key auth (on_connection_init), but thealertschannel needs{ "apiVersion": "gateway.api-platform.wso2.com/v1alpha1", "kind": "WebBrokerApi", "metadata": { "name": "stock-trading-v1.0" }, "spec": { "displayName": "Stock Trading WebBroker API", "version": "v1.0", "context": "/stock-trading/v1.0", "receiver": { "name": "websocket-receiver", "type": "websocket" }, "broker": { "name": "kafka-driver", "type": "kafka", "properties": { "brokers": [ "kafka:29092" ] } }, "allChannels": { "on_connection_init": { "policies": [] }, "on_produce": { "policies": [] }, "on_consume": { "policies": [] } }, "channels": { "prices": { "produceTo": { "topic": "stock.prices" }, "consumeFrom": { "topic": "dummy.prices" }, "on_connection_init": { "policies": [] }, "on_produce": { "policies": [] }, "on_consume": { "policies": [] } }, "alerts": { "produceTo": { "topic": "to.alerts" }, "consumeFrom": { "topic": "from.alerts" }, "on_connection_init": { "policies": [ { "name": "api-key-auth", "version": "v1", "params": { "in": "header", "key": "X-API-Key" } } ] }, "on_produce": { "policies": [] }, "on_consume": { "policies": [] } } } } }Related Issues
#1977 - Since API Key auth is engaged as a policy.