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

Code before:
                token_response = self._oauth_authorize
        else:
            token_response = self._oauth_authorize

        self._set_oauth_info(token_response, self._OAUTH_PROFILE)
        self.write_debug(f'{self._OAUTH_DISPLAY_ID}: Logged in using profile "{self._OAUTH_PROFILE}"')

    def _refresh_token(self, refresh_token):
        try:
            token_response = self._download_json(
                self._OAUTH_TOKEN_ENDPOINT,
                video_id=self._OAUTH_DISPLAY_ID,
                note='Refreshing access token',
                data=json.dumps({
                    'client_id': self._OAUTH_CLIENT_ID,
                    'client_secret': self._OAUTH_CLIENT_SECRET,
                    'refresh_token': refresh_token,
                    'grant_type': 'refresh_token',
                }).encode(),
                headers={'Content-Type': 'application/json'})
        except ExtractorError as e:
            if isinstance(e.cause, HTTPError):
                token_response = self._parse_json(
                    self._webpage_read_content(e.cause.response, None, self._OAUTH_DISPLAY_ID) or '{}',
                    video_id=self._OAUTH_DISPLAY_ID)
                error = traverse_obj(token_response, 'error', {str})
                if error == 'invalid_grant':
                    # RFC6749 § 5.2
                    raise ExtractorError(
                        'Failed to refresh access token: Refresh token is invalid, revoked, or expired (invalid_grant)',
                        expected=True, video_id=self._OAUTH_DISPLAY_ID)
                raise ExtractorError(
                    f'Failed to refresh access token: Authorization server returned error {error}', video_id=self._OAUTH_DISPLAY_ID)
            raise e
        return token_response

    @property
    def _oauth_authorize(self):
        code_response = self._download_json(
            self._OAUTH_DEVICE_AUTHORIZATION_ENDPOINT,
            video_id=self._OAUTH_DISPLAY_ID,
            note='Initializing authorization flow',
            data=json.dumps({
                'client_id': self._OAUTH_CLIENT_ID,
                'scope': self._OAUTH_SCOPE,
            }).encode(),
            headers={'Content-Type': 'application/json'})

        verification_url = traverse_obj(code_response, 'verification_url', {str})
        user_code = traverse_obj(code_response, 'user_code', {str})
        if not verification_url or not user_code:

Review comment (security):
passing the url might be nice for `--write-pages` purposes, and we can use traversal and `fatal=False` to make it totally non-fatal
```suggestion
                error = traverse_obj(
                    self._webpage_read_content(e.cause.response, self._OAUTH_TOKEN_ENDPOINT, self._OAUTH_DISPLAY_ID, fatal=False),
                    ({json.loads}, 'error', {str}))
```

Code after:
            try:
                token_response = self._refresh_token(refresh_token)
            except ExtractorError as e:
                error_msg = str(e.orig_msg).replace('Failed to refresh access token: ', '')
                self.report_warning(f'{self._OAUTH_DISPLAY_ID}: Failed to refresh access token: {error_msg}')
                token_response = self._oauth_authorize
        else:
            token_response = self._oauth_authorize

        self._set_oauth_info(token_response)
        self.write_debug(f'{self._OAUTH_DISPLAY_ID}: Logged in using profile "{self._OAUTH_PROFILE}"')

    def _refresh_token(self, refresh_token):
        try:
            token_response = self._download_json(
                self._OAUTH_TOKEN_ENDPOINT,
                video_id=self._OAUTH_DISPLAY_ID,
                note='Refreshing access token',
                data=json.dumps({
                    'client_id': self._OAUTH_CLIENT_ID,
                    'client_secret': self._OAUTH_CLIENT_SECRET,
                    'refresh_token': refresh_token,
                    'grant_type': 'refresh_token',
                }).encode(),
                headers={'Content-Type': 'application/json'})
        except ExtractorError as e:
            if isinstance(e.cause, HTTPError):
                error = self._read_oauth_error_response(e.cause.response)
                if error == 'invalid_grant':
                    # RFC6749 § 5.2
                    raise ExtractorError(
                        'Failed to refresh access token: Refresh token is invalid, revoked, or expired (invalid_grant)',
                        expected=True, video_id=self._OAUTH_DISPLAY_ID)
                raise ExtractorError(
                    f'Failed to refresh access token: Authorization server returned error {error}',
                    video_id=self._OAUTH_DISPLAY_ID)
            raise
        return token_response

    @property
    def _oauth_authorize(self):
        code_response = self._download_json(
            self._OAUTH_DEVICE_AUTHORIZATION_ENDPOINT,
            video_id=self._OAUTH_DISPLAY_ID,
            note='Initializing authorization flow',
            data=json.dumps({
                'client_id': self._OAUTH_CLIENT_ID,
                'scope': self._OAUTH_SCOPE,
            }).encode(),
            headers={'Content-Type': 'application/json'})