feat: allow overriding auth and session servers

This commit is contained in:
əlemi 2023-02-16 18:07:21 +01:00
parent 7bc7a14457
commit 2190554f71
Signed by: alemi
GPG key ID: A4895B84D311642C
2 changed files with 39 additions and 24 deletions

View file

@ -26,10 +26,14 @@ class AuthException(Exception):
class AuthInterface:
accessToken : str
selectedProfile : GameProfile
session_server_override : str
SESSION_SERVER = "https://sessionserver.mojang.com/session/minecraft"
TIMEOUT = aiohttp.ClientTimeout(total=3)
def __init__(self):
raise NotImplementedError
async def login(self) -> 'AuthInterface':
raise NotImplementedError
@ -45,9 +49,13 @@ class AuthInterface:
def deserialize(self, data:Dict[str, Any]):
raise NotImplementedError
@property
def session_server(self) -> str:
return self.session_server_override or self.SESSION_SERVER
async def join(self, server_id) -> dict:
return await self._post(
self.SESSION_SERVER + "/join",
self.session_server + "/join",
headers={"content-type":"application/json"},
json={
"serverId": server_id,
@ -56,16 +64,14 @@ class AuthInterface:
}
)
@classmethod # TODO more love for server side!
async def server_join(cls, username:str, serverId:str, ip:Optional[str] = None):
async def server_join(self, username:str, serverId:str, ip:Optional[str] = None):
params = {"username":username, "serverId":serverId}
if ip:
params["ip"] = ip
return await cls._get(cls.SESSION_SERVER + "/hasJoined", params=params)
return await self._get(self.session_server + "/hasJoined", params=params)
@classmethod
async def _post(cls, endpoint:str, **kwargs) -> Dict[str, Any]:
async with aiohttp.ClientSession(timeout=cls.TIMEOUT) as session:
async def _post(self, endpoint:str, **kwargs) -> Dict[str, Any]:
async with aiohttp.ClientSession(timeout=self.TIMEOUT) as session:
try:
async with session.post(endpoint, **kwargs) as res:
try:
@ -79,9 +85,8 @@ class AuthInterface:
except TimeoutError:
raise AuthException(endpoint, 0, {"error": "request timed out"}, kwargs)
@classmethod
async def _get(cls, endpoint:str, **kwargs) -> Dict[str, Any]:
async with aiohttp.ClientSession(timeout=cls.TIMEOUT) as session:
async def _get(self, endpoint:str, **kwargs) -> Dict[str, Any]:
async with aiohttp.ClientSession(timeout=self.TIMEOUT) as session:
try:
async with session.get(endpoint, **kwargs) as res:
try:

View file

@ -1,23 +1,23 @@
"""Minecraft identity utilities."""
import json
import uuid
import logging
from dataclasses import dataclass
from typing import Optional, Dict, Any
import aiohttp
from .interface import AuthInterface, AuthException
from ..definitions import GameProfile
@dataclass
class MojangAuthenticator(AuthInterface):
#selectedProfile : GameProfile
#accessToken : str
#session_server_override : str
username : str
password : Optional[str]
accessToken : str
clientToken : str
selectedProfile : GameProfile
auth_server_override : str
AGENT_NAME = "Minecraft"
AGENT_VERSION = 1
@ -25,12 +25,19 @@ class MojangAuthenticator(AuthInterface):
CONTENT_TYPE = "application/json"
HEADERS = {"content-type": CONTENT_TYPE}
def __init__(self, username:str="", password:Optional[str]=None):
def __init__(
self, username:str="",
password:Optional[str]=None,
session_server_override:Optional[str]=None,
auth_server_override:Optional[str]=None,
):
self.username = username
self.password = password
self.accessToken = ""
self.clientToken = ""
self.selectedProfile = GameProfile("", username)
self.session_server_override = session_server_override
self.auth_server_override = auth_server_override
def __equals__(self, other) -> bool:
if not isinstance(other, self.__class__):
@ -48,6 +55,10 @@ class MojangAuthenticator(AuthInterface):
def __str__(self) -> str:
return repr(self)
@property
def auth_server(self) -> str:
return self.auth_server_override or self.AUTH_SERVER
def serialize(self) -> Dict[str, Any]:
return {
"username":self.username,
@ -75,7 +86,7 @@ class MojangAuthenticator(AuthInterface):
payload["clientToken"] = uuid.uuid4().hex # don't include this to invalidate all other sessions
res = await self._post(self.AUTH_SERVER + "/authenticate", json=payload)
res = await self._post(self.auth_server + "/authenticate", json=payload)
self.accessToken=res["accessToken"]
self.clientToken=res["clientToken"]
@ -83,11 +94,10 @@ class MojangAuthenticator(AuthInterface):
return self
@classmethod
async def sign_out(cls, username:str, password:str) -> dict:
return await cls._post(
cls.AUTH_SERVER + "/signout",
headers=cls.HEADERS,
async def sign_out(self, username:str, password:str) -> dict:
return await self._post(
self.auth_server + "/signout",
headers=self.HEADERS,
json={
"username": username,
"password": password
@ -98,7 +108,7 @@ class MojangAuthenticator(AuthInterface):
if not self.accessToken or not self.clientToken:
raise AuthException("/refresh", 0, {"message":"No access token or client token"}, {})
res = await self._post(
self.AUTH_SERVER + "/refresh",
self.auth_server + "/refresh",
headers=self.HEADERS,
json={
"accessToken": self.accessToken,
@ -122,7 +132,7 @@ class MojangAuthenticator(AuthInterface):
if clientToken:
payload["clientToken"] = self.clientToken
await self._post(
self.AUTH_SERVER + "/validate",
self.auth_server + "/validate",
headers=self.HEADERS,
json=payload,
)
@ -130,7 +140,7 @@ class MojangAuthenticator(AuthInterface):
async def invalidate(self) -> AuthInterface:
await self._post(
self.AUTH_SERVER + "/invalidate",
self.auth_server + "/invalidate",
headers=self.HEADERS,
json= {
"accessToken": self.accessToken,