Repository navigation
Conversation
AppEntity.AllowedOriginsJson has been populated from the apps API since the initial schema, but nothing ever read it — the host emitted no CORS headers at all, so no browser client could call the data plane cross-origin. Implements spec 08 §7: - Default deny: no Origin match, no CORS headers. - Per-app allowed_origins resolved from the presented API key, with a 60 s memory cache keyed by the key hash (mirrors ApiKeyAuthenticationHandler). - Preflight carries no custom headers, so an unkeyed request falls back to the instance-level Cors:DefaultAllowedOrigins list (spec 12 §4). - Single-label wildcards (https://*.example.com); an extra label, a scheme mismatch, or a suffix forgery (evilexample.com) does not match. - Access-Control-Expose-Headers carries the OData and RateLimit headers. Runs before authentication (spec 02 §4 step 4) because a browser preflight is anonymous by definition — previously OPTIONS returned 401 before any CORS negotiation could happen. Placed ahead of the setup gate too, so a preflight during first-run setup still answers with headers rather than a bare 503. AuthService.Sha256 becomes public: the middleware must hash presented keys with exactly the same format as the auth handler, and duplicating it invites a silent divergence that would break origin lookup. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The compose demo assets were never committed. Fixes to what was there: - demo.sh waited for service status "Ready", which is not a ServiceStatus value (Pending/Introspecting/Active/Failed/Refreshing/Disabled). The wait always exhausted its 40x2s budget before printing the right answer anyway, so the script took ~3 min instead of ~5 s. Now breaks on Active|Failed. - The REST line printed "n/a": the route is /api/rest/<svc>/_table/<table> and rows come back under .resource, not .value or a bare array. - demo.sh's header told you to layer the demo overlay on docker-compose.yml. The overlay is written against the minimal profile (its own header explains why: the full profile's Postgres system-DB path uses EnsureCreated and fails on a SQLite-style filtered index). Corrected to match. Adds a third beat showing how to interrogate a service: the discovery endpoints (service document, $metadata, per-table _schema including the PII mask flag), the same query over MCP, and a Swagger UI on :8081 browsing all four generated specs. openapi.json requires auth, so Swagger UI cannot fetch it directly — demo.sh exports the specs to ./specs (gitignored) and nginx serves them. "Try it out" works: the overlay puts :8081 on Cors:DefaultAllowedOrigins. Co-Authored-By: Claude Opus 5 (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.
Why
AppEntity.AllowedOriginsJsonhas been written by the apps API since the initial schema, but nothing ever read it. The host emitted no CORS headers at all —grep -rn 'AddCors|UseCors|WithOrigins'oversrc/returned nothing — so no browser client could call the data plane cross-origin, and a preflight got a bare401before any negotiation. That also blocked the "full try-it console" thatui/src/pages/Docs.tsxnotes as a fast follow.CORS (spec 08 §7)
allowed_originsresolved from the presented API key, cached 60 s by key hash (mirrorsApiKeyAuthenticationHandler).Cors:DefaultAllowedOrigins(spec 12 §4).https://*.example.commatchestenant.example.com, but nota.b.example.com, nothttp://, and not the suffix forgeryevilexample.com.Access-Control-Expose-Headerscarries the OData and RateLimit headers.Ordering: the middleware runs before authentication (spec 02 §4 step 4) — a browser preflight carries no credentials by definition. It also sits ahead of the setup gate, so a preflight during first-run setup answers with headers instead of a bare
503.AuthService.Sha256becomespublic. The middleware has to hash presented keys in exactly the auth handler's format; duplicating that constant invites a silent divergence that breaks origin lookup.Demo
The compose demo assets were never committed. Two beats were broken:
demo.shwaited for status"Ready", which is not aServiceStatusvalue. The loop always exhausted its 40×2s budget before printing the correct answer anyway — ~3 min → ~5 s.n/a: the route is/api/rest/<svc>/_table/<table>, and rows come back under.resource.docker-compose.yml); the overlay is written againstdocker-compose.minimal.yml.New Beat 3 shows how to interrogate a service: discovery endpoints (service document,
$metadata, per-table_schemaincluding the PII mask flag), the same query over MCP, and a Swagger UI on:8081browsing all four generated specs.openapi.jsonrequires auth, sodemo.shexports the specs to./specs(gitignored) and nginx serves them.Testing
CorsTests— 14 tests: preflight allow/deny while anonymous, no-Origin passthrough, per-app origin honored for a keyed request, and a wildcard-matching theory covering the forgery cases. All pass.SqlServerEndToEndTests.SqlServer_full_lifecycle, is pre-existing and environmental — Testcontainers can't start a SQL Server container here (Docker API ... container is not running); that test sends noOriginheader, so the new middleware never engages.Access-Control-Allow-Origin, denied origin returns no CORS headers, wildcard matching behaves as specified.🤖 Generated with Claude Code