better auth checking

This commit is contained in:
əlemi 2021-11-11 13:00:17 +01:00
parent d8dc04e663
commit 699536fd78
2 changed files with 31 additions and 14 deletions

View file

@ -8,7 +8,7 @@ from typing import Dict, List, Callable, Type, Optional, Tuple
from .dispatcher import Dispatcher, ConnectionState from .dispatcher import Dispatcher, ConnectionState
from .mc.mctypes import VarInt from .mc.mctypes import VarInt
from .mc.packet import Packet from .mc.packet import Packet
from .mc.identity import Token from .mc.identity import Token, AuthException
from .mc import proto, encryption from .mc import proto, encryption
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
@ -34,6 +34,7 @@ _STATE_REGS = {
class Client: class Client:
host:str host:str
port:int port:int
options:dict
username:Optional[str] username:Optional[str]
password:Optional[str] password:Optional[str]
@ -41,6 +42,7 @@ class Client:
dispatcher : Dispatcher dispatcher : Dispatcher
_processing : bool _processing : bool
_authenticated : bool
_worker : Task _worker : Task
_packet_callbacks : Dict[ConnectionState, Dict[Packet, List[Callable]]] _packet_callbacks : Dict[ConnectionState, Dict[Packet, List[Callable]]]
@ -50,12 +52,14 @@ class Client:
self, self,
host:str, host:str,
port:int, port:int,
options:dict = None,
username:Optional[str] = None, username:Optional[str] = None,
password:Optional[str] = None, password:Optional[str] = None,
token:Optional[Token] = None, token:Optional[Token] = None,
): ):
self.host = host self.host = host
self.port = port self.port = port
self.options = options or {}
self.token = token self.token = token
self.username = username self.username = username
@ -63,6 +67,7 @@ class Client:
self.dispatcher = Dispatcher() self.dispatcher = Dispatcher()
self._processing = False self._processing = False
self._authenticated = False
self._packet_callbacks = {} self._packet_callbacks = {}
@ -97,15 +102,12 @@ class Client:
self.token = await Token.authenticate(self.username, self.password) self.token = await Token.authenticate(self.username, self.password)
self._logger.info("Authenticated from credentials") self._logger.info("Authenticated from credentials")
return True return True
return False raise AuthException("No token or credentials provided")
try: try:
await self.token.validate() # will raise an exc if token is invalid await self.token.validate() # will raise an exc if token is invalid
except Exception: # idk TODO except AuthException:
try: await self.token.refresh()
await self.token.refresh() self._logger.warning("Refreshed Token")
self._logger.warning("Refreshed Token")
except Exception:
return False
return True return True
async def change_server(self, server:str): async def change_server(self, server:str):
@ -129,10 +131,12 @@ class Client:
await self.start() await self.start()
try: try:
while True: # TODO don't busywait even if it doesn't matter much while self._processing: # TODO don't busywait even if it doesn't matter much
await asyncio.sleep(5) await asyncio.sleep(5)
except KeyboardInterrupt: except KeyboardInterrupt:
self._logger.info("Received SIGINT, stopping...") self._logger.info("Received SIGINT, stopping...")
else:
self._logger.warning("Client terminating...")
await self.stop() await self.stop()
@ -150,19 +154,25 @@ class Client:
async def _client_worker(self): async def _client_worker(self):
while self._processing: while self._processing:
if not await self.authenticate(): try:
raise Exception("Token not refreshable or credentials invalid") # TODO! await self.authenticate()
except AuthException:
self._logger.error("Token not refreshable or credentials invalid")
await self.stop(block=False)
try: try:
await self.dispatcher.connect(self.host, self.port) await self.dispatcher.connect(self.host, self.port)
for packet in self._handshake(): for packet in self._handshake():
await self.dispatcher.write(packet) await self.dispatcher.write(packet)
self.dispatcher.state = ConnectionState.LOGIN self.dispatcher.state = ConnectionState.LOGIN
await self._process_packets() await self._process_packets()
except AuthException as e: # TODO maybe tell what went wrong
self._authenticated = False
self._logger.error("Authentication exception")
except ConnectionRefusedError: except ConnectionRefusedError:
self._logger.error("Server rejected connection") self._logger.error("Server rejected connection")
except Exception: except Exception:
self._logger.exception("Exception in Client connection") self._logger.exception("Exception in Client connection")
await asyncio.sleep(2) await asyncio.sleep(self.options["rctime"])
def _handshake(self, force:bool=False) -> Tuple[Packet, Packet]: # TODO make this fancier! poll for version and status first def _handshake(self, force:bool=False) -> Tuple[Packet, Packet]: # TODO make this fancier! poll for version and status first
return ( proto.handshaking.serverbound.PacketSetProtocol( return ( proto.handshaking.serverbound.PacketSetProtocol(

View file

@ -1,12 +1,16 @@
"""Minecraft identity utilities.""" """Minecraft identity utilities."""
import json import json
import uuid import uuid
import logging
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional from typing import Optional
import aiohttp import aiohttp
class AuthException(Exception):
pass
@dataclass @dataclass
class Profile: class Profile:
id : str id : str
@ -96,6 +100,9 @@ class Token:
async def _post(cls, endpoint:str, data:dict) -> dict: async def _post(cls, endpoint:str, data:dict) -> dict:
async with aiohttp.ClientSession() as sess: async with aiohttp.ClientSession() as sess:
async with sess.post(endpoint, headers=cls.HEADERS, data=json.dumps(data).encode('utf-8')) as res: async with sess.post(endpoint, headers=cls.HEADERS, data=json.dumps(data).encode('utf-8')) as res:
# TODO parse and raise exceptions data = await res.json(content_type=None)
return await res.json(content_type=None) logging.info(f"Auth request | {data}")
if res.status != 200:
raise AuthException(f"Action '{endpoint.rsplit('/',1)[1]}' did not succeed")
return data