Origin Policy
The Connect SDK transmits health-plan member credentials between the
host page and app.tpastream.com. Those bytes must travel encrypted.
Two reinforcing checks enforce that:
- Browser-side, at
StreamConnect()init time, the SDK refuses to mount if the host page is not a secure context. - Server-side,
app.tpastream.comrefuses to send the CORSAccess-Control-Allow-Originheader for/sdk-api/*and/patientaccessapi/*requests originating from a non-allowed scheme.
Both layers allow the same set of origins:
| Scheme | Host pattern | Allowed | Notes |
|---|---|---|---|
https:// | any | ✅ | Production use |
http:// | localhost (any port) | ✅ | Local development |
http:// | 127.0.0.1 (any port) | ✅ | Local development |
http:// | [::1] (any port) | ✅ | Local development (IPv6 loopback) |
http:// | anything else | ❌ | Including staging, internal hostnames |
file:// | — | ❌ | |
| Any other scheme | — | ❌ |
What you'll see if the policy blocks your page
Browser console (SDK init)
[stream-connect-sdk] init failed: host page must be served over HTTPS
(or from http://localhost, http://127.0.0.1, or http://[::1] for local
development). The current page origin http://staging.example.com is
insecure, and the SDK transmits member credentials — sending those
over plain HTTP would expose them in transit. See
https://developers.tpastream.com/connect/origin-policy for the fix.
If you wired the optional handleInitErrors callback in your
StreamConnect({ … }) call, the same message is also delivered to
that callback as an Error so your monitoring picks it up.
Browser network tab (server-side enforcement, fallback)
If you somehow reach the network — older SDK versions, a non-SDK
client — the request to /sdk-api/* completes, but the response is
missing the Access-Control-Allow-Origin header, and the browser
blocks the JavaScript from reading the body. The console shows a
generic CORS error:
Access to fetch at 'https://app.tpastream.com/sdk-api/tpastream_sdk'
from origin 'http://staging.example.com' has been blocked by CORS
policy: No 'Access-Control-Allow-Origin' header is present on the
requested resource.
The SDK's init-time guard is intended to fire first so you don't have to deduce the cause from this message.
How to fix it
For production:
- Serve the host page over
https://. Almost every hosting platform (Vercel, Netlify, Cloudflare Pages, CloudFront + ACM, an nginx + Let's Encrypt deployment) provides a free TLS certificate for your domain. If you're loading the host page in an iframe inside another product, the host product must itself be HTTPS — the SDK can't upgrade the parent context.
For local development:
vite dev,webpack-dev-server,next dev,create-react-app,npm run devfor most frameworks, etc. all default tohttp://localhost:<port>. That's already allowed — no config change needed.- If your dev server binds to a LAN hostname like
http://my-machine.local:3000or your office's hostname, that fails the loopback check. Either bind tolocalhost/127.0.0.1, or run a local TLS proxy (Caddy,mkcert+ a tiny nginx) in front of the dev server.
Per-key allowlist (optional)
The policy above applies to every key. You can also restrict an individual key to your own websites. Only TPA Stream staff can change these settings, so to turn this on, ask TPA Stream support to enable it for your key and tell them the list of origins and the mode you want. Each Connect key has two settings:
- Embed origins: a list of origins, such as
https://benefits.example.comorhttps://*.example.com. - Mode:
off,reportorenforce.
| Mode | A browser request from an origin not on the list |
|---|---|
off (default) | Nothing changes. The list is ignored. |
report | The request succeeds and TPA Stream logs it, so you can see what enforcement would block. |
enforce | The request fails with HTTP 403 and {"error_code": "origin_not_allowed", "docs_url": "..."}. The response carries normal CORS headers so your page can read it. |
The check runs on every request that carries the key, after the key is
recognised, and uses the browser's Origin header. Requests with no
Origin header (server-to-server calls) are never affected, and CORS
preflight (OPTIONS) requests are not checked.
We suggest rolling out in three steps: leave the key on off while you
list every site that embeds the SDK, ask support to switch to report
for about a week and review what was logged, then ask support to switch
to enforce.
https://*.example.commatches subdomains and never matches the barehttps://example.com. List the bare origin separately if you need it.- Local development origins such as
http://localhost:3000must be listed too, including the port. - This limits which websites can use a key from a browser. It does not
protect a key used from a server, because server callers send no
Originheader. For server use, mint connect access tokens from your secret key instead.
If a request is rejected with origin_not_allowed, ask support to add
the site's origin to the key's embed origins, or to set the mode back
to report.
Related
- SDK Quickstart — the smallest possible page that mounts the SDK.
- Stream Connect SDK changelog —
0.8.2is the first release that includes the init-time guard.