Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Microsoft Accounts #234

Open
martinpugdal opened this issue Aug 24, 2021 · 11 comments
Open

Microsoft Accounts #234

martinpugdal opened this issue Aug 24, 2021 · 11 comments

Comments

@martinpugdal
Copy link

I think u need to update ur authentication.py file to get microsoft accounts to work with that.
U can example do that authentication.AuthenticationToken().authenticate(username, password, microsoft_account=True).

@MiniDigger
Copy link

MiniDigger commented Aug 26, 2021

sadly its not so straight forward, cause its kinda expected that you do this interactively, I am not sure if MSA has flows enabled that allow username password authentication like that.
see https://wiki.vg/Microsoft_Authentication_Scheme

edit: seems like I am wrong, prismarine is able to use device code flows https://github.com/PrismarineJS/node-minecraft-protocol/blob/master/src/client/microsoftAuth.js

@TheStaticTurtle
Copy link

Actually PrismarineJS doesn't use the device code flow by default, it only does if it can't authenticate on Xbox live with a username / password

Over the last few days I've been developing a custom Minecraft proxy and I needed Microsoft auth for someone, so I tried to implement it.
So here is my python implementation of the xbox live auth: https://github.com/TheStaticTurtle/MineProxy/blob/master/networking/McAuth/MicrosoftAuth.py
It's based on PrismarineJS and the xbox-live-auth module, and it seems to work for the person that I wrote it for, but it might have some bugs still

Actually, my implementation is somewhat compatible with the one of pyCraft, so it might work right away (provided that you only call authenticate and join_server)

@martinpugdal
Copy link
Author

Actually PrismarineJS doesn't use the device code flow by default, it only does if it can't authenticate on Xbox live with a username / password

Over the last few days I've been developing a custom Minecraft proxy and I needed Microsoft auth for someone, so I tried to implement it.
So here is my python implementation of the xbox live auth: https://github.com/TheStaticTurtle/MineProxy/blob/master/networking/McAuth/MicrosoftAuth.py
It's based on PrismarineJS and the xbox-live-auth module, and it seems to work for the person that I wrote it for, but it might have some bugs still

Actually, my implementation is somewhat compatible with the one of pyCraft, so it might work right away (provided that you only call authenticate and join_server)

Hmmm, so how can I use that, because I tried a lot stuff with your implementation and I can't get that to work.
Can you maybe make an example to use that with pycraft?

@TheStaticTurtle
Copy link

Actually, I meant compatible as in "doesn't need to rewrite everything that uses the auth_token variable", my token classes use pretty much the same functions as the current one, so it might work, but I'm not sure how seamless it would be

That said, if something were to work, it would look something like this:

auth_token= MicrosoftAuthenticationToken()
auth_token.authenticate("email", "password")

connection = Connection("127.0.0.1", 25565, auth_token=auth_token)
...

Just for context, I don't actually know how to use pycraft even tho I read a lot of its code for my project, so there might be something I missed.

@tropicbliss
Copy link

