passbolt/passbolt_api · error · InvalidArgumentException

The refresh token should be a valid UUID.

Error message

The refresh token should be a valid UUID.

What it means

validateRefreshToken rejects any refresh token that is not a valid UUID via CakePHP's Validation::uuid(), throwing InvalidArgumentException before any database lookup. Passbolt refresh tokens are AuthenticationToken entities keyed by UUID, so a non-UUID can never match and is rejected early as invalid input.

Solutions

  1. Send the refresh token exactly as issued: a UUID string (e.g. returned by the refresh/login endpoints).
  2. Validate the token client-side with a UUID regex /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i before calling the API.
  3. Check you are not sending the access token in the refresh-token field of the request body.
  4. Wrap calls in a try/catch for InvalidArgumentException and surface a 400-style input error instead of proceeding.

Example fix

// before
await service.getUserIdFromToken(accessToken); // JWT, not a UUID
// after
if (!/^\h{8}-\h{4}-\h{4}-\h{4}-\h{12}$/i.test(refreshToken)) throw new Error('refresh token must be a UUID');
await service.getUserIdFromToken(refreshToken);
Defensive patterns

Strategy: validation

Validate before calling

const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!UUID_RE.test(refreshToken ?? '')) throw new Error('refresh token must be a UUID');

Type guard

function isUuid(v: unknown): v is string {
  return typeof v === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v);
}

Try / catch

try {
  $service->validateRefreshToken($token);
} catch (InvalidArgumentException $e) {
  throw new BadRequestException('refresh_token must be a valid UUID', 400, $e);
}

Prevention

When it happens

Trigger: Calling queryRefreshToken or getUserIdFromToken (or the refresh/logout endpoints) with a null, empty, truncated, or otherwise malformed token string (e.g. 'abc123', a JWT access token mistakenly sent as the refresh token, or a base64 string).

Common situations: Confusing the JWT access token (long dot-separated string) with the UUID refresh token; sending the raw cookie value instead of the token id; copying a token with truncation or extra quoting; old clients from before refresh tokens were UUIDs.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at plugins/PassboltCe/JwtAuthentication/src/Service/RefreshToken/RefreshTokenAbstractService.php:126

        // Reflect the persisted state on the returned entity so callers do not operate on stale data
        $refreshToken->set('active', false);
        $refreshToken->set('modified', $modified);
        $refreshToken->setDirty('active', false);
        $refreshToken->setDirty('modified', false);

        return $refreshToken;
    }

    /**
     * @param mixed $token Refresh token to be validated.
     * @return void
     * @throws \InvalidArgumentException If the token is not a valid UUID
     */
    public function validateRefreshToken(mixed $token): void
    {
        if (!Validation::uuid($token)) {
            throw new InvalidArgumentException(__('The refresh token should be a valid UUID.'));
        }
    }

    /**
     * @param mixed $userId User id to be validated.
     * @return void
     * @throws \InvalidArgumentException if the $id is not valid
     */
    public function validateUserId(mixed $userId): void
    {
        if (!Validation::uuid($userId)) {
            throw new InvalidArgumentException(__('The user ID should be a valid UUID.'));
        }
    }

    /**
     * @param string|null $token Refresh token
     * @return \Cake\ORM\Query\SelectQuery

View on GitHub (pinned to 31c1bbc10f)