From cf41a93615600e7177959353dc7b4f3a6d993515 Mon Sep 17 00:00:00 2001 From: MC_cubes Date: Sat, 22 Jan 2022 20:55:01 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=BE=AE=E8=BD=AF?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E5=92=8C=E8=87=AA=E5=8A=A8=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + config.json | 2 + readme_cn.md | 4 ++ utils/config.py | 2 + utils/pycraft/authentication.py | 10 +++ utils/pycraft/microsoft_authentication.py | 78 +++++++++++++++++++++++ utils/recorder.py | 22 ++++++- 7 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 utils/pycraft/microsoft_authentication.py diff --git a/.gitignore b/.gitignore index 6fbb52b..82887fe 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ PCRC.spec tools/__RFETMP__ tools/protocol temp_recording/ +/TOKEN \ No newline at end of file diff --git a/config.json b/config.json index 8e70ef4..844f7d3 100644 --- a/config.json +++ b/config.json @@ -7,6 +7,8 @@ "online_mode": false, "username": "bot_PCRC", "password": "secret", + "micro_token": "", + "auto": false, "address": "localhost", "port": 25565, "server_name": "SECRET SERVER", diff --git a/readme_cn.md b/readme_cn.md index 265602e..6a264eb 100644 --- a/readme_cn.md +++ b/readme_cn.md @@ -77,6 +77,10 @@ PCRC 目前支持连接官服原版 Minecraft 服务端,支持以下版本: `password`: 用于正版登录时的 Minecraft 账号的密码 +`micro_token`: 微软登录token,用于微软登录。不填写则使用账号密码登录。获取方式:将`micro_token`填写为任意字符串,如`123`。将输出的网址在浏览器中打开,登录 Microsoft 账号。登录成功后,网页将重定向至空白页面。复制网址中的code参数再次填入。(注意不要包含后面的lc参数) + +`auto`: 是否开启自动登录,不影响用户名密码登录。需要 TOKEN 文件(微软登录成功后会自动生成,并把此选项自动改为`true`) + `address`: Minecraft 服务器的 IP 地址 `port`: Minecraft 服务器的端口 diff --git a/utils/config.py b/utils/config.py index 448fe43..130216d 100644 --- a/utils/config.py +++ b/utils/config.py @@ -11,6 +11,8 @@ "online_mode": false, "username": "bot_PCRC", "password": "secret", + "micro_token": "", + "auto": false, "address": "localhost", "port": 20000, "server_name": "SECRET SERVER", diff --git a/utils/pycraft/authentication.py b/utils/pycraft/authentication.py index e78e875..fb005c3 100644 --- a/utils/pycraft/authentication.py +++ b/utils/pycraft/authentication.py @@ -2,6 +2,7 @@ import json import uuid from .exceptions import YggdrasilError +from .microsoft_authentication import get_login_info #: The base url for Ygdrassil requests AUTH_SERVER = "https://authserver.mojang.com" @@ -133,6 +134,15 @@ def authenticate(self, username, password, invalidate_previous=False): return True + def microsoft_authenticate(self, microsoftToken: str): + resp = get_login_info(microsoftToken) + self.username = resp[2] + self.access_token = resp[0] + self.client_token = uuid.uuid4().hex + self.profile.id_ = resp[1] + self.profile.name = resp[2] + return True + def refresh(self): """ Refreshes the `AuthenticationToken`. Used to keep a user logged in diff --git a/utils/pycraft/microsoft_authentication.py b/utils/pycraft/microsoft_authentication.py new file mode 100644 index 0000000..a428780 --- /dev/null +++ b/utils/pycraft/microsoft_authentication.py @@ -0,0 +1,78 @@ +import requests +import json +from ..logger import Logger + +logger = Logger(name='PCRC') + +# https://login.live.com/oauth20_authorize.srf?client_id=00000000402b5328&response_type=code&scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL&redirect_uri=https%3A%2F%2Flogin.live.com%2Foauth20_desktop.srf +def getTokenByAccessCode(i): + context = {"client_id": "00000000402b5328", + "code": i, + "grant_type": "authorization_code", + "redirect_uri": "https://login.live.com/oauth20_desktop.srf", + "scope": "service::user.auth.xboxlive.com::MBI_SSL"} + + response = requests.post("https://login.live.com/oauth20_token.srf", context) + return response.json().get("access_token") + + +def XBL(i): + headers = {'Content-Type': 'application/json', "Accept": "application/json"} + context = { + "Properties": { + "AuthMethod": "RPS", + "SiteName": "user.auth.xboxlive.com", + "RpsTicket": i + }, + "RelyingParty": "http://auth.xboxlive.com", + "TokenType": "JWT" + } + response = requests.post("https://user.auth.xboxlive.com/user/authenticate", json.dumps(context), headers=headers) + if response.content == b'': + msg = "microsoft token is expired! Get it from https://login.live.com/oauth20_authorize.srf?client_id=00000000402b5328&response_type=code&scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL&redirect_uri=https%3A%2F%2Flogin.live.com%2Foauth20_desktop.srf" + raise ValueError(msg) + else: + return response.json().get("Token") + + +def XSL(i): + headers = {'Content-Type': 'application/json', "Accept": "application/json"} + context = { + "Properties": { + "SandboxId": "RETAIL", + "UserTokens": [ + i + ] + }, + "RelyingParty": "rp://api.minecraftservices.com/", + "TokenType": "JWT" + } + response = requests.post("https://xsts.auth.xboxlive.com/xsts/authorize", json.dumps(context), headers=headers) + response = response.json() + return response.get("Token"), response.get("DisplayClaims").get("xui")[0].get("uhs") + + +def minecraft(token, uhs): + headers = {'Content-Type': 'application/json', "Accept": "application/json"} + context = { + "identityToken": f"XBL3.0 x={uhs};{token}" + } + response = requests.post("https://api.minecraftservices.com/authentication/login_with_xbox", json.dumps(context), headers=headers) + + return response.json().get("access_token") + + +def getUuid(i): + headers = {"Authorization": f"Bearer {i}"} + res = requests.get("https://api.minecraftservices.com/minecraft/profile", headers=headers) + res = res.json() + return res.get("id"), res.get("name") + + +def get_login_info(accessCode: str) -> list: + Token = getTokenByAccessCode(accessCode) + XBL_token = XBL(Token) + XSL_token, uhs = XSL(XBL_token) + MineToken = minecraft(XSL_token, uhs) + ID, Name = getUuid(MineToken) + return [MineToken, ID, Name] diff --git a/utils/recorder.py b/utils/recorder.py index d894f46..072fe4d 100644 --- a/utils/recorder.py +++ b/utils/recorder.py @@ -3,6 +3,7 @@ import copy import heapq import os +import pickle import shutil import socket import threading @@ -55,8 +56,24 @@ def __init__(self, config_file, translation_folder): else: self.logger.log("Login in online mode") auth_token = authentication.AuthenticationToken() - auth_token.authenticate(self.config.get('username'), self.config.get('password')) - self.logger.log("Logged in as %s" % auth_token.profile.name) + micro_token = self.config.get('micro_token') + + if self.config.get('auto'): + with open("TOKEN", "rb") as f: + auth_token = pickle.load(f) + elif micro_token is not None or micro_token != '': + auth_token.microsoft_authenticate(micro_token) + print("Logged in as %s..." % auth_token.username) + + self.config.set_value('micro_token', '') + with open("TOKEN", "wb") as f: + pickle.dump(auth_token, f) + self.config.set_value('auto', True) + self.config.write_to_file() + else: + auth_token.authenticate(self.config.get('username'), self.config.get('password')) + self.logger.log("Logged in as %s" % auth_token.profile.name) + self.config.set_value('username', auth_token.profile.name) self.connection = Connection(self.config.get('address'), self.config.get('port'), auth_token=auth_token, @@ -65,6 +82,7 @@ def __init__(self, config_file, translation_folder): allowed_versions=constant.ALLOWED_VERSIONS, handle_exception=self.onConnectionException ) + self.connection.register_packet_listener(self.onPacketReceived, PycraftPacket) self.connection.register_packet_listener(self.onPacketSent, PycraftPacket, outgoing=True) From d8e0993811cceba1661d84f824f9df99d9743d77 Mon Sep 17 00:00:00 2001 From: MC_cubes Date: Sat, 22 Jan 2022 21:11:37 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=BE=AE=E8=BD=AF?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E5=92=8C=E8=87=AA=E5=8A=A8=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/config.py b/utils/config.py index 130216d..4f516bb 100644 --- a/utils/config.py +++ b/utils/config.py @@ -12,7 +12,7 @@ "username": "bot_PCRC", "password": "secret", "micro_token": "", - "auto": false, + "auto": false, "address": "localhost", "port": 20000, "server_name": "SECRET SERVER",