Skip to content

feat: attach short-lived app-session token to Core integration calls (BUG-438)#197

Open
arosenan wants to merge 2 commits into
base44:mainfrom
arosenan:feat/bug-438-app-session-token
Open

feat: attach short-lived app-session token to Core integration calls (BUG-438)#197
arosenan wants to merge 2 commits into
base44:mainfrom
arosenan:feat/bug-438-app-session-token

Conversation

@arosenan

Copy link
Copy Markdown

Why

Public Base44 apps (public_without_login) expose Core integration endpoints to anonymous callers by design — the app's own browser frontend invokes InvokeLLM, SendEmail, etc. with no logged-in user. The flaw tracked in BUG-438 is that nothing distinguishes a request coming from the served app from an arbitrary curl against the public endpoint, so anyone can drain the owner's integration credits, send mail from the trusted sender domain, and so on.

What

The backend now mints a short-lived, app-bound session token (companion backend PR). This SDK change makes the client replay it:

  • src/utils/app-session.ts — a lazily-caching session-token provider. On the first Core integration call it GETs /apps/{appId}/integration-session, caches the token until shortly before its TTL, and de-dupes concurrent refreshes.
  • src/modules/integrations.ts — for Core.* calls, attaches the token via the X-Base44-App-Session header.

Safety / rollout

  • Best-effort & non-breaking: if minting fails, the provider returns null and the call proceeds without the header. Nothing breaks if the endpoint is unavailable.
  • The backend runs in observe-only mode and only enforces the token once a per-app feature flag is enabled — so already-deployed apps bundling older SDK versions keep working while we measure the token-less traffic rate.
  • Only Core calls are touched; installable/custom integrations and authenticated calls are unchanged (the header is additive and ignored for authenticated requests).

Tests

tests/unit/app-session.test.ts — header attached when minted, token minted once and reused, and the best-effort path (proceeds without the header when minting fails). Full unit suite green (156 passed). npm run build + npm run lint pass.

🤖 Generated with Claude Code

arosenan and others added 2 commits June 11, 2026 13:31
BUG-438: public apps expose Core integration endpoints to anonymous
callers by design, with nothing distinguishing a call from the served
app frontend from an arbitrary request against the public endpoint. The
backend now mints a short-lived, app-bound session token; the SDK fetches
it lazily on the first Core integration call and replays it via the
X-Base44-App-Session header.

Best-effort: if minting fails the call proceeds without the header, so
this is non-breaking. The backend enforces the token only once a per-app
flag is enabled (observe-only until then).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BUG-438: the session token alone only turns a one-line curl into a two-line
curl (fetch token, then call the integration). Minting is now a two-step
challenge-response: GET advertises whether a Turnstile challenge is required
(and the public site key); the SDK renders an invisible Turnstile widget and
forwards the response token via Cf-Turnstile-Response on the POST that mints
the session token.

Best-effort: outside a browser, or if the script/challenge fails, getToken
returns null and the integration call proceeds without the header (backend is
observe-only until per-app enforcement).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant