Skip to content

fix(dev-server): inject service role token for unauthenticated function calls#516

Open
yurynix wants to merge 10 commits into
mainfrom
fix/dev-server-service-role-unauthenticated
Open

fix(dev-server): inject service role token for unauthenticated function calls#516
yurynix wants to merge 10 commits into
mainfrom
fix/dev-server-service-role-unauthenticated

Conversation

@yurynix
Copy link
Copy Markdown

@yurynix yurynix commented May 10, 2026

Note

Description

Makes the local dev server inject a synthetic service-role JWT on every function proxy call, so asServiceRole works locally even for unauthenticated callers (e.g. public-facing subscribe forms) — matching how production Base44 forwards a privileged service token. Also teaches RLS/FLS and the entities router to recognize the service principal and bypass denied rules, extracts the JWT helpers into a shared auth/tokens.ts, and patches Deno.serve via Object.defineProperty to remain compatible with Deno 2.8 (which exposes serve as a getter-only property).

Related Issue

None

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Other (please describe):

Changes Made

  • Add packages/cli/src/cli/dev/dev-server/auth/tokens.ts with createJwtToken, createServiceAuthorizationHeader, SERVICE_ROLE_EMAIL, and isServiceSubject helpers (extracted from auth-router.ts).
  • Always inject Base44-Service-Authorization: Bearer <service-jwt> in routes/functions.ts proxy, regardless of caller auth.
  • Resolve the service subject in routes/entities/current-user.ts to a synthetic SERVICE_USER (is_service: true, role: "admin") without a DB lookup.
  • Update checkRLS to short-circuit to true for is_service users, and fix applyFLS to allow service users to read fields with rls.read === false (also corrects !rule to rule === undefined to stop dropping false-rule fields silently).
  • Update entities-router delete handler so service users bypass an explicit rls.delete === false.
  • Patch deno-runtime/main.ts to override Deno.serve via Object.defineProperty (Deno 2.8 made it getter-only, so direct assignment threw).
  • Add tests/cli/dev-rls.spec.ts (unit) and three new integration tests in tests/cli/dev.spec.ts covering service-token injection on authenticated/unauthenticated calls and service-role RLS bypass on create/list/delete.
  • Sort exports in src/cli/index.ts to satisfy Biome.

Testing

  • I have tested these changes locally
  • I have added/updated tests as needed
  • All tests pass (npm test)

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (if applicable)
  • My changes generate no new warnings
  • I have updated docs/ (AGENTS.md) if I made architectural changes

Additional Notes

The service-role JWT is signed with the existing LOCAL_DEV_SECRET and identified by the server@server.com subject — purely a local dev sentinel, not a real credential. The applyFLS change from !rule to rule === undefined is a latent correctness fix: previously, fields with an explicit rls.read: false rule were filtered out for everyone (including admins with passing record-level RLS) instead of being evaluated.


🤖 Generated by Claude | 2026-06-02 12:06 UTC | c049d5e

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 10, 2026

🚀 Package Preview Available!


Install this PR's preview build with npm:

npm i @base44-preview/cli@0.0.52-pr.516.c049d5e

Prefer not to change any import paths? Install using npm alias so your code still imports base44:

npm i "base44@npm:@base44-preview/cli@0.0.52-pr.516.c049d5e"

Or add it to your package.json dependencies:

{
  "dependencies": {
    "base44": "npm:@base44-preview/cli@0.0.52-pr.516.c049d5e"
  }
}

Preview published to npm registry — try new features instantly!

// unauthenticated callers (e.g. public-facing subscribe forms).
proxyReq.setHeader(
"Base44-Service-Authorization",
authorization ?? "Bearer base44-dev-service-token",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not robust enough even for dev.
authorization is user authorization. It means that in case user is logged in locally with newly created user (also locally), then overall permissions will be limited. Which is not expected for server side authorization.

I think approach should be more robust than that.
I think Base44-Service-Authorization should be actual JWT, maybe simple solution will be to create token with email like server@server.com and then in the code check if email is "server" then allow everything. something like that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same goes for Bearer base44-dev-service-token - this token will fail for code that relies on user being presented in JWT:

export async function resolveCurrentUser(

yurynix and others added 6 commits June 1, 2026 17:16
…on calls

The function router only forwarded Base44-Service-Authorization when a
user Authorization header was present. Public-facing functions (e.g. a
subscribe form) are called without user auth, so asServiceRole threw
"Service token is required" before making any HTTP request.

In production, Base44 always injects the service role token when
forwarding requests to functions. Mirror that behaviour in the dev server
by defaulting to a synthetic dev token when no user auth header exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ated calls

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@yurynix yurynix force-pushed the fix/dev-server-service-role-unauthenticated branch from 55f4cdc to b24046f Compare June 1, 2026 14:22
yurynix and others added 4 commits June 1, 2026 17:47
Deno 2.8 exposes `Deno.serve` as a getter-only property, so the wrapper's
plain `Deno.serve = ...` assignment throws
`TypeError: Cannot set property serve ... which has only a getter`. The
function process crashes on startup, the dev-server proxy can't reach it,
and requests return 500.

Override it with Object.defineProperty (writable + configurable) instead,
which works on both the old writable property and the new 2.8 accessor.

CI installs Deno via `setup-deno@v2` with `deno-version: v2.x`, which now
floats to 2.8.1 — this is why the local-functions dev tests started
failing on every PR despite no related code change.

Co-Authored-By: Claude Opus 4.8 <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.

2 participants