Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/youtube/_base.py (Python)

Code before:
                            ('SAPISID3PHASH', yt_3psapisid)):
            if sid:
                authorizations.append(self._make_sid_authorization(scheme, sid, origin, additional_parts))

        if not authorizations:
            return None

        return ' '.join(authorizations)

    @functools.cached_property
    def is_authenticated(self):
        return bool(self._has_auth_cookies())

    def _has_auth_cookies(self):
        yt_sapisid, yt_1psapisid, yt_3psapisid = self._get_sid_cookies()
        # YouTube doesn't appear to clear 3PSAPISID when rotating cookies (as of 2025-04-26)
        # But LOGIN_INFO is cleared and should exist if logged in
        has_login_info = 'LOGIN_INFO' in self._youtube_cookies()
        return has_login_info and (yt_sapisid or yt_1psapisid or yt_3psapisid)

    def _request_webpage(self, *args, **kwargs):
        response = super()._request_webpage(*args, **kwargs)

        # Check that we are still logged-in and cookies have not rotated after every request
        if self._had_cookie_auth and not self._has_auth_cookies():
            self._had_cookie_auth = False
            raise ExtractorError(
                'The provided YouTube account cookies are no longer valid. '
                'They have likely been rotated in the browser as a security measure. '
                f'For tips on how to effectively export YouTube cookies, refer to  {self._COOKIE_HOWTO_WIKI_URL}  .',
                expected=True)

        return response

    def _call_api(self, ep, query, video_id, fatal=True, headers=None,
                  note='Downloading API JSON', errnote='Unable to download API page',
                  context=None, api_key=None, api_hostname=None, default_client='web'):

        data = {'context': context} if context else {'context': self._extract_context(default_client=default_client)}
        data.update(query)
        real_headers = self.generate_api_headers(default_client=default_client)
        real_headers.update({'content-type': 'application/json'})
        if headers:
            real_headers.update(headers)
        return self._download_json(
            f'https://{self._select_api_hostname(api_hostname, default_client)}/youtubei/v1/{ep}',
            video_id=video_id, fatal=fatal, note=note, errnote=errnote,
            data=json.dumps(data).encode('utf8'), headers=real_headers,
            query=filter_dict({
                'key': self._configuration_arg(
                    'innertube_key', [api_key], ie_key=CONFIGURATION_ARG_KEY, casesense=True)[0],

Review comment (security):
since `is_authenticated` is cached, can't we just use that instead of adding `_had_cookie_auth`?

```suggestion
        if self.is_authenticated and not self._has_auth_cookies():
```

Code after:
            if sid:
                authorizations.append(self._make_sid_authorization(scheme, sid, origin, additional_parts))

        if not authorizations:
            return None

        return ' '.join(authorizations)

    @property
    def is_authenticated(self):
        return self._has_auth_cookies

    @property
    def _has_auth_cookies(self):
        yt_sapisid, yt_1psapisid, yt_3psapisid = self._get_sid_cookies()
        # YouTube doesn't appear to clear 3PSAPISID when rotating cookies (as of 2025-04-26)
        # But LOGIN_INFO is cleared and should exist if logged in
        has_login_info = 'LOGIN_INFO' in self._youtube_cookies
        return bool(has_login_info and (yt_sapisid or yt_1psapisid or yt_3psapisid))

    def _request_webpage(self, *args, **kwargs):
        response = super()._request_webpage(*args, **kwargs)

        # Check that we are still logged-in and cookies have not rotated after every request
        if getattr(self, '_passed_auth_cookies', None) and not self._has_auth_cookies:
            self.report_warning(
                'The provided YouTube account cookies are no longer valid. '
                'They have likely been rotated in the browser as a security measure. '
                f'For tips on how to effectively export YouTube cookies, refer to  {self._COOKIE_HOWTO_WIKI_URL} .',
                only_once=False)

        return response

    def _call_api(self, ep, query, video_id, fatal=True, headers=None,
                  note='Downloading API JSON', errnote='Unable to download API page',
                  context=None, api_key=None, api_hostname=None, default_client='web'):

        data = {'context': context} if context else {'context': self._extract_context(default_client=default_client)}
        data.update(query)
        real_headers = self.generate_api_headers(default_client=default_client)
        real_headers.update({'content-type': 'application/json'})
        if headers:
            real_headers.update(headers)
        return self._download_json(
            f'https://{self._select_api_hostname(api_hostname, default_client)}/youtubei/v1/{ep}',
            video_id=video_id, fatal=fatal, note=note, errnote=errnote,
            data=json.dumps(data).encode('utf8'), headers=real_headers,
            query=filter_dict({
                'key': self._configuration_arg(
                    'innertube_key', [api_key], ie_key=CONFIGURATION_ARG_KEY, casesense=True)[0],
                'prettyPrint': 'false',