Checklist
Description
ResourceServersHandler.updateResourceServer() strips name from the update payload only for the Auth0 My Account API, and it matches on that API's literal name. Any other system resource server takes the full-update path, which sends name, and the Management API rejects it:
Payload validation error: 'Additional properties not allowed: name'
We hit this with the Auth0 My Organization API (https://{domain}/my-org/), which is a system resource server just like the My Account API.
Because calculateChanges() puts every asset it matches in the tenant into update regardless of whether anything actually changed, this happens on every run once the resource server exists. The first import succeeds (create strips is_system); the second one fails and aborts the whole processChanges stage for resourceServers, so the type is left half-applied.
The is_system half of the guard never fires:
// src/tools/auth0/handlers/resourceServers.ts
stripUpdateFields: ['identifier', 'client_id', 'is_system'],
...
if (update.is_system === true || update.name === 'Auth0 My Account API') {
stripFields() runs on the payload in DefaultHandler.processChanges() before updateResourceServer() is called, so update.is_system is always undefined. Only the hardcoded name comparison does anything, which is why the My Account API happens to work and nothing else does.
Expectation
Any system resource server round-trips. import should be idempotent for Auth0 My Organization API in the same way it already is for Auth0 My Account API.
Reproduction
Reproduces consistently, every time.
-
Given a tenant with the My Organization API activated (the Activate button under Applications > APIs, or POST /api/v2/resource-servers with identifier https://{domain}/my-org/), and this in the config directory:
// resource-servers/Auth0 My Organization API.json
{
"name": "Auth0 My Organization API",
"identifier": "https://{domain}/my-org/"
}
-
When you run a0deploy import twice — the first run creates the resource server if it is not there yet, the second run updates it
-
Then the second run fails:
info: Updated [resourceServers]: {"name":"Auth0 My Account API","identifier":"https://{domain}/me/"}
error: Problem running command import during stage processChanges when processing type resourceServers
error: Problem updating resourceServers {"name":"Auth0 My Organization API","identifier":"https://{domain}/my-org/"}
BadRequestError: BadRequestError
Status code: 400
Body: {
"statusCode": 400,
"error": "Bad Request",
"message": "Payload validation error: 'Additional properties not allowed: name'.",
"errorCode": "invalid_body"
}
Putting a file for Auth0 My Account API next to it makes the contrast visible: in the same run that one updates fine and the My Organization API does not.
Suggested fix
Read is_system off the existing resource server instead of off the stripped update payload, and drop the name comparison:
const existing = this.existing?.find((rs) => rs.id === id);
if (existing?.is_system === true) {
...
}
getType() already keeps is_system on what it caches in this.existing, and it runs before processChanges(). Happy to open a PR — I have one ready.
Related
getType()'s allowlist for system resource servers keeps authorization_policy but not subject_type_authorization, while updateResourceServer() does send the latter. Minor and separate, but it means that field cannot round-trip through an export.
Deploy CLI version
8.44.0 (also reproduced on 8.35.0)
Node version
24.12.0
Checklist
Description
ResourceServersHandler.updateResourceServer()stripsnamefrom the update payload only for the Auth0 My Account API, and it matches on that API's literal name. Any other system resource server takes the full-update path, which sendsname, and the Management API rejects it:We hit this with the Auth0 My Organization API (
https://{domain}/my-org/), which is a system resource server just like the My Account API.Because
calculateChanges()puts every asset it matches in the tenant intoupdateregardless of whether anything actually changed, this happens on every run once the resource server exists. The firstimportsucceeds (create stripsis_system); the second one fails and aborts the wholeprocessChangesstage forresourceServers, so the type is left half-applied.The
is_systemhalf of the guard never fires:stripFields()runs on the payload inDefaultHandler.processChanges()beforeupdateResourceServer()is called, soupdate.is_systemis alwaysundefined. Only the hardcoded name comparison does anything, which is why the My Account API happens to work and nothing else does.Expectation
Any system resource server round-trips.
importshould be idempotent forAuth0 My Organization APIin the same way it already is forAuth0 My Account API.Reproduction
Reproduces consistently, every time.
Given a tenant with the My Organization API activated (the Activate button under Applications > APIs, or
POST /api/v2/resource-serverswith identifierhttps://{domain}/my-org/), and this in the config directory:When you run
a0deploy importtwice — the first run creates the resource server if it is not there yet, the second run updates itThen the second run fails:
Putting a file for
Auth0 My Account APInext to it makes the contrast visible: in the same run that one updates fine and the My Organization API does not.Suggested fix
Read
is_systemoff the existing resource server instead of off the stripped update payload, and drop the name comparison:getType()already keepsis_systemon what it caches inthis.existing, and it runs beforeprocessChanges(). Happy to open a PR — I have one ready.Related
getType()'s allowlist for system resource servers keepsauthorization_policybut notsubject_type_authorization, whileupdateResourceServer()does send the latter. Minor and separate, but it means that field cannot round-trip through an export.Deploy CLI version
8.44.0 (also reproduced on 8.35.0)
Node version
24.12.0