msmc

A bare bones login library for Minecraft based projects to authenticate individuals with a Microsoft account.

Usage no npm install needed!

<script type="module">
  import msmc from 'https://cdn.skypack.dev/msmc';
</script>

README

# MSMC MIT license Version Number Github Stars

A bare bones login library for Minecraft based projects to authenticate individuals with a Microsoft account.

Support

At the momement you can get support via Discord (link above).

Supported gui frameworks

If you're using MSMC with one of the frameworks below. The behaviour each framework displays should be identical. Mainly each will generate a pop-up that can be used by the end user to login. In-line logins, where the main window of your app is redirected to allow a user to log in should be implemented manually. Check the wiki page for more information! {Coming soon!}

"Auto"

This framework is not a framework. Instead it tells MSMC to attempt detecting the optimal framework it should be using itself. Still in testing.

...
const msmc = require("msmc");
msmc.fastLaunch("auto"...

"Raw"

This is not a framework as this method takes advantage off the chromium based web browsers a user will likely already have on their machine. Windows already ships with one, Microsoft edge, and you're in full control of you launcher’s dependencies on Linux.

Chromium based web browser that are compatible with this method: Google Chrome, Microsoft Edge, Vivaldi, Blisk, Brave Browser, Yandex (Only Mac/Linux), Chromium (Linux only) No additional dependencies needed!

Example

const msmc = require("msmc");
const fetch = require("node-fetch");
//msmc's testing enviroment sometimes runs into this issue that it can't load node fetch
msmc.setFetch(fetch)
msmc.fastLaunch("raw", 
(update) => {
     //A hook for catching loading bar events and errors, standard with MSMC
     console.log("CallBack!!!!!")
     console.log(update)
 }).then(result => {
     //If the login works
     if (msmc.errorCheck(result)){
         console.log("We failed to log someone in because : "+result.reason)
         return;
     }
     console.log("Player profile and mojang token : "+result);
 }).catch(reason=>{
     //If the login fails
     console.log("We failed to log someone in because : "+reason);
 })
1) Raw was tested running under pure node
2) Raw doesn't respect the "resizable" window property

NW.js

You should use the require method to load this module. Loading it by linking directly to the index.js file from the browser side of things may lead to unintended errors and glitches our testing did not account for.

Example

If you noticed that this and the raw gui framework has the same example bar the change of the word "raw" to "nwjs". That's more due to the new architecture in 2.2.0 allowing us to streamline documentation.

const msmc = require("msmc");
const fetch = require("node-fetch");
//msmc's testing enviroment sometimes runs into this issue that it can't load node fetch
msmc.setFetch(fetch)
msmc.fastLaunch("nwjs", 
 (update) => {
     //A hook for catching loading bar events and errors, standard with MSMC
     console.log("CallBack!!!!!")
     console.log(update)
 }).then(result => {
     //If the login works
     if (msmc.errorCheck(result)){
         console.log("We failed to log someone in because : "+result.reason)
         return;
     }
     console.log("Player profile and mojang token : "+result);
 }).catch(reason=>{
     //If the login fails
     console.log("We failed to log someone in because : "+reason);
 })

Electron

It is recommended to run the code for this library on the back-end side of electron. This is mainly due to security and the fast launch methods using functions and objects not available on the front end of electron.

Do note that some frameworks that use electron as a base might not have these issues, but it is still something you should consider if you're having issues.

Example

This is a code sample for electron should be added to your main.js file. NOTE: Only pass the profile object and the raw xprofile object you get back from the "getXbox" command included in the final object returned by msmc. Passing the entire object returned by msmc may cause issues.

app.whenReady().then(() => {
  ...
    require("msmc").fastLaunch("electron", 
        (update) => {
            //A hook for catching loading bar events and errors, standard with MSMC
            console.log("CallBack!!!!!")
            console.log(update)
        }).then(result => {
            //If the login works
             if (msmc.errorCheck(result)){
                console.log("We failed to log someone in because : "+result.reason)
                return;
            }
            console.log("Player profile and mojang token : "+result);
        }).catch(reason=>{
            //If the login fails
            console.log("We failed to log someone in because : "+reason);
        })
 ...
})

