Require bearer token for writing API endpoints (#522)#544
Open
maltehuebner wants to merge 1 commit into
Open
Conversation
The writing REST endpoints (PUT /api/station, POST /api/station/{code},
PUT /api/city, POST /api/{citySlug}, PUT /api/value) were completely
unauthenticated, allowing anyone to create/overwrite stations, cities and
measurements.
Add App\EventSubscriber\ApiWriteAuthenticationSubscriber, a single
kernel.request enforcement point that challenges every write method
(POST/PUT/PATCH/DELETE) below /api with a shared bearer token
("Authorization: Bearer <token>"). The token is configured via the
API_WRITE_TOKEN env var and compared with hash_equals. Read endpoints
(GET/HEAD, /api/doc) stay public.
Fails closed: when API_WRITE_TOKEN is unset, all writes are rejected
(403). Missing token => 401, wrong token => 403.
Providers must be updated to send the token (tracked separately in
luft-api-bundle). A full symfony/security-bundle firewall with per-client
tokens remains a possible follow-up.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the critical Broken Access Control issue: all writing REST endpoints below
/api(PUT /api/station,POST /api/station/{stationCode},PUT /api/city,POST /api/{citySlug},PUT /api/value) were completely unauthenticated and persisted straight to the database.Changes
App\EventSubscriber\ApiWriteAuthenticationSubscriber— a singlekernel.requestenforcement point (priority 8, runs after routing, before the controller). It challenges every write method (POST/PUT/PATCH/DELETE) whose path is/apior starts with/api/, and leaves read requests (GET/HEAD),/api/doc, and everything outside/apiuntouched. Also automatically covers any future write endpoint under/api.API_WRITE_TOKENenv var, bound inconfig/services.yaml, sent by clients asAuthorization: Bearer <token>, and compared withhash_equals()(constant time)..env: documentedAPI_WRITE_TOKEN(empty default).CLAUDE.md: updated the API and Security notes.tests/EventSubscriber/ApiWriteAuthenticationSubscriberTest.php(10 cases) covering read pass-through,/api/doc, valid token, missing token (401), wrong token (403), non-bearer header, unconfigured token (fail closed), and sub-request bypass.Behaviour
API_WRITE_TOKENis unset/empty, every write is rejected with403.401 Unauthorized(withWWW-Authenticate: Bearer).403 Forbidden.Deliberately not implemented
symfony/security-bundlefirewall with per-client credentials. The issue lists that as one option; a shared token (option 2) is the minimal, self-contained fix and is noted as the chosen approach. A firewall remains a reasonable follow-up.provider-*/luft-api-bundle) and are tracked there. Until they send the token, writes fail closed by design.Verification
vendor/bin/phpstan analyse— no errors.vendor/bin/phpunit --testsuite="Project Test Suite"— 131 tests green (includes the new subscriber test).bin/console lint:container --env=prod— OK (token binding wires cleanly).Closes #522
🤖 Generated with Claude Code