chore: add self-report demo app (vanilla PHP) - #23
Merged
Conversation
…Url that only exists post-create
…d over IAM friction)
…DK reads only $_SERVER
tomasstark
added a commit
that referenced
this pull request
Jul 14, 2026
…from $_SERVER (#24) This PR makes `RequestContext::fromGlobals()` fall back to `getallheaders()` for the `Authorization` header, fixing silently-broken verification on Apache with mod_php. ## Context Apache/mod_php withholds the `Authorization` request header from the CGI-style `$_SERVER` variables (a CGI-era security behavior), while `getallheaders()` 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 the `404 {"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-apache` returned only the decoy to backend health probes. Empirically confirmed in the container — `getallheaders()` carried the challenge JWT while `$_SERVER` had nothing. The demo image works around it with `SetEnvIf`; this PR fixes the SDK so integrators don't need to know the incantation. ## Key Changes - New pure helper `RequestContext::resolveAuthorizationHeader(array $server, array $requestHeaders): ?string` — `$_SERVER` still wins; otherwise the raw request headers are searched case-insensitively for `Authorization`. `fromGlobals()` feeds it `getallheaders()` when the SAPI provides the function (guarded by `function_exists`; the FPM and built-in-server SAPIs have it too, plain CLI doesn't). - 5 unit tests covering precedence, both fallbacks, case-insensitive matching, and the absent-everywhere null. Verified on real Apache: ran the demo container with its `SetEnvIf` workaround removed and this patch applied — `$_SERVER['HTTP_AUTHORIZATION']` unset, `fromGlobals()->authorizationHeader` correctly returns the bearer. Full suite green (286 tests). --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new demo/self-report/ vanilla-PHP “publisher” app intended to serve as a persistent public target for end-to-end testing of the SDK-served /.well-known/supertab/status self-report endpoint against the real sandbox API, and as a minimal integration reference.
Changes:
- Introduces a tiny front controller (
index.php) that fixes proxy scheme, short-circuits/healthz, and delegates all other traffic toSupertabConnect::handleRequest(). - Adds container + deployment artifacts (Dockerfile, Fly.io config) and runbooks for local + hosted usage.
- Pins the demo to the released Packagist SDK version via
composer.json/composer.lock.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| demo/self-report/README.md | Documents purpose, config, local run steps, and probe expectations. |
| demo/self-report/index.php | Minimal front controller routing requests through the SDK and rendering a small “allowed” page. |
| demo/self-report/fly.toml | Fly.io config to keep an always-warm instance for probing. |
| demo/self-report/Dockerfile | Multi-stage build that installs Composer deps and configures Apache to route all requests to index.php. |
| demo/self-report/DEPLOY.md | Fly.io deployment runbook and operational notes. |
| demo/self-report/composer.lock | Locked dependency set for reproducible demo builds. |
| demo/self-report/composer.json | Demo project definition + pinned SDK requirement. |
| demo/self-report/.gitignore | Ignores vendor directory. |
| demo/self-report/.dockerignore | Excludes vendor and README from Docker build context. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…UPERTAB_ANALYTICS
…mple + placeholders)
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 adds
demo/self-report/— a minimal vanilla-PHP publisher for testing the/.well-known/supertab/statusself-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 throughSupertabConnect::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 existingdemo/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 thecomponent: {kind: "php-sdk"}identity) — validating a new release is a pin bump +fly deploy.Key Changes
index.php(~100 lines, no framework):X-Forwarded-Protoscheme fix-up (TLS-terminating proxies forward plain HTTP; without it every probe'saudmismatches and gets the decoy) →/healthzshort-circuit (health checks never look like traffic) → everything else throughhandleRequest(), emittingAllowas a demo HTML page andBlock/Respondverbatim.SUPERTAB_MERCHANT_API_KEY(required, fail-fast 500),SUPERTAB_BASE_URL(defaults sandbox),SUPERTAB_ENFORCEMENT,SUPERTAB_ANALYTICS— so the payload'senforcement/eventReportingcan be flipped per test without code changes.Dockerfile: multi-stage composer install from the committed lockfile;php:8.3-apacheon port 8080 withFallbackResource /index.php(dot-prefixed paths route to the front controller without mod_rewrite) and aSetEnvIfrestoring theAuthorizationheader, which Apache/mod_php withholds from$_SERVER(root fix in fix: read Authorization via getallheaders() when Apache withholds it from $_SERVER #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,
200payload withcomponent: {kind: "php-sdk", version: "v1.4.0-beta.9"}. Along the way this rig surfaced the ApacheAuthorization-header bug now fixed in #24.(Deployment history: an AWS App Runner variant was the original plan; it was dropped over
iam:PassRolefriction — the App Runner runbook is retrievable from this branch's git history.)