Examples

MCLC example

A basic example of how to hook this library into Mincraft Launcher core to launch Minecraft

const { Client, Authenticator } = require('minecraft-launcher-core');
const launcher = new Client();
const msmc = require("msmc");
const fetch = require("node-fetch");
//msmc's testing enviroment sometimes runs into this issue that it can't load node fetch
msmc.setFetch(fetch)
msmc.fastLaunch("raw",
    (update) => {
        //A hook for catching loading bar events and errors, standard with MSMC
        console.log("CallBack!!!!!")
        console.log(update)
    }).then(result => {
        //Let's check if we logged in?
        if (msmc.errorCheck(result)){
            console.log(result.reason)
            return;
        }
        //If the login works
        let opts = {
            clientPackage: null,
            // Pulled from the Minecraft Launcher core docs , this function is the star of the show
            authorization: msmc.getMCLC().getAuth(result),
            root: "./minecraft",
            version: {
                number: "1.17.1",
                type: "release"
            },
            memory: {
                max: "6G",
                min: "4G"
            }
        }
        console.log("Starting!")
        launcher.launch(opts);

        launcher.on('debug', (e) => console.log(e));
        launcher.on('data', (e) => console.log(e));
    }).catch(reason => {
        //If the login fails
        console.log("We failed to log someone in because : " + reason);
    })

Pure Node Example:

This is the set-up you'd use if you where only using node or an incompatible gui framework. Like writing a terminal based minecraft launcher!

const MSMC = require("msmc");
MSMC.login({ client_id: "<token>" }, 
    (link) => {
            //This is the link to the login page
        console.log("Click ME!!!!");
        console.log(link);
        },(update) => {
            //A hook for catching loading bar events and errors
            console.log("CallBack!!!!!");
            console.log(update);
        }
    ).then((result) => {
           //Let's check if we logged in?
        if (msmc.errorCheck(result)){
            //We had an error? Lets see what it was. 
            console.log("Failed!!!!!!!");
            console.log(result)
            return;
        }
        console.log("Logged in!!!!!!!");
        //Else, lets continue
        console.log(result)
    }).catch(reason=>{
        //If the login fails
        console.log("We failed to log someone in because : "+reason);
    })

Docs: Types

prompt

Basically this is the prompt value in the request sent to Microsoft. This should only be important if you're using either the fastLaunch or Launch functions under either Electron or NW.js

For more information. Check out Microsoft's support page:
type prompt = "login" | "none" | "consent" | "select_account";

mclcUser

A copy of the user object mclc uses

type mclcUser = {
    access_token: string;
    client_token?: string;
    uuid: string;

    name?: string;
    meta?: { 
        type: "mojang" | "xbox", 
        xuid?: string, 
        demo?: boolean 
    };
    user_properties?: Partial<any>;
}

framework

A list of gui frameworks supported by this library. Used by the launch and fastLaunch functions to figure out what functions they should target.

type framework = "electron" | "nwjs" | "raw";

ts

This is mostly used to aid translators. In theory one could create an add-on package that took in these codes and translated the given output accordingly.

export type ts =  "Login.Success.User" | "Login.Success.DemoUser" | "Login.Fail.Relog" | "Login.Fail.Xbox" | "Login.Fail.MC" | "Account.Unknown" | "Account.UserNotFound" | "Account.UserNotAdult" | "Cancelled.GUI" | "Cancelled.Back";
Value English translation
Login.Success.User Success
Login.Success.DemoUser You do not own Minecraft on this Microsoft Account
Login.Fail.Relog Please relog
Login.Fail.Xbox We failed to log you into your Xbox Account
Login.Fail.MC We failed to log you into Minecraft with the given Xbox Account
Account.Unknown We encountered an unknown error Attempting to get your Xbox One Security Token
Account.UserNotFound The given Microsoft Account is not connected to a given Xbox Account
Account.UserNotAdult According to Microsoft. You need to be an adult to log in from your current location.
Cancelled.GUI The user dismissed the Login popup without logging in.
Cancelled.Back The user dismissed the Login popup without logging in by clicking the back option or an error occured.

Docs: Interfaces

token

The Oauth2 details needed to log you in.

Resources

  1. https://docs.microsoft.com/en-us/graph/auth-register-app-v2
  2. https://docs.microsoft.com/en-us/graph/auth-v2-user#1-register-your-app
  3. https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps

Recommendations:

  1. If all of this seems confusing. Use the fastLaunch method. Since doing so will allow you to skip this mess!

  2. Use "Mobile and desktop applications" as your type setting and make sure to set it up to only use "Personal Microsoft accounts only". You're not a university!

  3. set the redirect to "http://localhost/...", With localhost specifically Microsoft does not check port numbers. This means that http://localhost:1/... to http://localhost:65535/... are all the same redirect to MS. (http://localhost/... == http://localhost:80/... btw)

  4. If you set the redirect to, for example, "http://localhost/Rainbow/Puppy/Unicorns/hl3/confirmed" then the variable {redirect} needs to equal "Rainbow/Puppy/Unicorns/hl3/confirmed".

  5. Basically the redirect field is equal to your redirect URL you gave Microsoft without the "http://localhost/" part. Please keep this in mind or you'll get weird errors as a mismatch here will still work...sort of.

This library does not allow you to set the port manually, due to the extreme risk of unforeseen bugs popping up.
interface token {
    client_id: string;
    clientSecret?: string;
    redirect?: string;
    prompt?: prompt;
}

profile

A version of a Minecraft profile you'd get from the auth end points

interface profile {
    id: string, name: string, skins?: [], capes?: [],xuid: string;
}

xprofile

This is an xbox account object. Used to allow for intergration with xbox related services and features. For example getting a user's xbox profile picture.

interface xprofile {
    /**The xuid that belongs to this user */
    xuid: string,
    /**The user's gamer tag */
    gamerTag: string,
    /**The user's username */
    name: string,
    /**The user's profile picture's url */
    profilePictureURL: string,
    /**The user's "Gamer score"*/
    score: string,
    /**The auth token you need for an "Authorization" header non of the ms docs tell you about, 
     * but which you absolutely need if you want to hit up any xbox live end points. 
     * 
     * I swear I will fork those fudging documents one of these days and make them a whole lot clearer then they are!!!! -Hanro50
     */
    getAuth?: () => string
}

result

The return object that all the async login procedures return

Possible values for the 'type' parameter in this interface:

Value Cause Other fields
Success The user has been logged in and it has been verified that the user owns a licence to the game access_token : string
profile : profile
getXbox : {function}
DemoUser The user has been logged in, but does not appear to own the game.
THIS IS ONLY MEANT TO BE USED FOR LAUNCHING THE GAME IN DEMO MODE
access_token : string
reason : string
getXbox : {function}
Authentication The user could not be logged into their Microsoft account reason : string
data : Response
Cancelled The user closed the login prompt (Gui based login flows) No added fields.
Unknown This unused at the moment No added fields.

The resulting typescript object.

    interface result {
        type: "Success" | "DemoUser" | "Authentication" | "Cancelled" | "Unknown"
        /**Your classic Mojang auth token.*/
        "access_token"?: string, 
        /**Player profile. Similar to the one you'd normally get with the Mojang login*/
        profile?: profile,
        /**Used with the error types */
        reason?: string,
        /**Used to make translation easier */
        translationString?: ts,
        /**Used when there was a fetch rejection.*/
        data?: Response,
        /**Get Xbox profile of user */
        getXbox?: (updates?: (info: update) => void) => Promise<xprofile>;
    }

update

Used in the callback that is fired multiple times during the login process to give the user feedback on how far along the login process is

interface update {
    type:  "Starting" | "Loading" | "Error"; //See table below!
    /**Some information about the call. Like the component that's loading or the cause of the error. */
    data?: string;
    /**Used by the Error type.*/
    error?: result;
    /**Used to show how far along the object is in terms of loading*/
    percent?: number;
}

Possible values for the 'type' parameter:

Value Cause
"Starting" This is fired once when the whole loading process is started. This is mainly for setting up loading bars and stuff like that.
"Loading" This gives input with regards to how far along the login process is.
"Error" This is given with a normal MC account error and will give you some user readable feedback.
[Only thrown with the versions of the auth functions returned by the "getCallback()" wrapper]

windowProperties

Window properties is set to any to avoid needing both nw.js and electron loaded as developer dependencies
The common properties between both libraries has been backed into the type information for this interface however

See this resource for Electron, if you want to know what options are available

See this resource for NW.js, if you want to know the available options

interface windowProperties {
    width: number;
    height: number;
    resizable?: boolean; 
    [key: string]: any;
}

Docs: Functions

setFetch

An override to manually define which version of fetch should be used

fetchIn => A version of fetch

function setFetch(fetchIn: any): void;

mojangAuthToken <Advance users only>

Gets a premade token with mojang's auth token already set up .

prompt => See the type definition for "prompt" for more information

This token is owned and controlled by Mojang. Using it for anything other then Minecraft might get you an angry email from a lawyer eventually.
export declare function mojangAuthToken(prompt: prompt): token;

createLink

This function will create a login link based on the inputs provided.

Note that this function is called internally after the redirect has been formatted. Aka after "http://localhost:<port>/" is appended to the redirect.

This is done to allow us to create the "fastLaunch" methods which don't rely on an internal http server

token => Your MS Login token. Mainly your client ID, client secret (optional | Depends how azure is set up) and a redirect;
returns => A link you can plug into a web browser to send a user to a ms login page

function createLink(token: token): String;

authenticate <Advance users only>

Used when you want to implement your own login code, but still want msmc to handle authentication for you.

code => The code gotten from a successful login
MStoken => The Microsoft token object used to obtain the login code
updates => A callback that one can hook into to get updates on the login process
returns => A promise that will grant you a user profile and Mojang login token

function authenticate(code: string, MStoken: token, updates?: (info: update) => void): Promise<result>;

refresh

Used to refresh login tokens. It is recommended to do this at start up after calling validate to check if the client token needs a refresh

