Simply set the default User-Agent when requesting an access token and ignore any custom User-Agent values set by the user.
I don't know why, but Twitch simply rejects access token requests when we don't set our default Firefox UA string. I've tried setting the HTTPSession's user-agent string (affected by user overrides) on the Chromium instance before retrieving the client-integrity token, and I also tried setting Chromium's User-Agent value on the HTTPSession after requesting the client-integrity token. None of that worked, in addition of course to the custom UA value from the CLI (a Chromium UA value).
This makes me believe that they are checking the HTTPSession's HTTPS connection (TLS handshakes and other specifics) in regards to the sent UA string.
So the solution for now is to simply discard custom user inputs and send the default FF UA when requesting an access token. This change depends on the previous commit on master. The plugin can therefore not be sideloaded unless the base Streamlink install is >=7.4.0+17.g0360795c
$ streamlink -l trace --http-header 'User-Agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36' --twitch-force-client-integrity --twitch-purge-client-integrity twitch.tv/pgl_dota2
[16:26:39.329462][MainThread][cli][debug] OS: Linux-6.15.3-1-git-x86_64-with-glibc2.41
[16:26:39.329561][MainThread][cli][debug] Python: 3.13.5
[16:26:39.329627][MainThread][cli][debug] OpenSSL: OpenSSL 3.5.0 8 Apr 2025
[16:26:39.329684][MainThread][cli][debug] Streamlink: 7.4.0+17.g0360795c
[16:26:39.329732][MainThread][cli][debug] Dependencies:
[16:26:39.330677][MainThread][cli][debug] certifi: 2024.12.14
[16:26:39.331441][MainThread][cli][debug] isodate: 0.7.2
[16:26:39.331749][MainThread][cli][debug] lxml: 5.3.0
[16:26:39.332170][MainThread][cli][debug] pycountry: 24.6.1
[16:26:39.332455][MainThread][cli][debug] pycryptodome: 3.21.0
[16:26:39.332811][MainThread][cli][debug] PySocks: 1.7.1
[16:26:39.333139][MainThread][cli][debug] requests: 2.32.3
[16:26:39.333485][MainThread][cli][debug] trio: 0.27.0
[16:26:39.333773][MainThread][cli][debug] trio-websocket: 0.11.1
[16:26:39.334098][MainThread][cli][debug] urllib3: 2.3.0
[16:26:39.334437][MainThread][cli][debug] websocket-client: 1.8.0
[16:26:39.334522][MainThread][cli][debug] Arguments:
[16:26:39.334568][MainThread][cli][debug] url=twitch.tv/pgl_dota2
[16:26:39.334614][MainThread][cli][debug] --loglevel=trace
[16:26:39.334669][MainThread][cli][debug] --player=mpv
[16:26:39.334710][MainThread][cli][debug] --player-args=--cache=yes --demuxer-max-back-bytes=2G
[16:26:39.334758][MainThread][cli][debug] --stream-segment-threads=2
[16:26:39.334796][MainThread][cli][debug] --hls-live-edge=2
[16:26:39.334844][MainThread][cli][debug] --http-header=[('User-Agent', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36')]
[16:26:39.334888][MainThread][cli][debug] --webbrowser-headless=True
[16:26:39.334933][MainThread][cli][debug] --twitch-disable-ads=True
[16:26:39.334973][MainThread][cli][debug] --twitch-low-latency=True
[16:26:39.335012][MainThread][cli][debug] --twitch-api-header=[('Authorization', 'OAuth REDACTED')]
[16:26:39.335058][MainThread][cli][debug] --twitch-force-client-integrity=True
[16:26:39.335104][MainThread][cli][debug] --twitch-purge-client-integrity=True
[16:26:39.335185][MainThread][cli][info] Found matching plugin twitch for URL twitch.tv/pgl_dota2
[16:26:39.335417][MainThread][plugins.twitch][debug] Getting live HLS streams for pgl_dota2
[16:26:39.335474][MainThread][plugins.twitch][info] Removing cached client-integrity token...
[16:26:39.336375][MainThread][plugins.twitch][info] Acquiring new client-integrity token...
[16:26:39.430015][MainThread][webbrowser.webbrowser][info] Launching web browser: /usr/bin/chromium (headless=True)
[16:26:41.987001][MainThread][webbrowser.webbrowser][debug] Waiting for web browser process to terminate
error: Error: server error
$ streamlink -l trace --http-header 'User-Agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36' --twitch-force-client-integrity --twitch-purge-client-integrity twitch.tv/pgl_dota2
[16:27:01.055560][MainThread][cli][debug] OS: Linux-6.15.3-1-git-x86_64-with-glibc2.41
[16:27:01.055682][MainThread][cli][debug] Python: 3.13.5
[16:27:01.055739][MainThread][cli][debug] OpenSSL: OpenSSL 3.5.0 8 Apr 2025
[16:27:01.055785][MainThread][cli][debug] Streamlink: 7.4.0+18.gee2fba6b
[16:27:01.055830][MainThread][cli][debug] Dependencies:
[16:27:01.056864][MainThread][cli][debug] certifi: 2024.12.14
[16:27:01.057624][MainThread][cli][debug] isodate: 0.7.2
[16:27:01.057920][MainThread][cli][debug] lxml: 5.3.0
[16:27:01.058326][MainThread][cli][debug] pycountry: 24.6.1
[16:27:01.058597][MainThread][cli][debug] pycryptodome: 3.21.0
[16:27:01.058940][MainThread][cli][debug] PySocks: 1.7.1
[16:27:01.059241][MainThread][cli][debug] requests: 2.32.3
[16:27:01.059572][MainThread][cli][debug] trio: 0.27.0
[16:27:01.059849][MainThread][cli][debug] trio-websocket: 0.11.1
[16:27:01.060175][MainThread][cli][debug] urllib3: 2.3.0
[16:27:01.060490][MainThread][cli][debug] websocket-client: 1.8.0
[16:27:01.060576][MainThread][cli][debug] Arguments:
[16:27:01.060622][MainThread][cli][debug] url=twitch.tv/pgl_dota2
[16:27:01.060669][MainThread][cli][debug] --loglevel=trace
[16:27:01.060723][MainThread][cli][debug] --player=mpv
[16:27:01.060764][MainThread][cli][debug] --player-args=--cache=yes --demuxer-max-back-bytes=2G
[16:27:01.060812][MainThread][cli][debug] --stream-segment-threads=2
[16:27:01.060850][MainThread][cli][debug] --hls-live-edge=2
[16:27:01.060897][MainThread][cli][debug] --http-header=[('User-Agent', 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36')]
[16:27:01.060940][MainThread][cli][debug] --webbrowser-headless=True
[16:27:01.060986][MainThread][cli][debug] --twitch-disable-ads=True
[16:27:01.061026][MainThread][cli][debug] --twitch-low-latency=True
[16:27:01.061073][MainThread][cli][debug] --twitch-api-header=[('Authorization', 'OAuth REDACTED')]
[16:27:01.061112][MainThread][cli][debug] --twitch-force-client-integrity=True
[16:27:01.061149][MainThread][cli][debug] --twitch-purge-client-integrity=True
[16:27:01.061225][MainThread][cli][info] Found matching plugin twitch for URL twitch.tv/pgl_dota2
[16:27:01.061455][MainThread][plugins.twitch][debug] Getting live HLS streams for pgl_dota2
[16:27:01.061512][MainThread][plugins.twitch][info] Removing cached client-integrity token...
[16:27:01.062414][MainThread][plugins.twitch][info] Acquiring new client-integrity token...
[16:27:01.155707][MainThread][webbrowser.webbrowser][info] Launching web browser: /usr/bin/chromium (headless=True)
[16:27:04.025307][MainThread][webbrowser.webbrowser][debug] Waiting for web browser process to terminate
[16:27:04.915892][MainThread][plugins.twitch][debug] {'adblock': False, 'geoblock_reason': '', 'hide_ads': False, 'server_ads': True, 'show_ads': True}
[16:27:04.937859][MainThread][utils.l10n][debug] Language code: en_US
Available streams: audio_only, 160p (worst), 360p, 480p, 720p, 720p60, 1080p60 (best)