Add API key authentication to ApiServer - #13
Conversation
|
Interesting insight, I provided my answer through zulip |
There was a problem hiding this comment.
Please revert the formatting changes to this file
| when(req.getHeader("X-API-Key")).thenReturn("secret"); | ||
| assertNull(ApiServer.checkAuth(req, "secret")); | ||
| } | ||
| } No newline at end of file |
There was a problem hiding this comment.
Add a newline here. This can also be covered by Checkstyle eventually.
There was a problem hiding this comment.
Hi @subhramit , thank you for the feedback!
I added a new-line in the most recent changes.
| import static org.junit.Assert.*; | ||
| import static org.mockito.Mockito.*; |
There was a problem hiding this comment.
Don't import *, just import the required methods
There was a problem hiding this comment.
Hi Subhramit.
I appreciate your feedback! I addressed this in the most recent changes.
subhramit
left a comment
There was a problem hiding this comment.
Hey, can you provide some steps to manually test this?
Also can you look into if assertEquals needs explicit boxing for comparison? (Integer.valueOf)
Signed-off-by: Chukwuemeka David <[email protected]>
Signed-off-by: Chukwuemeka David <[email protected]>
Signed-off-by: Chukwuemeka David <[email protected]>
Signed-off-by: Chukwuemeka David <[email protected]>
|
Hi @subhramit. Why assertEquals needs explicit boxing for comparison. The build was failing with a compilation error: "reference to assertEquals is ambiguous". I researched and found out it happened because Because the test was mixing a primitive type (like int) and a wrapper object type (like Long or Integer), Java's automatic type conversion (autoboxing) made both method signatures equally valid. The compiler couldn't determine which version to invoke, making the build fail. Solution: As to the steps to the manual testing, I updated the description of this PR to include it. |
axmsoftware
left a comment
There was a problem hiding this comment.
@thechibuikem Please trace calls from apis-main_controller on the client side to apis-web, and how apis-web calls apis-main. Use debugger on the Java side with break points, and the Developer tools on the Python web app. Thanks!
Signed-off-by: Chukwuemeka David <[email protected]>
Signed-off-by: Chukwuemeka David <[email protected]>
Signed-off-by: Chukwuemeka David <[email protected]>
Hi @axmsoftware. According to my findings from tracing the calls sent by apis-main_controller to apis-web. I discovered that Apis-main_controller interacts with apis-web via the:
Which are at The third port being exposed by apis-web, via the ApiServer (port 9999) at |
Signed-off-by: Chukwuemeka David <[email protected]>
| clean: | ||
| mvn clean | ||
| rm -f *.log *.err | ||
| rm -f *.log *.err No newline at end of file |
There was a problem hiding this comment.
Thanks for the feedback @subhramit, I just resolved this in the latest changes.
Signed-off-by: Chukwuemeka David <[email protected]>
Signed-off-by: Chukwuemeka David <[email protected]>

Addresses hyphae/APIS#91 (item 1: Unauthenticated Control APIs)
Adds timing-safe X-API-Key header check to ApiServer's HTTP request handler, covering all three handlers (ErrorGeneration, DealGeneration, LogConfiguration) since the check runs before dispatch. Returns 500 if apiKey unconfigured, 401 on missing/invalid key.
Tests
Unit Tests
ApiServerAuthTest: missing key, empty key, missing header, wrongheader, valid header.
Manual Testing: ApiServer X-API-Key Authentication
Covers
checkAuth()inApiServer— the shared auth gate for/error,/deal, and/log. Tested against/log, but behavior is identical for the other two endpoints since auth runs before any handler-specific code.Prerequisites
apis-webrunning (Docker or local).envconfigured withDEV_INTERNAL_API_KEYcurlavailableSet once per session for convenience:
HOST=localhost KEY="<your configured DEV_INTERNAL_API_KEY>"Manual Testing: ApiServer X-API-Key Authentication
Covers
checkAuth()inApiServer— the shared auth gate for/error,/deal, and/log. Tested against/log, but behavior is identical for the other two endpoints since auth runs before any handler-specific code.Prerequisites
apis-webrunning (Docker or local).envconfigured withDEV_INTERNAL_API_KEYcurlavailableSet once per session for convenience:
HOST=localhost KEY="<your configured DEV_INTERNAL_API_KEY>"curl -i http://$HOST:9999/log -H "X-API-Key: $KEY"200, HTML form bodycurl -i http://$HOST:9999/log401,unauthorizedcurl -i http://$HOST:9999/log -H "X-API-Key: "401,unauthorizedcurl -i http://$HOST:9999/log -H "X-API-Key: wrong-value"401,unauthorizedDEV_INTERNAL_API_KEY, restart server, thencurl -i http://$HOST:9999/log -H "X-API-Key: anything"500,server misconfiguredScreenshots
Valid key (200):

Missing key (401):

Empty key (401):

Wrong key (401):

Unconfigured server (500):

Notes
checkAuth()usesMessageDigest.isEqualfor constant-time comparison — prevents key-length/prefix leakage via response-time analysis. No need to test this behaviorally; it's a code-level guarantee.ApiServerbefore path dispatch, so this behavior applies identically to/errorand/deal— no need to repeat these cases per-endpoint.DEV_INTERNAL_*env-var resolution, so an unsetDEV_INTERNAL_API_KEYleftapiKeynull past that guard — causing an uncaught NPE instead of a 500. Added a second check after the swap.@FlawzyByte @axmsoftware @subhramit
Follow-up (separate PRs)