I have a similar process that gets the bearer token (drawback is that it ofc doesn't support 2FA enabled accounts). It's in rust but it should be easy enough to understand.
https://github.com/tropicbliss/xboxlive-auth/blob/main/src/xbox.rs

@mckuhei
Copy link

mckuhei commented Oct 17, 2021

I made it on Python! Just change a little for it
microsoft_login.py

@tcrch
Copy link

tcrch commented Mar 7, 2022

I made it on Python! Just change a little for it microsoft_login.py

Links not working. Anyone else has any clue about the Microsoft Login situation?

@Acurisu
Copy link

Acurisu commented Apr 20, 2022

Links not working. Anyone else has any clue about the Microsoft Login situation?

If you are still struggling you can use helper/Auth.py as an example on how to implement it using a local http server and your default webbrowser.

get_auth_code gets the auth_code which you then can pass to authenticate which then returns the auth_token which can be passed to Connection.

The reason I implemented it in two steps was that a server for which one only has console access does not provide a default browser. See README#server.

And as the usual you need an Azure App for this to work to begin with. See README#microsoft-login.

@mckuhei
Copy link

mckuhei commented Apr 20, 2022

I made it on Python! Just change a little for it microsoft_login.py

Links not working. Anyone else has any clue about the Microsoft Login situation?

You need register a toke. For more information, See wiki.vg

@zty012
Copy link

zty012 commented Dec 15, 2022

I made it on Python! Just change a little for it microsoft_login.py

404?

@zty012
Copy link

zty012 commented Dec 26, 2022

@martinersej #253 U can add this code to ur authentication.py

# ... imports
import os

# other code.....

class Microsoft_AuthenticationToken(object):
    """
    Represents an authentication token.
    See https://wiki.vg/Microsoft_Authentication_Scheme.
    """

    UserLoginURL = "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"

    oauth20_URL = 'https://login.live.com/oauth20_token.srf'
    XBL_URL = 'https://user.auth.xboxlive.com/user/authenticate'
    XSTS_URL = 'https://xsts.auth.xboxlive.com/xsts/authorize'
    LOGIN_WITH_XBOX_URL = "https://api.minecraftservices.com/\
authentication/login_with_xbox"

    CheckAccount_URL = 'https://api.minecraftservices.com/entitlements/mcstore'
    Profile_URL = 'https://api.minecraftservices.com/minecraft/profile'

    jwt_Token = ''

    def __init__(self, access_token=None):
        self.access_token = access_token
        self.profile = Profile()

    def GetoAuth20(self, code='') -> object:
        if code == '':
            print("Please copy this link to your browser to open:"
                  "\n%s" % self.UserLoginURL)
            code = input(
                "After logging in,"
                "paste the 'code' field in your browser's address bar here:")
        oauth20 = requests.post(
            self.oauth20_URL,
            data={
                "client_id": "00000000402b5328",
                "code": "{}".format(code),
                "grant_type": "authorization_code",
                "redirect_uri": "https://login.live.com/oauth20_desktop.srf",
                "scope": "service::user.auth.xboxlive.com::MBI_SSL"
            },
            headers={"content-type": "application/x-www-form-urlencoded"},
            timeout=15)
        oauth20 = json.loads(oauth20.text)
        if 'error' in oauth20:
            print("Error: %s" % oauth20["error"])
            return 1
        else:
            self.oauth20_access_token = oauth20['access_token']
            self.oauth20_refresh_token = oauth20['refresh_token']
            oauth20_access_token = oauth20['access_token']
            oauth20_refresh_token = oauth20['refresh_token']
        return {
            "access_token": oauth20_access_token,
            "refresh_token": oauth20_refresh_token
        }

    def GetXBL(self, access_token: str) -> object:
        XBL = requests.post(self.XBL_URL,
                            json={
                                "Properties": {
                                    "AuthMethod": "RPS",
                                    "SiteName": "user.auth.xboxlive.com",
                                    "RpsTicket": "{}".format(access_token)
                                },
                                "RelyingParty": "http://auth.xboxlive.com",
                                "TokenType": "JWT"
                            },
                            headers=HEADERS,
                            timeout=15)
        return {
            "Token": json.loads(XBL.text)['Token'],
            "uhs": json.loads(XBL.text)['DisplayClaims']['xui'][0]['uhs']
        }

    def GetXSTS(self, access_token: str) -> object:
        XBL = requests.post(self.XSTS_URL,
                            json={
                                "Properties": {
                                    "SandboxId": "RETAIL",
                                    "UserTokens": ["{}".format(access_token)]
                                },
                                "RelyingParty":
                                "rp://api.minecraftservices.com/",
                                "TokenType": "JWT"
                            },
                            headers=HEADERS,
                            timeout=15)
        return {
            "Token": json.loads(XBL.text)['Token'],
            "uhs": json.loads(XBL.text)['DisplayClaims']['xui'][0]['uhs']
        }

    def GetXBOX(self, access_token: str, uhs: str) -> str:
        mat_jwt = requests.post(
            self.LOGIN_WITH_XBOX_URL,
            json={"identityToken": "XBL3.0 x={};{}".format(uhs, access_token)},
            headers=HEADERS,
            timeout=15)
        self.access_token = json.loads(mat_jwt.text)['access_token']
        return self.access_token

    def CheckAccount(self, jwt_Token: str) -> bool:
        CheckAccount = requests.get(
            self.CheckAccount_URL,
            headers={"Authorization": "Bearer {}".format(jwt_Token)},
            timeout=15)
        CheckAccount = len(json.loads(CheckAccount.text)['items'])
        if CheckAccount != 0:
            return True
        else:
            return False

    def GetProfile(self, access_token: str) -> object:
        if self.CheckAccount(access_token):
            Profile = requests.get(
                self.Profile_URL,
                headers={"Authorization": "Bearer {}".format(access_token)},
                timeout=15)
            Profile = json.loads(Profile.text)
            if 'error' in Profile:
                return False
            self.profile.id_ = Profile["id"]
            self.profile.name = Profile["name"]
            self.username = Profile["name"]
            return True
        else:
            return False

    @property
    def authenticated(self):
        """
        Attribute which is ``True`` when the token is authenticated and
        ``False`` when it isn't.
        """
        if not self.username:
            return False

        if not self.access_token:
            return False

        if not self.oauth20_refresh_token:
            return False

        if not self.profile:
            return False

        return True

    def authenticate(self):
        "Get verification information for a Microsoft account"
        oauth20 = self.GetoAuth20()
        if oauth20 == 1:
            return False
        XBL = self.GetXBL(oauth20['access_token'])
        XSTS = self.GetXSTS(XBL['Token'])
        XBOX = self.GetXBOX(XSTS['Token'], XSTS['uhs'])
        if self.GetProfile(XBOX):
            print('GameID: {}'.format(self.profile.id_))
            self.PersistenceLogoin_w()
            return True
        else:
            print('Account does not exist')
            return False

    def refresh(self):
        """
        Refreshes the `AuthenticationToken`. Used to keep a user logged in
        between sessions and is preferred over storing a user's password in a
        file.
        Returns:
            Returns `True` if `AuthenticationToken` was successfully refreshed.
            Otherwise it raises an exception.
        Raises:
            minecraft.exceptions.YggdrasilError
            ValueError - if `AuthenticationToken.access_token` or
                `AuthenticationToken.client_token` isn't set.
        """
        if self.access_token is None:
            raise ValueError("'access_token' not set!'")

        if self.oauth20_refresh_token is None:
            raise ValueError("'oauth20_refresh_token' is not set!")

        oauth20 = requests.post(
            self.oauth20_URL,
            data={
                "client_id": "00000000402b5328",
                "refresh_token": "{}".format(self.oauth20_refresh_token),
                "grant_type": "refresh_token",
                "redirect_uri": "https://login.live.com/oauth20_desktop.srf",
                "scope": "service::user.auth.xboxlive.com::MBI_SSL"
            },
            headers={"content-type": "application/x-www-form-urlencoded"},
            timeout=15)
        oauth20 = json.loads(oauth20.text)
        if 'error' in oauth20:
            print("Error: %s" % oauth20["error"])
            return False
        else:
            self.oauth20_access_token = oauth20['access_token']
            self.oauth20_refresh_token = oauth20['refresh_token']
            XBL = self.GetXBL(self.oauth20_access_token)
            XSTS = self.GetXSTS(XBL['Token'])
            XBOX = self.GetXBOX(XSTS['Token'], XSTS['uhs'])
            if self.GetProfile(XBOX):
                self.PersistenceLogoin_w()
                print('account: {}'.format(self.profile.id_))
                return True
            else:
                print('Account does not exist')
                return False

    def join(self, server_id):
        """
        Informs the Mojang session-server that we're joining the
        MineCraft server with id ``server_id``.
        Parameters:
            server_id - ``str`` with the server id
        Returns:
            ``True`` if no errors occured
        Raises:
            :class:`minecraft.exceptions.YggdrasilError`
        """
        if not self.authenticated:
            err = "AuthenticationToken hasn't been authenticated yet!"
            raise YggdrasilError(err)

        res = _make_request(
            SESSION_SERVER, "join", {
                "accessToken": self.access_token,
                "selectedProfile": self.profile.to_dict(),
                "serverId": server_id
            })

        if res.status_code != 204:
            _raise_from_response(res)
        return True

    def PersistenceLogoin_w(self):
        "Save access token persistent login"
        ProjectDir = os.path.dirname(os.path.dirname('{}'.format(__file__)))
        PersistenceDir = '{}/Persistence'.format(ProjectDir)
        if not self.authenticated:
            err = "AuthenticationToken hasn't been authenticated yet!"
            raise YggdrasilError(err)
        if not os.path.exists(PersistenceDir):
            os.mkdir(PersistenceDir)
        print(PersistenceDir)
        "Save access_token and oauth20_refresh_token"
        with open("{}/{}".format(PersistenceDir, self.username),
                  mode='w',
                  encoding='utf-8') as file_obj:
            file_obj.write('{{"{}": "{}","{}": "{}"}}'.format(
                'access_token', self.access_token, 'oauth20_refresh_token',
                self.oauth20_refresh_token))
            file_obj.close()
        return True

    def PersistenceLogoin_r(self, GameID: str):
        "Load access token persistent login"
        ProjectDir = os.path.dirname(os.path.dirname('{}'.format(__file__)))
        PersistenceDir = '{}/Persistence'.format(ProjectDir)
        if not os.path.exists(PersistenceDir):
            return False
        "Load access_token and oauth20_refresh_token"
        if os.path.isfile("{}/{}".format(PersistenceDir, GameID)):
            with open("{}/{}".format(PersistenceDir, GameID),
                      mode='r',
                      encoding='utf-8') as file_obj:
                Persistence = file_obj.read()
                file_obj.close()
                Persistence = json.loads(Persistence)
                self.access_token = Persistence["access_token"]
                self.oauth20_refresh_token = Persistence[
                    "oauth20_refresh_token"]
                self.refresh()
            return self.authenticated
        else:
            return False
Then u can
auth = minecraft.authentication.Microsoft_AuthenticationToken()
auth.authenticate()
conn = minecraft.networking.connection.Connection("mc.hypixel.net", 25565, auth, None, "1.8")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants