Implement the auth token invalidation button

This commit is contained in:
DevilXD committed 2026-07-02 15:19:53 +02:00
1 parent 98f166d94d
commit 38f880efc2
4 files changed
+66 -4

No files matched your search

+1
View File
@@ -256,6 +256,7 @@ class State(Enum):
CHANNELS_FETCH = auto() CHANNELS_FETCH = auto()
CHANNELS_CLEANUP = auto() CHANNELS_CLEANUP = auto()
CHANNEL_SWITCH = auto() CHANNEL_SWITCH = auto()
RESTART = auto()
EXIT = auto() EXIT = auto()
+40 -1
View File
@@ -36,7 +36,7 @@ if sys.platform == "darwin":
from translate import _ from translate import _
from cache import ImageCache from cache import ImageCache
from exceptions import MinerException, ExitRequest from exceptions import MinerException, ExitRequest
from utils import resource_path, set_root_icon, webopen, Game, _T from utils import resource_path, set_root_icon, webopen, task_wrapper, Game, _T
from constants import ( from constants import (
MAX_INT, MAX_INT,
SELF_PATH, SELF_PATH,
@@ -61,6 +61,7 @@ if TYPE_CHECKING:
from inventory import DropsCampaign, TimedDrop from inventory import DropsCampaign, TimedDrop
logger = logging.getLogger("TwitchDrops")
TK_PADDING = Union[int, Tuple[int, int], Tuple[int, int, int], Tuple[int, int, int, int]] TK_PADDING = Union[int, Tuple[int, int], Tuple[int, int, int], Tuple[int, int, int, int]]
DIGITS = ceil(log10(WS_TOPICS_LIMIT)) DIGITS = ceil(log10(WS_TOPICS_LIMIT))
@@ -2096,6 +2097,9 @@ class HelpTab:
# use a frame to center the content within the tab # use a frame to center the content within the tab
center_frame = ttk.Frame(master) center_frame = ttk.Frame(master)
center_frame.grid(column=0, row=0) center_frame.grid(column=0, row=0)
# use a frame for the bottom row specifically
bottom_frame = ttk.Frame(master)
bottom_frame.grid(column=0, row=1, sticky="nsew")
irow = 0 irow = 0
# About # About
about = ttk.LabelFrame(center_frame, padding=(4, 0, 4, 4), text="About") about = ttk.LabelFrame(center_frame, padding=(4, 0, 4, 4), text="About")
@@ -2160,6 +2164,41 @@ class HelpTab:
getstarted, text=_("gui", "help", "getting_started_text"), wraplength=self.WIDTH getstarted, text=_("gui", "help", "getting_started_text"), wraplength=self.WIDTH
).grid(sticky="nsew") ).grid(sticky="nsew")
# Invalidate button
invalidate_frame = ttk.Frame(bottom_frame)
bottom_frame.columnconfigure(0, weight=1) # center within the column
invalidate_frame.grid(column=0, row=0, sticky="nse")
ttk.Label(
invalidate_frame, text=_("gui", "help", "invalidate", "text")
).grid(column=0, row=0)
self._invalidate_button: ttk.Button = ttk.Button(
invalidate_frame,
text=_("gui", "help", "invalidate", "button"),
command=self.invalidate_token,
state="disabled",
)
self._invalidate_button.grid(column=1, row=0)
def invalidate_token(self) -> None:
# sync to async bridge
asyncio.create_task(task_wrapper(self._invalidate_token)())
async def _invalidate_token(self) -> None:
auth_state = await self._twitch.get_auth()
async with self._twitch.request(
"POST",
"https://id.twitch.tv/oauth2/revoke",
data={
"client_id": self._twitch._client_type.CLIENT_ID,
"token": auth_state.access_token,
}
) as response:
if response.status == 200:
auth_state.invalidate(delete_cookies=True)
else:
logger.error(f"Failed to invalidate the auth token: {response.status}")
self._twitch.change_state(State.RESTART)
########################################## ##########################################
# GUI DEFINITION END / GUI MANAGER START # # GUI DEFINITION END / GUI MANAGER START #
+10
View File
@@ -197,12 +197,18 @@ class GUIHelpLinks(TypedDict):
campaigns: str campaigns: str
class GUIHelpInvalidate(TypedDict):
button: str
text: str
class GUIHelp(TypedDict): class GUIHelp(TypedDict):
links: GUIHelpLinks links: GUIHelpLinks
how_it_works: str how_it_works: str
how_it_works_text: str how_it_works_text: str
getting_started: str getting_started: str
getting_started_text: str getting_started_text: str
invalidate: GUIHelpInvalidate
class GUIMessages(TypedDict): class GUIMessages(TypedDict):
@@ -434,6 +440,10 @@ default_translation: Translation = {
"the \"Priority mode\", requires you to press on \"Reload\" " "the \"Priority mode\", requires you to press on \"Reload\" "
"for the changes to take an effect." "for the changes to take an effect."
), ),
"invalidate": {
"button": "Invalidate",
"text": "Invalidate the authentication token (log out):",
},
}, },
}, },
} }
+15 -3
View File
@@ -96,6 +96,17 @@ class _AuthState:
if hasattr(self, attr): if hasattr(self, attr):
delattr(self, attr) delattr(self, attr)
def invalidate(self, *, delete_cookies: bool = False) -> None:
self._delattrs("access_token", "user_id")
self._logged_in.clear()
self._twitch.gui.help._invalidate_button.config(state="disabled")
if delete_cookies:
session = self._twitch._session
if session is not None:
jar = cast(aiohttp.CookieJar, session.cookie_jar)
jar.clear()
COOKIES_PATH.unlink(missing_ok=True)
def clear(self) -> None: def clear(self) -> None:
self._delattrs( self._delattrs(
"user_id", "user_id",
@@ -105,6 +116,7 @@ class _AuthState:
"client_version", "client_version",
) )
self._logged_in.clear() self._logged_in.clear()
self._twitch.gui.help._invalidate_button.config(state="disabled")
async def _oauth_login(self) -> str: async def _oauth_login(self) -> str:
login_form: LoginForm = self._twitch.gui.login login_form: LoginForm = self._twitch.gui.login
@@ -414,11 +426,9 @@ class _AuthState:
# update our cookie and save it # update our cookie and save it
jar.update_cookies(cookie, client_info.CLIENT_URL) jar.update_cookies(cookie, client_info.CLIENT_URL)
jar.save(COOKIES_PATH) jar.save(COOKIES_PATH)
self._twitch.gui.help._invalidate_button.config(state="normal")
self._logged_in.set() self._logged_in.set()
def invalidate(self):
self._delattrs("access_token")
class Twitch: class Twitch:
def __init__(self, settings: Settings): def __init__(self, settings: Settings):
@@ -866,6 +876,8 @@ class Twitch:
self.print(_("status", "no_channel")) self.print(_("status", "no_channel"))
self.change_state(State.IDLE) self.change_state(State.IDLE)
del new_watching, selected_channel, watching_channel del new_watching, selected_channel, watching_channel
elif self._state is State.RESTART:
raise ReloadRequest()
elif self._state is State.EXIT: elif self._state is State.EXIT:
self.gui.tray.change_icon("pickaxe") self.gui.tray.change_icon("pickaxe")
self.gui.status.update(_("gui", "status", "exiting")) self.gui.status.update(_("gui", "status", "exiting"))