Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,20 @@ Watch your favorite gaming streams on Kodi.
LOGIN
----------------

1. Go to __Settings - Login - Login (device code)__
2. Visit [twitch.tv/activate](https://www.twitch.tv/activate) on any device, sign in, and enter the code shown in Kodi

The add-on stores the tokens and refreshes them automatically. Automatic refresh
requires a public Client ID — the bundled Client ID cannot refresh tokens, so you
will be asked to log in again once the token expires. To enable automatic refresh:
Set up your own Client ID first, then log in — in that order. The bundled Client ID
cannot refresh tokens (Twitch treats it as a confidential client), so with it you have
to log in again every time the token expires.

1. Register your own application at [dev.twitch.tv/console/apps](https://dev.twitch.tv/console/apps)
(OAuth Redirect URL: `http://localhost`, Client Type: __Public__)
2. Enter its Client ID in __Settings - Developer - OAuth Client ID__
3. Log in again via __Settings - Login - Login (device code)__
3. Go to __Settings - Login - Login (device code)__
4. Visit [twitch.tv/activate](https://www.twitch.tv/activate) on any device, sign in, and enter the code shown in Kodi

The add-on stores the tokens and refreshes them automatically from then on.

If you logged in before entering your Client ID, simply log in again (step 3) — the
previous token belongs to the bundled Client ID and cannot be refreshed.

AD-FREE PLAYBACK (TURBO / SUBSCRIPTIONS)
----------------
Expand Down
43 changes: 22 additions & 21 deletions resources/lib/twitch_addon/addon/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,27 +93,28 @@ def valid_token(self, client_id, token, scopes): # client_id, token used for un
else:
return False
else:
matches_default = token_check['client_id'] == utils.get_client_id(default=True)
log_utils.log('Error: OAuth Client-ID mismatch', log_utils.LOGERROR)
if matches_default:
_ = kodi.Dialog().ok(
i18n('oauth_token'),
'[CR]'.join([i18n('client_id_mismatch'), i18n('ok_to_resolve')])
)
utils.clear_client_id()
self.client_id = utils.get_client_id(default=True)
self.queries.CLIENT_ID = self.client_id
self.client = oauth.clients.MobileClient(self.client_id, self.client_secret)
else:
_ = kodi.Dialog().ok(
i18n('oauth_token'),
'[CR]'.join([
i18n('client_id_mismatch'),
i18n('get_new_oauth_token') %
(i18n('settings'), i18n('login'), i18n('device_login'))
])
)
return False
# The stored token belongs to a different Client-ID than the configured one. The
# configured Client-ID is the user's deliberate setting, so the stale token is what
# has to go -- discard it and ask for a new login.
#
# Do NOT clear the configured Client-ID here (as this used to do when the token
# matched the bundled one). Following the README, a user logs in first and enters
# their own Client-ID afterwards, which lands exactly in this branch: clearing the
# setting sent them back to the bundled Client-ID, which is confidential and cannot
# refresh, so every subsequent login died again an hour later -- and re-entering the
# Client-ID triggered the very same wipe. See issue #712.
log_utils.log('Error: OAuth Client-ID mismatch, discarding the stored token',
log_utils.LOGERROR)
utils.clear_oauth_tokens()
_ = kodi.Dialog().ok(
i18n('oauth_token'),
'[CR]'.join([
i18n('client_id_mismatch'),
i18n('get_new_oauth_token') %
(i18n('settings'), i18n('login'), i18n('device_login'))
])
)
return False

@cache.cache_method(cache_limit=1)
def valid_private_token(self, client_id, token): # client_id used for unique caching only
Expand Down
1 change: 0 additions & 1 deletion resources/lib/twitch_addon/addon/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@
'items': 30214,
'duration': 30215,
'curated': 30216,
'ok_to_resolve': 30217,
'search_id_url_description': 30218,
'authorize_url_fail': 30220,
'unknown': 30221,
Expand Down
4 changes: 0 additions & 4 deletions resources/lib/twitch_addon/addon/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,6 @@ def get_private_client_id():
return kodi.decode_utf8(settings_id)


def clear_client_id():
kodi.set_setting('oauth_clientid', '')


def get_oauth_token(token_only=True, required=False):
oauth_token = _read_oauth_store().get('access', '')
if not oauth_token or not oauth_token.strip():
Expand Down
38 changes: 36 additions & 2 deletions resources/lib/twitch_addon/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

from .addon.common import kodi, log_utils
from .addon.constants import Keys
from .addon.utils import i18n, get_stamp_diff, get_vodcast_color, ensure_valid_token
from .addon.utils import (i18n, get_stamp_diff, get_vodcast_color, ensure_valid_token,
get_client_id, get_oauth_token, clear_oauth_tokens)
from .addon.player import TwitchPlayer
from .addon import api, cache

Expand Down Expand Up @@ -202,10 +203,43 @@ def stopped(self):
return self._stopped.is_set()


class SettingsMonitor(xbmc.Monitor):
"""Discards the stored OAuth token as soon as the configured Client-ID changes.

A token is only valid for the Client-ID it was issued for. Without this, entering a
Client-ID *after* logging in leaves a stale token behind that surfaces much later, on
the next API call, as a "Client id mismatch" dialog -- the trap behind issue #712.
Dropping the token right here makes the order of "log in" and "set Client-ID"
irrelevant: whichever comes last, the user simply logs in once more.

Note on re-entrancy: clearing the store mirrors the empty values back into the Kodi
settings, which fires onSettingsChanged again. Updating self._client_id *before*
clearing makes that second call a no-op.
"""

def __init__(self):
xbmc.Monitor.__init__(self)
self._client_id = get_client_id()

def onSettingsChanged(self):
client_id = get_client_id()
if client_id == self._client_id:
return
self._client_id = client_id
if not get_oauth_token(token_only=True, required=False):
return # no token stored -> nothing to invalidate
clear_oauth_tokens()
log_utils.log('OAuth: Client-ID changed, discarded the token issued for the previous one',
log_utils.LOGNOTICE)
kodi.notify(header=i18n('client_id_mismatch'),
msg=i18n('get_new_oauth_token') % (i18n('settings'), i18n('login'), i18n('device_login')),
duration=7000)


def run():
log_utils.log('Service: Start', log_utils.LOGNOTICE)

monitor = xbmc.Monitor()
monitor = SettingsMonitor()

check_adaptive()

Expand Down
Loading