From c5249008ddf96c1d2fd0fa6f4b0a56f6567b25e0 Mon Sep 17 00:00:00 2001 From: ABaumher <78619068+ABaumher@users.noreply.github.com> Date: Mon, 19 Jun 2023 16:55:54 -0400 Subject: [PATCH] No 2 fa bugfix (#29) enums: Corrected type hint in `to_TwoFactorMethod` steam_auth_polling_data: Corrected 'any' check in 'has_valid_confirmation_method'. No 2FA's enum value is 0, which was being coerced to False. backend_steam_network: The value from no two-factor is now returned instead of set to result and unused. The _handle_steam_guard_none function now properly calls the finish auth process so we're actually logged in. previously it'd just short-circuit that process and then error later. Properly does this, expected wrong UserActionRequired value and also wasn't awaited. in other words, several small, dumb mistakes. bumped manifest and current version to 1.0.7 as a result of these changes. Hardened the User Info Cache code so it would not error if the data somehow was cleared to Null and written to the credentials storage. It will also write an empty dict to the credentials instead of a dict of nulls. Stripped the leading and trailing whitespace characters during 2FA code inputs so the whitespace wouldn't make your 2FA fail. Properly type-hint 2FA code so it would resolve nicely in any IDE or MyPy Fixed broken urls in current_version,json Updated version.py to have 1.0.7 with relevant changelog information Hardened the User Info Cache code so it would not error if the data somehow was cleared to Null and written to the credentials storage. It will also write an empty dict to the credentials instead of a dict of nulls. Stripped the leading and trailing whitespace characters during 2FA code inputs so the whitespace wouldn't make your 2FA fail. Properly type-hint 2FA code so it would resolve nicely in any IDE or MyPy Fixed broken urls in current_version,json update gitignore to avoid .gz files. The mac with installer is uses that compression. partially updated the batch file to deal with issues, still needs some tweaking --- .gitignore | 1 + auto-copy-windows.bat | 2 +- current_version.json | 6 +-- src/backend_steam_network.py | 22 +++++----- src/manifest.json | 2 +- src/steam_network/enums.py | 2 +- src/steam_network/steam_auth_polling_data.py | 2 +- src/steam_network/user_info_cache.py | 43 ++++++++++++-------- src/version.py | 6 ++- 9 files changed, 50 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index ccee25fb..fb22dbb2 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ output/ /protoc *.zip +*.gz diff --git a/auto-copy-windows.bat b/auto-copy-windows.bat index 3bf633b0..27578cf8 100644 --- a/auto-copy-windows.bat +++ b/auto-copy-windows.bat @@ -6,4 +6,4 @@ set "PLUGIN_PATH=%localappdata%\GOG.com\Galaxy\plugins\installed\steam_ca27391f- rmdir /S /Q -rf "%PLUGIN_PATH%" mkdir "%PLUGIN_PATH%" -powershell Expand-Archive ".\windows.zip" -DestinationPath %PLUGIN_PATH% \ No newline at end of file +powershell "[Environment]::CurrentDirectory = Get-Location; Expand-Archive '.\windows.zip' -DestinationPath '%PLUGIN_PATH%'" \ No newline at end of file diff --git a/current_version.json b/current_version.json index d8049f73..83d35dca 100644 --- a/current_version.json +++ b/current_version.json @@ -1,12 +1,12 @@ { - "tag_name": "v1.0.6", + "tag_name": "v1.0.7", "assets": [ { - "browser_download_url": "https://github.com/ABaumher/galaxy-integration-steam/releases/download/v1.0.6-beta/windows.zip", + "browser_download_url": "https://github.com/ABaumher/galaxy-integration-steam/releases/download/v1.0.7/windows.zip", "name": "windows.zip" }, { - "browser_download_url": "https://github.com/ABaumher/galaxy-integration-steam/releases/download/v1.0.6-beta/mac.zip", + "browser_download_url": "https://github.com/ABaumher/galaxy-integration-steam/releases/download/v1.0.7/mac.zip", "name": "mac.zip" } ] diff --git a/src/backend_steam_network.py b/src/backend_steam_network.py index a42cb020..19ee84c9 100644 --- a/src/backend_steam_network.py +++ b/src/backend_steam_network.py @@ -2,7 +2,7 @@ import logging import ssl from contextlib import suppress -from typing import Callable, List, Any, Dict, Union, Coroutine +from typing import Callable, List, Any, Dict, Union, Coroutine, cast from urllib import parse from pprint import pformat @@ -192,7 +192,7 @@ def _get_mobile_confirm_kwargs(self, allowed_methods: Dict[TwoFactorMethod, str] return fallbackData - async def pass_login_credentials(self, step, credentials, cookies): + async def pass_login_credentials(self, step, credentials: Dict[str, str], cookies): end_uri = credentials["end_uri"] if (DisplayUriHelper.LOGIN.EndUri() in end_uri): @@ -219,7 +219,7 @@ def sanitize_string(data : str) -> str: """ return (''.join([i if ord(i) < 128 else '' for i in data]))[:64] - async def _handle_login_finished(self, credentials) -> Union[NextStep, Authentication]: + async def _handle_login_finished(self, credentials: Dict[str, str]) -> Union[NextStep, Authentication]: parsed_url = parse.urlsplit(credentials["end_uri"]) params = parse.parse_qs(parsed_url.query) if ("password" not in params or "username" not in params): @@ -236,7 +236,7 @@ async def _handle_login_finished(self, credentials) -> Union[NextStep, Authentic allowed_methods = self._authentication_cache.two_factor_allowed_methods method, msg = allowed_methods[0] if (method == TwoFactorMethod.Nothing): - result = await self._handle_steam_guard_none() + return await self._handle_steam_guard_none() elif (method == TwoFactorMethod.PhoneCode): return next_step_response_simple(DisplayUriHelper.TWO_FACTOR_MOBILE) elif (method == TwoFactorMethod.EmailCode): @@ -250,12 +250,12 @@ async def _handle_login_finished(self, credentials) -> Union[NextStep, Authentic return next_step_response_simple(DisplayUriHelper.LOGIN, True) #result here should be password, or unathorized. - async def _handle_steam_guard(self, credentials, method: TwoFactorMethod, fallback: DisplayUriHelper) -> Union[NextStep, Authentication]: - parsed_url = parse.urlsplit(credentials["end_uri"]) + async def _handle_steam_guard(self, credentials: Dict[str, str], method: TwoFactorMethod, fallback: DisplayUriHelper) -> Union[NextStep, Authentication]: + parsed_url: parse.SplitResult = parse.urlsplit(credentials["end_uri"]) params = parse.parse_qs(parsed_url.query) if ("code" not in params): return next_step_response_simple(fallback, True) - code = params["code"][0] + code = params["code"][0].strip() await self._websocket_client.communication_queues["websocket"].put({'mode': AuthCall.UPDATE_TWO_FACTOR, 'two-factor-code' : code, 'two-factor-method' : method }) result = await self._get_websocket_auth_step() if (result == UserActionRequired.NoActionConfirmLogin): @@ -269,10 +269,12 @@ async def _handle_steam_guard(self, credentials, method: TwoFactorMethod, fallba async def _handle_steam_guard_none(self) -> Authentication: result = await self._handle_2FA_PollOnce() - if (result != UserActionRequired.NoActionRequired): - raise UnknownBackendResponse() - else: + if (result == UserActionRequired.NoActionRequired): return Authentication(self._user_info_cache.steam_id, self._user_info_cache.persona_name) + elif result == UserActionRequired.NoActionConfirmToken: + return await self._finish_auth_process() + else: + raise UnknownBackendResponse() async def _handle_steam_guard_check(self, fallback: DisplayUriHelper, is_confirm: bool, **kwargs:str) -> Union[NextStep, Authentication]: result = await self._handle_2FA_PollOnce(is_confirm) diff --git a/src/manifest.json b/src/manifest.json index 06e42a51..207ec76a 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -2,7 +2,7 @@ "name": "Galaxy Steam plugin Version 2", "platform": "steam", "guid": "ca27391f-2675-49b1-92c0-896d43afa4f8", - "version": "1.0.6", + "version": "1.0.7", "description": "Galaxy plugin for Steam. Updated with new authentication logic", "author": "ABaumher, don-de-marco, et al.", "email": "baumhera912@gmail.com", diff --git a/src/steam_network/enums.py b/src/steam_network/enums.py index 8d1dad75..40e96782 100644 --- a/src/steam_network/enums.py +++ b/src/steam_network/enums.py @@ -114,7 +114,7 @@ class TwoFactorMethod(enum.IntEnum): #EmailConfirm = 5 #Does not exist? Likely something Steam thought about implementing and decided not to. if that changes, we can support it. -def to_TwoFactorMethod(auth_enum : EnumTypeWrapper) -> TwoFactorMethod: +def to_TwoFactorMethod(auth_enum : Union[CAuthentication_AllowedConfirmation, EnumTypeWrapper]) -> TwoFactorMethod: if (isinstance(auth_enum, CAuthentication_AllowedConfirmation)): auth_enum = auth_enum.confirmation_type ret_val, _ = _to_TwoFactorMethod(auth_enum, None) diff --git a/src/steam_network/steam_auth_polling_data.py b/src/steam_network/steam_auth_polling_data.py index c3f3d33d..d766601d 100644 --- a/src/steam_network/steam_auth_polling_data.py +++ b/src/steam_network/steam_auth_polling_data.py @@ -48,4 +48,4 @@ def extended_error_message(self): def has_valid_confirmation_method(self): #if any value in allowed confirmations does not equal unknown, return true. else return false. - return any(c for c in self._allowed_confirmations if c != TwoFactorMethod.Unknown) + return any(c != TwoFactorMethod.Unknown for c in self._allowed_confirmations) diff --git a/src/steam_network/user_info_cache.py b/src/steam_network/user_info_cache.py index 69b95e16..c137a1b2 100644 --- a/src/steam_network/user_info_cache.py +++ b/src/steam_network/user_info_cache.py @@ -1,7 +1,7 @@ import asyncio import base64 import logging -from typing import Optional +from typing import Optional, Dict logger = logging.getLogger(__name__) @@ -31,30 +31,37 @@ def is_initialized(self) -> bool: def to_dict(self): - creds = { - 'steam_id': base64.b64encode(str(self._steam_id).encode('utf-8')).decode('utf-8'), - 'refresh_token': base64.b64encode(str(self._refresh_token).encode('utf-8')).decode('utf-8'), - 'account_username': base64.b64encode(str(self._account_username).encode('utf-8')).decode('utf-8'), - 'persona_name': base64.b64encode(str(self._persona_name).encode('utf-8')).decode('utf-8'), - } + creds = {} + if self.is_initialized(): + creds = { + 'steam_id': base64.b64encode(str(self._steam_id).encode('utf-8')).decode('utf-8'), + 'refresh_token': base64.b64encode(self._refresh_token.encode('utf-8')).decode('utf-8'), + 'account_username': base64.b64encode(self._account_username.encode('utf-8')).decode('utf-8'), + 'persona_name': base64.b64encode(self._persona_name.encode('utf-8')).decode('utf-8'), + } return creds - def from_dict(self, lookup): - for key in lookup.keys(): - if lookup[key]: + def from_dict(self, lookup: Dict[str, str]): + for key, val in lookup.items(): + if val: logger.info(f"Loaded {key} from stored credentials") - if 'steam_id' in lookup: - self._steam_id = int(base64.b64decode(lookup['steam_id']).decode('utf-8')) + item = lookup.get('steam_id') + if item is not None: + self._steam_id = int(base64.b64decode(item).decode('utf-8')) + + item = lookup.get('account_username') + if item is not None: + self._account_username = base64.b64decode(item).decode('utf-8') - if 'account_username' in lookup: - self._account_username = base64.b64decode(lookup['account_username']).decode('utf-8') - if 'persona_name' in lookup: - self._persona_name = base64.b64decode(lookup['persona_name']).decode('utf-8') + item = lookup.get('persona_name') + if item is not None: + self._persona_name = base64.b64decode(item).decode('utf-8') - if 'refresh_token' in lookup: - self._refresh_token = base64.b64decode(lookup['refresh_token']).decode('utf-8') + item = lookup.get('refresh_token') + if item is not None: + self._refresh_token = base64.b64decode(item).decode('utf-8') @property def changed(self): diff --git a/src/version.py b/src/version.py index bf49df36..36f450af 100755 --- a/src/version.py +++ b/src/version.py @@ -1,7 +1,11 @@ -__version__ = "1.0.6" +__version__ = "1.0.7" __changelog__ = { "unreleased": ''' ''', + "1.0.7": """ + - Fixes issues when SteamGuard is disabled. Made it so 2FA codes would ignore leading or trailing whitespace. + - Code cleanup + """, "1.0.6": """ - reintroduces password santization so users with long passwords or illegal characters can log in as intended - Code cleanup