private function checkRedirectUri(
        Request $request,
        $clientId
    ) {
        // redirect_uri may not exists.
        $redirectUri = $request->request->get('redirect_uri');
        $errors = $this->validator->validate($redirectUri, [
            new \AuthBucket\OAuth2\Symfony\Component\Validator\Constraints\RedirectUri(),
        ]);
        if (count($errors) > 0) {
            throw new InvalidRequestException([
                'error_description' => 'The request includes an invalid parameter value.',
            ]);
        }

        // redirect_uri is not required if already established via other channels,
        // check an existing redirect URI against the one supplied.
        $stored = null;
        $clientManager = $this->modelManagerFactory->getModelManager('client');
        $result = $clientManager->readModelOneBy([
            'clientId' => $clientId,
        ]);
        if ($result !== null && $result->getRedirectUri()) {
            $stored = $result->getRedirectUri();
        }

        // At least one of: existing redirect URI or input redirect URI must be
        // specified.
        if (!$stored && !$redirectUri) {
            throw new InvalidRequestException([
                'error_description' => 'The request is missing a required parameter.',
            ]);
        }

        // If there's an existing uri and one from input, verify that they match.
        if ($stored && $redirectUri) {
            // Ensure that the input uri starts with the stored uri.
            if (strcasecmp(substr($redirectUri, 0, strlen($stored)), $stored) !== 0) {
                throw new InvalidGrantException([
                    'error_description' => 'The provided authorization grant does not match the redirection URI used in the authorization request.',
                ]);
            }
        }

        return $redirectUri ?: $stored;
    }