profile => Player profile. Similar to the one you'd normally get with the Mojang login
updates => A callback that one can hook into to get updates on the login process
MStoken => Microsoft token object used to obtain the login code (Optional, will use the vanilla client token if it doesn't have anything)
returns => A promise that will grant you an updated user profile and Mojang login token

function refresh(profile: profile, updates?: (info: update) => void, MStoken?: token): Promise<result>;

validate

Checks if a profile object is still valid

profile => Player profile. Similar to the one you'd normally get with the Mojang login
return => Returns a boolean stating whether a set account is still valid

 function validate(profile: profile): Boolean;

login

A generic login method. Useful if you aren't using electron or NW.js and want to make a terminal launcher or are using an unsupported framework

token => Your MS Login token. Mainly your client ID, client secret (optional | Depends how azure is set up) and a redirect (Do not include http://localhost:<port>/ as that's added for you!)
getlink => The callback will give you a link you can redirect a user to.
updates => A callback that one can hook into to get updates on the login process
returns => A promise that will grant you a user profile and Mojang login token

 function login(token: token, getlink: (info: string) => void, updates?: (info: update) => void): Promise<result>;

launch

Used with electron or nwjs to launch a popup that a user can use to sign in with

type => The GUI framework this is compatible with
token => Your MS Login token. Mainly your client ID, client secret
updates => A callback that one can hook into to get updates on the login process
properties => See windowProperties interface for more information
returns => A promise that will grant you a user profile and mojang login token

 function launch(type: framework, token: token, updates?: (info: update) => void, properties?: windowProperties): Promise<result>;

fastLaunch

Mimics the vanilla launcher in how it works. Like launch in creates a pop-up a user can log in with

type => The GUI framework this is compatible with
updates => A callback that one can hook into to get updates on the login process
prompt => See the type definition for "prompt" for more information
properties => See windowProperties interface for more information
returns => A promise that will grant you a user profile and mojang login token

function fastLaunch(type: framework, updates?: (info: update) => void, prompt?: prompt, properties?: windowProperties): Promise<result>;

getMCLC

Replaces some of the functions the Authenticator component in MCLC.

getAuth

This serves as a MSMC friendly version of getAuth function in MCLC's Authenticator component. Translating the information MSMC gets into something mclc can comprehend. This does however not work with normal Mojang accounts

validate

This serves as a drop in replacement for the validate function in MCLC's Authenticator component. This works with Mojang and Microsoft accounts.

refresh

This serves as a drop in replacement for the refreshAuth function in MCLC's Authenticator component. This will refresh vanilla and MSMC accounts. A hidden _msmc variable is used to determine how an account should be refreshed so please avoid removing that somehow since the Mojang method of refreshing accounts is not compatible with MSMC signed in accounts.

toProfile <Advance users only>

This converts mclc profile objects back into msmc ones. It should be noted that mclc profile objects created with mclc's native authenticator module should not be passed to this function as it will likely result in an error upon converting them back to mclc profile objects.

export declare function getMCLC(): {
    getAuth: (info: result) => mclcUser
    validate: (profile: mclcUser) => Promise<Boolean>
    refresh: (profile: mclcUser, updates?: (info: update) => void, MStoken?: token) => Promise<mclcUser>
    toProfile: (profile: mclcUser) => profile
}

errorCheck

Checks if a return value is valid and if the login procedure has been successful

Demo accounts will cause this function to return false.
function errorCheck(result: result): Boolean;

isDemoUser

Checks if a player object was created with a demo account. Useful for if you're using msmc without mclc and still want to implement demo account support.

This function shouldn't be needed with MCLC as the profile object MSMC creates for MCLC contains some meta data that tells MCLC to launch the game in demo mode.
function isDemoUser(profile: profile | result): Boolean;

getExceptional

Wraps the following functions and causes each to throw a result object as an error on a failed login instead of passing back said result object

See the docs for the normal async versions of these functions for a better idea of what they do
function getExceptional(): {
    authenticate: (code: string, MStoken: token, updates?: (info: update) => void) => Promise<result>
    refresh: (profile: profile, updates?: (info: update) => void, MStoken?: token) => Promise<result>
    login: (token: token, getlink: (info: string) => void, updates?: (info: update) => void) => Promise<result>
    launch: (type: framework, token: token, updates?: (info: update) => void, properties?: windowProperties) => Promise<result>
    fastLaunch: (type: framework, updates?: (info: update) => void, prompt?: prompt, properties?: windowProperties) => Promise<result>
}

getCallback

Wraps the following functions and presents them in a similar style to the old 2.1.x series of msmc. In that you must provide the set of functions a callback function as a variable.

Also errors are sent to the update funtion that you provide similarly to the 2.1.x series of msmc.

See the docs for the normal async versions of these functions for a better idea of what they do
export declare function getCallback(): {
    authenticate: (callback: (r: result) => void, code: string, MStoken: token, updates?: (info: update) => void) => void
    refresh: (callback: (r: result) => void, profile: profile, updates?: (info: update) => void, MStoken?: token) => void
    login: (callback: (r: result) => void, token: token, getlink: (info: string) => void, updates?: (info: update) => void) => void
    launch: (callback: (r: result) => void, type: framework, token: token, updates?: (info: update) => void, properties?: windowProperties) => void
    fastLaunch: (callback: (r: result) => void, type: framework, updates?: (info: update) => void, prompt?: prompt, properties?: windowProperties) => void
}

NWJS

If you're already using this GUI framework. A variant of Fetch is already available in global scope and will thus be selected by msmc automatically. You do not need to manually add fetch.

Electron

Electron does not provide an implementation of fetch to the node backend. It is recommended not to use the implementation of fetch provided by the frontend. This is mostly due to potential issues with cores you'll have if you ignore this warning.

Recommended

Two fetch implementations msmc is tested against is node-fetch and electron-fetch. If either are present then MSMC will pull them down automatically. If you however want to specify an implementation of fetch msmc should use. Please see the setFetch function for more information!.

Final notes

This module includes a ES6 layer.

Credit

Based off the Oauth2 flow outline on this site

Please report any type file bugs asap