passbolt/passbolt_api · error · BadRequestException

The key provided does not belong to given user.

Error message

The key provided does not belong to given user.

What it means

Thrown by validateData in RecoverCompleteService when the fingerprint of the OpenPGP key submitted with the recovery does not match any stored key for that user. Recovery re-registers the user's ORIGINAL key, so a different key is rejected to prevent an attacker swapping keys.

Solutions

  1. Use the exact key pair originally registered for this account (fingerprint must match the stored gpgkeys.fingerprint)
  2. If the key is truly lost, an administrator must delete the user's account key / re-invite the user so a new key can be registered
  3. Compare fingerprints locally before calling: gpg --show-keys <file> and diff against the stored fingerprint
  4. Ensure only the primary key's armored block is sent, not a subkey-only export

Example fix

// before: recovering with a freshly generated key
const armor = await generateAndExportNewKey();
await recoverComplete(userId, token, armor);
// after: use the original registered key
const armor = await exportOriginalRegisteredKey();
await recoverComplete(userId, token, armor);
Defensive patterns

Strategy: validation

Validate before calling

const fp = await getFingerprint(armoredKey);
const stored = await getStoredFingerprint(userId);
if (fp !== stored) throw new Error('key does not match account');

Try / catch

try { await recoverComplete(userId, token, armoredKey); }
catch (e) { if (isKeyMismatch(e)) { /* contact admin — original key required */ } else throw e; }

Prevention

When it happens

Trigger: POST /setup/recover/complete/{userId}/{token} with gpgkey.armored_key whose fingerprint differs from the user's stored Gpgkeys row; user regenerated/lost their key and tries to recover with the new one.

Common situations: User lost their private key and generated a new pair hoping to recover; importing the wrong key file (another account's key); key with multiple subkeys where the wrong primary fingerprint is derived; typo when pasting the armored key.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/57fdf7fe3dec1ea0. Report an issue: GitHub.

Appendix: source

Thrown at src/Service/Setup/RecoverCompleteService.php:97

    }

    /**
     * Validate the user and the gpgkey
     *
     * @param string $userId User ID
     * @return \App\Model\Entity\User
     * @throws \Cake\Http\Exception\BadRequestException if the data provided is not valid
     */
    protected function validateData(string $userId): User
    {
        // Check request sanity
        $user = $this->getAndAssertUser($userId);
        $gpgkey = $this->getAndAssertGpgkey($userId);

        // Check that the "new" gpg key match the old one
        $userKey = $this->Gpgkeys->getByFingerprintAndUserId($gpgkey->fingerprint, $userId);
        if (empty($userKey)) {
            throw new BadRequestException(__('The key provided does not belong to given user.'));
        }

        return $user;
    }

    /**
     * Return the user for matching the requesting id
     *
     * @param string $userId the user uuid
     * @throws \Cake\Http\Exception\BadRequestException if the user id is not a valid uuid
     * @throws \Cake\Http\Exception\BadRequestException if the user was deleted or has not completed the setup
     * @return \App\Model\Entity\User
     */
    protected function getAndAssertUser(string $userId): User
    {
        try {
            return (new UserGetService())->getActiveNotDeletedNotDisabledOrFail($userId);
        } catch (NotFoundException $exception) {

View on GitHub (pinned to 31c1bbc10f)