Upgrade from 9.5 to 9.6
RESTHeart 9.6 changes the default behavior of originVetoer for requests with no Origin header, and fixes a gap where VETOER authorizers were not enforced on secure = false services.
This page explains the changes and how to restore previous behavior if needed.
What changed
VETOER authorizers now enforced on secure = false services
In RESTHeart 9.5, services declared with secure = false (the default for many endpoints, including all restheart-accounts authentication endpoints) skipped all authorizer checks — not just authentication. This meant originVetoer and any other VETOER authorizer never ran on those endpoints.
Starting from 9.6, VETOER authorizers are always evaluated, even on secure = false services. The FullAuthorizer (which allows all requests) is still used as the ALLOWER, so authentication remains optional, but deny rules are now enforced.
| 9.5 | 9.6 |
|---|---|
|
|
|
|
This affects the following restheart-accounts endpoints (all secure = false by design):
-
POST /auth/register -
GET /auth/verify -
POST /auth/forgot-password -
PATCH /auth/reset-password -
PATCH /auth/activate -
POST /auth/invite -
POST /auth/resend-invite -
GET /auth/oauth/authorize/{provider} -
GET /auth/oauth/callback/{provider}
Why this matters
A deployment that configured originVetoer in 9.5 was protected on data APIs but left registration, password reset and OAuth entry points open to any origin. This was a security gap, not a feature. The fix ensures the origin check applies consistently.
Impact on third-party plugins
If you have custom VETOER authorizers registered via @RegisterPlugin(authorizerType = Authorizer.TYPE.VETOER), they will now also run on secure = false services. Make sure your veto predicates handle requests to unrelated services gracefully (e.g. by short-circuiting on path or request type). Existing built-in vetoes (GetRoleService, AccountsInitializer) already do this.
originVetoer: allow-missing-origin default changed to true
In RESTHeart 9.5, when originVetoer was active (a whitelist or patterns were configured), a request with no Origin header was always denied. This was the correct default for browser-only APIs, but it prevented non-browser clients (curl, CLIs, backend integrations, cron jobs) from calling the API even when they were legitimate.
Starting from 9.6, the new allow-missing-origin configuration option defaults to true, meaning requests without an Origin header are allowed through. Browser requests (which always include Origin) are still validated against the whitelist as before.
| 9.5 | 9.6 |
|---|---|
Missing |
Missing |
No |
|
Why this is safe
Browsers always send the Origin header on the requests that matter for CSRF protection:
-
Cross-site
POST,PUT,DELETErequests -
Any CORS-preflighted request
Setting allow-missing-origin: true does not open a CSRF vulnerability. It simply means the vetoer no longer acts as a blanket "browser-only" gate.
Migration steps
If you want to keep the 9.5 behavior
Add allow-missing-origin: false to your originVetoer configuration in restheart.yml:
originVetoer:
enabled: true
whitelist:
- https://your-frontend.example.com
allow-missing-origin: false # restores 9.5 behavior: missing Origin is denied
If you accept the new default (recommended)
No changes required. Non-browser clients will now be able to call your API without an Origin header, while browser requests continue to be validated against the whitelist.
Per-request override (multi-tenant deployments)
For multi-tenant deployments, the allow-missing-origin setting can be overridden per request using an interceptor:
// Allow non-browser clients for this specific request
request.attachParam("override-origin-allow-missing", true);
// Or deny non-browser clients even when the static config allows them
request.attachParam("override-origin-allow-missing", false);
This is consistent with the existing override-origin-whitelist mechanism introduced in 9.5.
Configuration reference
See Security Hardening: originVetoer for the full configuration documentation.