fix: read Authorization via getallheaders() when Apache withholds it from $_SERVER - #24
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates RequestContext::fromGlobals() to correctly resolve the Authorization header on Apache/mod_php deployments by falling back to raw request headers (getallheaders()) when CGI-style $_SERVER variables don’t include it.
Changes:
- Added
RequestContext::resolveAuthorizationHeader()to centralize Authorization resolution with sensible precedence. - Updated
fromGlobals()to use the helper and optionally readgetallheaders(). - Added unit tests covering precedence, fallbacks, case-insensitive header name matching, and null behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/Http/RequestContext.php |
Adds Authorization resolution helper and uses getallheaders() as a fallback in fromGlobals(). |
tests/Http/RequestContextTest.php |
Adds focused unit tests for the new Authorization resolution behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
tomasstark
added a commit
that referenced
this pull request
Jul 14, 2026
This PR adds `demo/self-report/` — a minimal vanilla-PHP publisher for
testing the `/.well-known/supertab/status` self-report endpoint
end-to-end against the real sandbox API, deployed as a persistent public
target on **Fly.io** (hostname kept internal; see the sandbox
merchant-site registration). It doubles as the canonical "plain PHP"
integration reference: every request flows through
`SupertabConnect::handleRequest()`, so the status endpoint works with
zero endpoint-specific code.
## Context
The backend's live-health probe needs a real, registered origin to mint
challenges against (`aud` = origin, signed by the platform JWKS). The
existing `demo/` CLI demo is self-contained with a mock API, so it can't
exercise this. This app pins the **released Packagist SDK**
(`1.4.0-beta.9`, which ships the `component: {kind: "php-sdk"}`
identity) — validating a new release is a pin bump + `fly deploy`.
## Key Changes
- `index.php` (~100 lines, no framework): `X-Forwarded-Proto` scheme
fix-up (TLS-terminating proxies forward plain HTTP; without it every
probe's `aud` mismatches and gets the decoy) → `/healthz` short-circuit
(health checks never look like traffic) → everything else through
`handleRequest()`, emitting `Allow` as a demo HTML page and
`Block`/`Respond` verbatim.
- Env-var config: `SUPERTAB_MERCHANT_API_KEY` (required, fail-fast 500),
`SUPERTAB_BASE_URL` (defaults sandbox), `SUPERTAB_ENFORCEMENT`,
`SUPERTAB_ANALYTICS` — so the payload's `enforcement`/`eventReporting`
can be flipped per test without code changes.
- `Dockerfile`: multi-stage composer install from the committed
lockfile; `php:8.3-apache` on port 8080 with `FallbackResource
/index.php` (dot-prefixed paths route to the front controller without
mod_rewrite) and a `SetEnvIf` restoring the `Authorization` header,
which Apache/mod_php withholds from `$_SERVER` (root fix in #24).
- `fly.toml`: one always-warm machine (`auto_stop_machines = 'off'`,
`min_machines_running = 1`) so backend probes never hit a cold start;
HTTP health check on `/healthz`.
- `DEPLOY.md`/`README.md`: Fly runbook — setup, smoke checks, sandbox
registration, update cadence.
Verified end-to-end: the deployed instance passed the backend's live
self-report check — challenge verified against the sandbox platform
JWKS, `200` payload with `component: {kind: "php-sdk", version:
"v1.4.0-beta.9"}`. Along the way this rig surfaced the Apache
`Authorization`-header bug now fixed in #24.
tomasstark
added a commit
to getsupertab/connect-wp
that referenced
this pull request
Jul 14, 2026
…Status_Handler (#19) This PR fixes the self-report status endpoint silently serving the 404 decoy to the backend's challenge probes on hosts where Apache withholds the `Authorization` header from `$_SERVER` — which is why the live self-report check fails for sites on WP Engine despite running 1.3.0-beta.8. ## Context The backend's health check probes `/.well-known/supertab/status` with a challenge JWT in the `Authorization` header. `Status_Handler::get_authorization_header()` read only `$_SERVER['HTTP_AUTHORIZATION']` / `REDIRECT_HTTP_AUTHORIZATION` — but Apache withholds `Authorization` from the CGI-style `$_SERVER` variables (a CGI-era behavior), while `getallheaders()` still exposes it. Since every verification failure fails closed to the decoy, the endpoint looked healthy from outside while every valid probe was rejected. Stock WordPress masks this: since WP 5.6 the core `.htaccess` rewrite block includes `RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]`, which re-exports the header. Hosts that generate their own server config without that rule (WP Engine among them) hit the bug. That's also why the original wp-env verification couldn't catch it — wp-env's stock `.htaccess` carries the compensating rule. This is the same defect fixed in the SDK by getsupertab/connect-sdk-php#24, found live on the vanilla-PHP self-report demo; the plugin duplicated the pre-fix header reading instead of going through the SDK. ## Key Changes - **SDK bump `1.4.0-beta.9` → `1.4.0-beta.10`**, which ships `RequestContext::resolveAuthorizationHeader()` (and fixes the same bug inside the SDK's own `handleRequest()` path used by `Bot_Protection` for license-token checks). - **`Status_Handler::get_authorization_header()`** now delegates to the SDK resolver: `$_SERVER` wins when populated, otherwise the raw request headers are searched case-insensitively. New protected `get_raw_request_headers()` seam wraps `getallheaders()` (guarded by `function_exists`; unavailable under the CLI SAPI) so tests can inject headers. - **3 regression tests**: fallback to raw headers when `$_SERVER` lacks the header, `$_SERVER` precedence, and empty result when absent everywhere. Verified end-to-end in wp-env (Apache + mod_php) via A/B: with the core `.htaccess` auth rule removed to mirror affected hosts, the old code never delivered the bearer to the verifier (no JWKS activity, decoy served); with this fix the same probe reaches `StatusChallengeVerifier`, fetches the platform JWKS, and fails only on the (deliberately) unverifiable test token — decoy behavior for invalid/missing challenges is unchanged.
github-actions Bot
pushed a commit
to getsupertab/connect-wp
that referenced
this pull request
Jul 14, 2026
# [1.3.0-beta.9](v1.3.0-beta.8...v1.3.0-beta.9) (2026-07-14) ### Bug Fixes * resolve Authorization via the SDK's getallheaders() fallback in Status_Handler ([#19](#19)) ([d515452](d515452)), closes [getsupertab/connect-sdk-php#24](getsupertab/connect-sdk-php#24)
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.
This PR makes
RequestContext::fromGlobals()fall back togetallheaders()for theAuthorizationheader, fixing silently-broken verification on Apache with mod_php.Context
Apache/mod_php withholds the
Authorizationrequest header from the CGI-style$_SERVERvariables (a CGI-era security behavior), whilegetallheaders()still exposes it.fromGlobals()read only$_SERVER['HTTP_AUTHORIZATION']/REDIRECT_HTTP_AUTHORIZATION, so on vanilla Apache — one of the most common PHP deployments — every bearer looked absent. Because verification fails closed, nothing errored: status probes got the404 {"supertab": true}decoy and license-token checks treated licensed bots as unlicensed.Found live: the self-report demo site (#23) deployed on
php:8.3-apachereturned only the decoy to backend health probes. Empirically confirmed in the container —getallheaders()carried the challenge JWT while$_SERVERhad nothing. The demo image works around it withSetEnvIf; this PR fixes the SDK so integrators don't need to know the incantation.Key Changes
RequestContext::resolveAuthorizationHeader(array $server, array $requestHeaders): ?string—$_SERVERstill wins; otherwise the raw request headers are searched case-insensitively forAuthorization.fromGlobals()feeds itgetallheaders()when the SAPI provides the function (guarded byfunction_exists; the FPM and built-in-server SAPIs have it too, plain CLI doesn't).Verified on real Apache: ran the demo container with its
SetEnvIfworkaround removed and this patch applied —$_SERVER['HTTP_AUTHORIZATION']unset,fromGlobals()->authorizationHeadercorrectly returns the bearer. Full suite green (286 tests).