Skip to content

Dashboard profile page saves via updateAdministrator, so admins without the UpdateAdministrator permission cannot edit their own profile #5037

Description

@juangadiel

Describe the bug

The Dashboard's profile page (/profile) saves changes using the updateAdministrator mutation, which is gated by Permission.UpdateAdministrator. As a result, any administrator whose role does not include UpdateAdministrator cannot edit their own first name, last name, email address, or password — the page loads and the form is editable, but saving fails with a permission error.

The Admin API already has a mutation intended for exactly this case: updateActiveAdministrator, which is gated by Permission.Owner and resolves the target id from ctx.activeUserId, so it can only ever modify the calling user's own administrator. The Dashboard does not use it anywhere.

Note the asymmetry within the same page: the activeAdministrator query that loads the page is correctly gated by Permission.Owner, so the page renders fine for every logged-in admin. Only the save path requires the elevated permission.

Relevant source (v3.6.5):

  • packages/dashboard/src/app/routes/_authenticated/_profile/profile.graphql.ts — declares updateAdministratorDocument using updateAdministrator.
  • packages/dashboard/src/app/routes/_authenticated/_profile/profile.tsx:51 — passes it as updateDocument to useDetailPage.
  • packages/core/src/api/resolvers/admin/administrator.resolver.tsupdateActiveAdministrator is @Allow(Permission.Owner); updateAdministrator is @Allow(Permission.UpdateAdministrator).

To Reproduce

Steps to reproduce the behavior:

  1. Create a Role that does not include UpdateAdministrator — for example a shop-staff role with catalog/order permissions only, plus Permission.Authenticated.
  2. Create an Administrator and assign that role.
  3. Log in to the Dashboard as that administrator.
  4. Go to /profile (user menu → Profile). The page loads correctly and shows the current first name, last name, email address and authentication methods.
  5. Change the first name (or type a new password) and click Update.
  6. A toast appears: Failed to update profile with the description You are not currently authorized to perform this action. Nothing is saved.

Expected behavior

Every logged-in administrator can edit their own profile, regardless of whether their role grants UpdateAdministrator. Editing one's own name, email address and password is self-service and should only require being authenticated — which is what Permission.Owner and the existing updateActiveAdministrator mutation express.

Actual behavior

The save is rejected with a FORBIDDEN error unless the administrator's role includes UpdateAdministrator. Because UpdateAdministrator is the permission that governs editing other administrators, the only way to let a user edit their own profile today is to also let them edit other administrators.

Error logs

GraphQL response from the UpdateAdministrator mutation:

{
  "errors": [
    {
      "message": "You are not currently authorized to perform this action",
      "extensions": { "code": "FORBIDDEN" },
      "path": ["updateAdministrator"]
    }
  ],
  "data": null
}

Server log:

warn <timestamp> [GraphQL] You are not currently authorized to perform this action

Environment (please complete the following information):

  • @vendure/core version: 3.6.5 (@vendure/dashboard 3.6.5)
  • Nodejs version: 24.18.0
  • Database (mysql/postgres etc): PostgreSQL
  • Operating System (Windows/macOS/Linux): Windows 11 (also applies on Linux — this is not platform-specific)
  • Browser (if applicable): Chrome
  • Package manager (npm/yarn/pnpm): npm 12.0.1

Configuration

No special configuration is required to reproduce — this reproduces on a default config. The only requirement is a Role that omits UpdateAdministrator, e.g.:

await roleService.create(ctx, {
  code: 'shop-staff',
  description: 'Shop staff',
  channelIds: [channel.id],
  permissions: [
    Permission.Authenticated,
    Permission.ReadCatalog,
    Permission.UpdateCatalog,
    Permission.ReadOrder,
    Permission.UpdateOrder,
    // no Permission.UpdateAdministrator
  ],
});

Minimal reproduction

No repository needed — the two steps above (create a role without UpdateAdministrator, log in as a member of it, try to save /profile) reproduce it on a stock Vendure install.

Suggested fix

Point the profile page at the mutation that already exists for this purpose. In profile.graphql.ts:

-export const updateAdministratorDocument = graphql(`
-    mutation UpdateAdministrator($input: UpdateAdministratorInput!) {
-        updateAdministrator(input: $input) {
+export const updateActiveAdministratorDocument = graphql(`
+    mutation UpdateActiveAdministrator($input: UpdateActiveAdministratorInput!) {
+        updateActiveAdministrator(input: $input) {
             id
         }
     }
 `);

and update the import and updateDocument reference in profile.tsx accordingly.

One non-obvious detail: setValuesForUpdate in profile.tsx currently returns id: entity.id, and useDetailPage submits the form values verbatim as the mutation input. Since UpdateActiveAdministratorInput has no id field, that line has to be dropped as part of the change or every save fails schema validation:

         setValuesForUpdate: entity => {
             return {
-                id: entity.id,
                 firstName: entity.firstName,

UpdateActiveAdministratorInput exposes exactly the five fields the profile form edits (firstName, lastName, emailAddress, password, customFields) and deliberately omits roleIds, so this also removes the theoretical possibility of the profile form touching role assignments.

Workaround

We patch the two Dashboard source files in node_modules/@vendure/dashboard from a postinstall script, applying the diff shown above (swap the mutation document, update the import and updateDocument, and drop id from setValuesForUpdate). With that patch applied, npm run build:dashboard succeeds and administrators on restricted roles can edit their own profiles without being granted UpdateAdministrator.

Additional context

  • Happens consistently, on every save attempt, for every role lacking UpdateAdministrator.
  • Not triggered by any recent change on our side — it is the behaviour of a stock 3.6.5 Dashboard.
  • Context for why this matters to us: we run a multi-tenant setup where each merchant store is a Channel, and merchant-facing roles are deliberately scoped. Granting those roles UpdateAdministrator purely to unblock self-service profile editing would give them a considerably broader capability than intended, so the profile page is effectively unusable for every non-superadmin user.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions