diff --git a/.gitignore b/.gitignore index e90c4dd..86841fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ package-lock.json - +dist +types node_modules -bin \ No newline at end of file +bin diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8b04d52 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/dist/tests.js", + "outFiles": [ + "${workspaceFolder}/dest/**/*.js" + ] + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index d57bec9..218f5e5 100644 --- a/README.md +++ b/README.md @@ -7,681 +7,431 @@ # Support
- - chat on Discord + +
+At the moment you can get support via Discord (link above). - 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. -###### Due to the risk of badly behaving code. This framework is marked for removal -```js -... -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 - -```js -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. - ```js -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. -```js -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); - }) - ... -}) -``` +

UNDER CONSTRUCTION; 4.0.0 is still in an alpha state!

# Examples - -## MCLC example - -> A basic example of how to hook this library into Mincraft Launcher core to launch Minecraft - +These are in an unfinished state +## A basic ES6 example with [MCLC](https://github.com/Pierce01/MinecraftLauncher-core) ```js -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" - } +import msmc, { wrapError } from "msmc"; +import { Client, Authenticator } from "minecraft-launcher-core"; +const launcher = new Client();//We're simple setting up mclc here... +const auth = new msmc.auth(); //Spawn a new auth object using mojang's token + + +auth.on('load', console.log) //read load events into the console +try{ + const xbx = await auth.luanch('raw')//In the example we use raw, but you can replace the word raw with electron or nwjs if you're using either of the two + const mc = await xbx.getMinecraft()//Lets get the information we need to launch minecraft + + // Pulled from the Minecraft Launcher core docs. + let opts = { + clientPackage: null, + // Simply call this function to convert the msmc minecraft object into a mclc authorization object + authorization: mc.mclc(), + root: "./minecraft", + version: { + number: "1.18.2", + type: "release" + }, + memory: { + max: "6G", + min: "4G" } - console.log("Starting!") - launcher.launch(opts); + } + 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); - }) + launcher.on('debug', (e) => console.log(e)); + launcher.on('data', (e) => console.log(e)); +}catch(e){ + console.log(wrapError(e)) // The wrap error function is here to convert an msmc error into something we can decode. +} ``` -## 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! -```js -const MSMC = require("msmc"); -MSMC.login({ client_id: "" }, - (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); - }) -``` -## inline login \ -> Potentially usefull when you want to use MSMC for something beyond launching minecraft or are simply seeking to implement an inline login method. -> -> Whatever the case, this is usefull if you solely want to use MSMC as a specialised oauth2 client for whatever reason. Do note that the vanilla token will likely no longer work as you will likely need to define a custom redirect URL. -> -> Due to this being for advance users only. Please note that the example here will not produce working code if copied and pasted directly. -```js -const msmc = require("msmc"); -const token = { - client_id: "Your clientID from your token here", - clientSecret: " Your secret, typically needed for websites", - redirect: "The redirect back to your website", - prompt: "login" | "none" | "consent" | "select_account" + +# Modules +## auth +This module is the starting point of msmc. It will be the first msmc object you create. It is also the object that'll handle all of msmc's events for you. Mainly the load event. +```ts +class auth extends EventEmitter { + token: MStoken; + constructor(prompt?: prompt); + constructor(token: MStoken); + createLink(): string; + login(code: string): Promise; + refresh(MS: msAuthToken): Promise; + refresh(refreshToken: string): Promise; + luanch(framework: framework, windowProperties?: windowProperties): Promise; + server(port?: number): Promise; + + on(event: "load", listener: (asset: lexcodes, message: string) => void): this; + once(event: "load", listener: (asset: lexcodes, message: string) => void): this; } -const link = msmc.createLink(token); -... -//Handle redirecting the client to the link provided by link variable here and the callback to get the data returned by the get request microsoft sent back to you here. -... -const code = "The code returned by that afformentioned get request"; -//We're going to leave out the update variable here. See the entry for this function for more information. -msmc.authenticate(code,token).then(result=>{ - //Handle the result object as you would from any other login callback msmc uses -}); ``` -###### Recommend spinning up an HTTP/HTTPS server for this. +>### `constructor(prompt?: prompt)` +This version of the constructor will generate an auth object with the vanilla Minecraft launcher token. The prompt variable is a string that provides the prompt field in the vanilla token as that is not provided by default. -# 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: ```ts type prompt = "login" | "none" | "consent" | "select_account"; ``` - -## mclcUser -> A copy of the user object mclc uses +To learn more about the prompt type, check out Microsoft's support page. It will provide more details for the possible value of this field. +>### `constructor(token: MStoken)` \ +This version of the constructor is for use with custom Microsoft tokens. ```ts -type mclcUser = { - access_token: string; - client_token?: string; - uuid: string; - - name?: string; - meta?: { - type: "mojang" | "xbox", - xuid?: string, - demo?: boolean - }; - user_properties?: Partial; +interface MStoken { + client_id: string, + redirect: string, + clientSecret?: string, + prompt?: prompt } ``` -## 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. +The Oauth2 token details needed for you to log people in with Microsoft's service. + +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 +>### `createLink(): string` \ +Creates a login link using the given Token. Can be used if you're forgoing msmc's default gui based login flow for something custom. In essence you should use this link as a redirect, then capture the returning code url-parameter and feed it into the login function +>### `login(code: string): Promise` \ +The low level login function msmc uses. The returning promise is for the next stage in the login chain. Mainly the xbox module. I'd refer you to the next module to learn more! + +returns an instance of the [xbox](#xbox) module or throws an error +>### `refresh(MS: msAuthToken): Promise` +The low level refresh function msmc uses. This will attempt to refresh the Microsoft token at the core of msmc and return an xbox object as a result. Please see the msToken variable under the xbox module. ```ts -type framework = "electron" | "nwjs" | "raw"; +interface msAuthToken { + token_type: string, + expires_in: number, + scope: string, + access_token: string, + refresh_token: string, + user_id: string, + foci: string +} ``` +The 'refresh_token' and the 'access_token' are the only two fields of note to this project. -## 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. -```ts -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"; -``` +returns an instance of the [xbox](#xbox) module or throws an error + +>### `refresh(refreshToken: string): Promise` +Refreshes a user solely based on the refresh token of a set user's refresh_token. See the [save](#save-string) function in the [xbox](#xbox) for more information. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ValueEnglish translation
Login.Success.UserSuccess
Login.Success.DemoUserYou do not own Minecraft on this Microsoft Account
Login.Fail.RelogPlease relog
Login.Fail.XboxWe failed to log you into your Xbox Account
Login.Fail.MCWe failed to log you into Minecraft with the given Xbox Account
Account.UnknownWe encountered an unknown error Attempting to get your Xbox One Security Token
Account.UserNotFoundThe given Microsoft Account is not connected to a given Xbox Account
Account.BannedCountryThe login is from an account that hails from a country where Xbox Live is not available
Account.ChildInSouthKoreaThe account needs adult verification on Xbox page. (South Korea)
Account.UserNotAdultAccording to Microsoft. You need to be an adult to log in from your current location.
Cancelled.GUIThe user dismissed the Login popup without logging in.
Cancelled.BackThe 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. +returns an instance of the [xbox](#xbox) module or throws an error + +>### ` luanch(framework: framework, windowProperties?: windowProperties): Promise` +Launches a pop-up window prompting the user to login to their Microsoft account. ```ts -interface token { - client_id: string; - clientSecret?: string; - redirect?: string; - prompt?: prompt; -} +type framework = "electron" | "nwjs" | "raw"; ``` -## profile -> A version of a Minecraft profile you'd get from the auth end points +The supported frameworks are electron, nwjs and raw. + ```ts -interface profile { - id: string, name: string, skins?: [], capes?: [],xuid: string; +interface windowProperties { + width: number, + height: number, + /**Raw ignores this property!*/ + resizable?: boolean, + /**Raw only: Stops MSMC from passing through the browser console log*/ + suppress?: boolean, + [key: string]: any } ``` -## 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. +This is the properties msmc passes through to the function of a set framework that spawns a pop-up. For more information of which properties are available depending on your preferred GUI framework of choice. Click here for nwjs and here for electron. The raw framework only uses the properties "width","height" and "suppress" +returns an instance of the [xbox](#xbox) module or throws an error + +> ### `server(port?: number): Promise` \ +WIP, not implemented yet + +returns an instance of the [xbox](#xbox) module or throws an error +> ### `on(event: "load", listener: (asset: lexcodes, message: string) => void): this` +Event handler. Fires on a load event. Can be used for loading indicators similar to the update function in previous versions on msmc. +> ### `once(event: "load", listener: (asset: lexcodes, message: string) => void): this` +The same as the "on" function, but only fires once. +
+ +## xbox +The second stage of the authentication phase. In this phase the user has been logged in with their Microsoft account, but they haven't been logged into Minecraft nor have they been authenticated to the degree needed to access social features yet. This being said, you now potentially have the ability to do both if you have made it this far. + +Please see [auth](#auth) module for more information on how to spawn this object ```ts -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, - /**Gets a user's friend list */ - getFriends?: () => promise, - /**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 +class xbox { + readonly parent: auth; + readonly msToken: msAuthToken; + readonly xblToken: xblAuthToken; + readonly exp: number; + + xAuth(RelyingParty?: string): Promise; + refresh(force?: boolean): Promise; + getSocial(): Promise; + getMinecraft(): Promise; + validate(): boolean; + save(): string; } ``` - -## result ->The return object that all the async login procedures return
- ->Possible values for the 'type' parameter in this interface: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ValueCauseOther fields
SuccessThe 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} -
DemoUserThe 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} -
AuthenticationThe user could not be logged into their Microsoft account - reason : string
- data : Response -
CancelledThe user closed the login prompt (Gui based login flows) - No added fields. -
UnknownThis unused at the moment - No added fields. -
- -> The resulting typescript object.
- +> ### properties \ ```ts - 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; - } +parent: auth; ``` -## 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 - +The auth object that was used to create this Xbox object. ```ts -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; +interface msAuthToken { + token_type: string; + expires_in: number; + scope: string; + access_token: string; + refresh_token: string; + user_id: string; + foci: string; } +msToken: msAuthToken; ``` - -> Possible values for the 'type' parameter: - - - - - - - - - - - - - - - - - - -
ValueCause
"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
- +This is the token that was generated when the user was logged into their Microsoft account. ```ts -interface windowProperties { - width: number; - height: number; - resizable?: boolean; - [key: string]: any; +interface xblAuthToken { + IssueInstant: string; + NotAfter: string; + Token: string; + DisplayClaims: { + xui: [{ + uhs: string; + }]; + }; } + +xblToken: xblAuthToken; ``` -# Docs: Functions -## setFetch -> An override to manually define which version of fetch should be used
+This is the token that was generated when msmc authenticated against a user's microsoft account to gain xbox live authentication access. -fetchIn => A version of fetch ```ts -function setFetch(fetchIn: any): void; +exp: number; ``` -## mojangAuthToken \ -> 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. - ```ts -export declare function mojangAuthToken(prompt: prompt): token; +The time in milliseconds the provided tokens are valid till. If this date is surpassed it can be assumed the provided tokens need to be refreshed. + +> ### `xAuth(RelyingParty?: string): Promise` \ +Retrieves the auth header for a set replying party. In theory it could be expanded to be used with more services that work with Xbox live endpoints. Right now this is mainly an internal function that happens to be exposed +> ### `refresh(force?: boolean): Promise` +Refreshes the tokens of this Xbox object. This function should be called once per hour. This being said, msmc does check if the tokens need to be refreshed before refreshing them. The exception being when the "force" property is set to true. In that case it will force all tokens to be refreshed. +> ### `getSocial(): Promise` +Gets an instance of the [social](#social) module. Can be used to implement friend lists. +> ### `getMinecraft(): Promise` +Gets an instance of the [Minecraft](#minecraft) module. Can be used to obtain the needed information to launch Minecraft. + +NB: There is an additional internal check that will be done to determine if a user got Minecraft Java edition via game pass. This is experimental at the moment so please report any issues you observe with this. It may also not function with older versions of the game just yet. +> ### `validate(): boolean` +Checks if the internal tokens in this object are still valid and usable. If this returns false then it is a good idea to call the [refresh](#refreshforce-boolean-promisethis) function listed earlier (This also gets called by that funtion btw). +> ### `save(): string` +Returns a token that can be fed into a [refresh](#refreshrefreshtoken-string-promisexbox) function in an instance of the auth module. Useful if you want to save the information needed to recreate a set Xbox object to file. +
+ +## minecraft +The module needed to obtain the information required to launch Minecraft. +```ts +minecraft { + mcToken: string; + profile: mcProfile; + parent: xbox; + xuid: string; + + entitlements(): Promise; + isDemo(): boolean; + mclc(): mclcUser; + refresh(force?: boolean): Promise; + validate(): boolean; +} ``` -## 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:\/" 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 +> ### properties \ ```ts -function createLink(token: token): String; +mcToken: string; ``` -## authenticate \ -> 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
+The minecraft authentication token. This is needed to launch the game in online mode ```ts -function authenticate(code: string, MStoken: token, updates?: (info: update) => void): Promise; +interface mcProfile { + id: string, + name: string, + skins: Array<{ + id: string, + state: 'ACTIVE', + url: string, + variant: 'SLIM' | 'CLASSIC' + }>, + capes: Array<{ + id: string, + state: 'ACTIVE', + url: string, + alias: string + }>, + demo?: boolean +} + +profile: mcProfile; ``` -## 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
+The raw Minecraft profile object. Do note that the skins and capes arrays may be empty. Generally this contains everything you need to launch Minecraft. -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
```ts -function refresh(profile: profile, updates?: (info: update) => void, MStoken?: token): Promise; +parent: xbox; ``` -## 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
+The [xbox](#xbox) object that spawned this instance of the Minecraft module. ```ts - function validate(profile: profile): Boolean; +xuid: string; ``` -## 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:/ 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
+The xuid of the Xbox user id of the logged in user. +> ### `entitlements(): Promise` +Generates a list of mojang products/games a set user owns. ```ts - function login(token: token, getlink: (info: string) => void, updates?: (info: update) => void): Promise; +type entitlements = "game_minecraft" | "game_minecraft_bedrock" | "game_dungeons" | "product_minecraft" | "product_minecraft_bedrock" | "product_dungeons"; ``` -## 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
+> ### `mclc(): mclcUser` +Creates a Mincraft Launcher core user object. Usefull if you wish to use msmc with that library. +> ### `refresh(force?: boolean): Promise` +Refreshes the minecraft and xbox tokens. Like the [refresh](#refreshforce-boolean-promisethis) function in the [xbox](#xbox) module. +> ### `validate(): boolean` +Like the Like the [validate](#validate-boolean) function in the [xbox](#xbox) module, but just for the Minecraft token. Which remains valid for 24 hours +
+ +## social +The social module is unique. Partly because it is expected to be ran server side in some settings. This is done to allow for features such as allowing you to create screens where players may be able to see which of their friends are online and such. + +If you have deeper access to the game, such as in the scenario where your launcher is a front end for some type of custom client. Then I foresee the possibility of even implementing a kind of "click to join" function. In the end this is merely here to serve as a bases for something substantially more complex that is beyond the scope of msmc. + ```ts - function launch(type: framework, token: token, updates?: (info: update) => void, properties?: windowProperties): Promise; +class social { + auth: string; + constructor(auth: string); + getProfile(xuid?: string): Promise; + getFriends(xuid?: string): Promise; + xGet(enpoint: string, xuid?: string): Promise; +} ``` -## 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
+> ### `class xplayer` ```ts -function fastLaunch(type: framework, updates?: (info: update) => void, prompt?: prompt, properties?: windowProperties): Promise; +class xplayer { + auth: social; + score: number; + xuid: string; + gamerTag: string; + name: string; + profilePictureURL: string; + getFriends(): Promise; +} ``` -## getMCLC +auth=> The instance of the [social](#social) module that spawned this xplayer object
+score=> The user's player score....not sure what it does, but the endpoint provides it
+xuid=>The xbox user id of the player this instance of xplayer represents.
+gamerTag=>The gamer tag of the user this instance of xplayer represents.
+name=>The name of the user this instance of xplayer represents.
+profilePictureURL=>The profile picture url of the user this instance of xplayer represents.
+getFriends=>This function returns a list of xplayer modules that represents everyone on a give user's friend list. -> Replaces some of the functions the Authenticator component in MCLC. +> ### `constructor(auth: string)` \ +The auth header is needed to use the underlying endpoints that make this function. To get this header, run [xAuth](#xauthrelyingparty-string-promisestring-advanced) function in the [xbox](#xbox) module. This header can potentially be sent as an authentication string for an endpoint of your launcher's back end server. -### getAuth +> ### `getProfile(xuid?: string): Promise` +Gets the user profile of a given user. If the xuid field is missing it will return the profile of the user the auth header belongs to. -> 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 +> ### `getFriends(xuid?: string): Promise` +Gets the friend list of a given user. If the xuid field is missing it will return the friend list of the user the auth header belongs to. -### validate +> ### `xGet(enpoint: string, xuid?: string): Promise` \ +The raw back end function used to obtain information related to a given user based on the xuid provided. If a xuid is not provided the information returned will instead be based on the profile the auth header belongs to. +
-> This serves as a drop in replacement for the validate function in MCLC's Authenticator component. This works with Mojang and Microsoft accounts. +## assets +A collection of helper functions to aid in using msmc. -### 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. +> ### Languages and you +See our premade [lexipacks here](/lexipacks) and see the [loadLexiPack](#function-loadlexipackfile-string-typeof-lexicon) function for more information on how to load it. -### toProfile \ -> 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. +I've noticed that a fair amount of the people in the mcjs café discord tend to maintain launchers that by default aren't set to English. While older versions of msmc made efforts to address this. I'm happy to announce that we switched over to a solution that isn't just hacked onto existing code this time. +Introducing the lexicon property. By overriding this object with your own code you can effectively localize MSMC without essentially needing to hunt for every English piece of dialogue. Potential msmc language packs will only need to override this one property to translate the entirety of msmc's errors and load events to another language. + +#### `lexicon` ```ts -export declare function getMCLC(): { - getAuth: (info: result) => mclcUser - validate: (profile: mclcUser) => Promise - refresh: (profile: mclcUser, updates?: (info: update) => void, MStoken?: token) => Promise - toProfile: (profile: mclcUser) => profile +export let lexicon = { + //Error + "error": "An unknown error has occured", + "error.auth": "An unknown authentication error has occured", + "error.auth.microsoft": "Failed to login to Microsoft account", + "error.auth.xboxLive": "Failed to login to Xbox Live", + "error.auth.xsts": "Unknown error occured when attempting to optain an Xbox Live Security Token", + "error.auth.xsts.userNotFound": "The given Microsoft account doesn't have an Xbox account", + "error.auth.xsts.bannedCountry": "The given Microsoft account is from a country where Xbox live is not available", + "error.auth.xsts.child": "The account is a child (under 18) and cannot proceed unless the account is added to a Family account by an adult", + "error.auth.xsts.child.SK": "South Korean law: Go to the Xbox page and grant parental rights to continue logging in.", + + "error.auth.minecraft": "Unknown error occured when attempting to login to Minecraft", + "error.auth.minecraft.login": "Failed to authenticate with Mojang with given Xbox account", + "error.auth.minecraft.profile": "Failed to fetch minecraft profile", + "error.auth.minecraft.entitlements": "Failed to fetch player entitlements", + + "error.gui": "An unknown gui framework error has occured", + "error.gui.closed": "Gui closed by user", + "error.gui.raw.noBrowser": "no chromium browser was set, cannot continue!", + + "error.state.invalid": "[Internal]: Method not implemented.", + "error.state.invalid.gui": "[Internal]: Invalid gui framework.", + "error.state.invalid.redirect": "[Internal]: The token must have a redirect starting with 'http://localhost/' for this function to work!", + "error.state.invalid.electron": "[Internal]: It seems you're attempting to load electron on the frontend. A critical function is missing!", + //Load events + "load": "Generic load event", + "load.auth": "Generic authentication load event", + "load.auth.microsoft": "Logging into Microsoft account", + "load.auth.xboxLive": "Logging into Xbox Live", + "load.auth.xboxLive.1": "Logging into Xbox Live", + "load.auth.xboxLive.2": "Authenticating with xbox live", + "load.auth.xsts": "Generating Xbox Live Security Token", + + "load.auth.minecraft": "Generic Minecraft login flow event", + "load.auth.minecraft.login": "Authenticating with Mojang's servers", + "load.auth.minecraft.profile": "Fetching player profile", + "load.auth.minecraft.gamepass": "[experimental!] Checking if a user has gamepass" } ``` -## 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. -```ts -function errorCheck(result: result): Boolean; -``` +A note on implementation. If msmc updates and a new event gets added, say `load.auth.example.new`. If your translation supports `load.auth.example`, msmc will proceed to use the translation text you provided for that code if `load.auth.example.new` is not available. This is why `load` and `load.auth` still have translations provided even if they're not called directly by msmc. They're in essence fall backs. -## 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. +Note: if you want to translate the read me into another language. Then hit me up on the discord! +#### `lst(lexcodes: lexcodes): any` ```ts -function isDemoUser(profile: profile | result): Boolean; +function lst(lexcodes: lexcodes): any; ``` +This function will translate lexcodes into readable text based on the [lexicon](#lexicon) object. -## 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 -```ts -function getExceptional(): { - authenticate: (code: string, MStoken: token, updates?: (info: update) => void) => Promise - refresh: (profile: profile, updates?: (info: update) => void, MStoken?: token) => Promise - login: (token: token, getlink: (info: string) => void, updates?: (info: update) => void) => Promise - launch: (type: framework, token: token, updates?: (info: update) => void, properties?: windowProperties) => Promise - fastLaunch: (type: framework, updates?: (info: update) => void, prompt?: prompt, properties?: windowProperties) => Promise -} +#### `function loadLexiPack(...file: string[]): typeof lexicon;` +Loads a set lexipack and returns it when it finishes loading it. + +Usage: +```js +import {assets} from "msmc"; +assets.loadLexiPack(path,to,lexipack,here); ``` -## 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 +> ### Error handling +Handling errors in msmc changed a little. Since we moved back to a throw on error model last seen when we moved to an async architecture. The issue of error typing has propped up again. If an msmc object throws an error. It will be in one of two formats. ```ts -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 -} +interface response{ response: Response, ts: lexcodes } ``` -### 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 +This is thrown when a fetch request errors out. The ts object will be the raw lexcode. The [lst](#lstlexcodes-lexcodes-any) function can translate the lexcodes into readable text for you. The response object is the response from the fetch object that caused the exception + +Otherwise only the raw lexcode will be thrown. You can use [lst](#lstlexcodes-lexcodes-any) to translate if for you, but msmc already ships with a function to handle all this for you. +```ts +function wrapError(code: string | exptOpts | any): { + name: lexcodes; + opt?: { + response: Response; + }; + message: any; +}; +``` +This function will take errors thrown by msmc and wrap them up for you. The message will be the translated cause of the error. The opt field will contain the response object if the error was caused by a fetch operation and the name is the standard lexcode if you want to do some processing based on the lexcode of the error. diff --git a/build.js b/build.js new file mode 100755 index 0000000..1f1ed4f --- /dev/null +++ b/build.js @@ -0,0 +1,16 @@ +#!/bin/node + +const { execSync } = require("child_process"); +const { rmSync } = require("fs"); + +rmSync('dist', { recursive: true, force: true }); +rmSync('types', { recursive: true, force: true }); + +try { + console.log("[MSMC]:Compiling....") + console.log(execSync('tsc -b ./tsconfig.dist.json').toString('ascii')) + console.log("[MSMC]:Constructing declarations....") + console.log(execSync('tsc -b ./tsconfig.types.json').toString('ascii')) +} catch (e) { + console.log(e.toString('ascii')) +} \ No newline at end of file diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 16df073..0000000 --- a/index.d.ts +++ /dev/null @@ -1,305 +0,0 @@ -/** - * For more information. Check out Microsoft's support page: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code
- * - * 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 - */ -export type prompt = "login" | "none" | "consent" | "select_account"; -/** - * This library's supported gui frameworks. - * (Raw requires no extra dependencies, use it if you're using some unknown framework!) - */ -export type framework = "electron" | "nwjs" | "raw"; - -/** - * Here for translators. - */ -export type ts = "Login.Success.DemoUser" | "Login.Success.User" | "Login.Fail.MS" | "Login.Fail.Relog" | "Login.Fail.Xbox" | "Login.Fail.MC" | "Account.Unknown" | "Account.UserNotFound" | "Account.BannedCountry" | "Account.ChildInSouthKorea" | "Account.UserNotAdult" | "Cancelled.GUI" | "Cancelled.Back"; -/** - * 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) 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! - * - * 2) 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) - * This library does not allow you to set the port manually, due to the extreme risk of unforeseen bugs popping up. - * - * 3) 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". - * - * 4) 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. - */ -export interface token { - client_id: string, - clientSecret?: string, - redirect?: string, - prompt?: prompt -} - -/**A version of a Minecraft profile you'd get from the auth end points */ -export interface profile { - id: string, name: string, skins?: [], capes?: [], xuid: string -} -export 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, - /**Gets a user's friend list */ - getFriends?: () => Promise; - /**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 -} - -/**The return object that all the async login procedures return */ -export interface result { - type: "Success" | "DemoUser" | "Authentication" | "Cancelled" | "Unknown" - /**Only returned when the user has logged in via microsoft */ - "access_token"?: string, //Your classic Mojang auth token. - /**Only returned on a successful login and if the player owns the game*/ - profile?: profile, //Player profile. Similar to the one you'd normally get with the Mojang login - /**Used with the error types*/ - reason?: string, - /**Used when there was a fetch rejection.*/ - data?: Response, - /**Used to make translation easier */ - translationString?: ts, - /**Get Xbox profile of user */ - getXbox?: (updates?: (info: update) => void) => Promise; -} - -/**The object returned to give you information about how the login process is progressing */ -export interface update { - /** - * 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: TThis 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. - */ - type: "Starting" | "Loading" | "Error", - /**Used by the loading call to inform you about the asset being loaded */ - data?: string, - /**Used to show how far along the object is in terms of loading*/ - percent?: number - /**Used by the Error type.*/ - error?: result, - -} -/** - * Used by graphical Electron and NW.js integrations to set the properties of the generated pop-up - */ -export interface windowProperties { - width: number, - height: number, - /**Raw ignores this property!*/ - resizable?: boolean, - /**Raw only: Stops MSMC from passing through the browser console log*/ - suppress?: boolean, - [key: string]: any -} - -/** - * An override to manually define which version of fetch should be used - * @param fetchIn A version of fetch - * @deprecated No longer needed due to msmc always defaulting to node-fetch. - */ -export declare function setFetch(fetchIn: any): void; -/** - * Gets a premade token with mojang's auth. - * @param prompt See the type definition for "prompt" for more information - */ -export declare function mojangAuthToken(prompt: prompt): token; -/** - * Gets the friendlist from a set xprofile of a set user. - * @param profile The xprofile of the user you want to get the friendlist from - * @deprecated Function has been moved! Check the xbox module. Will be removed in 3.2.0 - */ -export declare function getFriendlist(profile: xprofile): Promise; -/** - * Gets the friendlist from a set user based on thier xbox id - * @param auth The auth header needed to use the endpoint - * @param xuid The user's xbox ID - * @deprecated Function has been moved! Check the xbox module. Will be removed in 3.2.0 - */ -export declare function getFriendlist(auth: string | (() => string), xuid?: string): Promise; - - -/** - * 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:\/" 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
- * @param 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 -*/ -export declare function createLink(token: token): String; - -/** - * This function will create a login link based on the inputs provided.
- * This method assumes you're planning on using Mojang's endpoints - * @param prompt See the type definition for "prompt" for more information - * @returns A link you can plug into a web browser to send a user to a ms login page - */ -export declare function createLink(prompt: prompt): String; - -/** - * Used when you want to implement your own login code, but still want MSMC to handle authentication for you. - * @param code The code gotten from a successful login - * @param MStoken The Microsoft token object used to obtain the login code - * @param 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 - */ -export declare function authenticate(code: string, MStoken: token, updates?: (info: update) => void): Promise; - -/** - * 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 - * @param profile Player profile. Similar to the one you'd normally get with the Mojang login - * @param updates A callback that one can hook into to get updates on the login process - * @param 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 - */ -export declare function refresh(profile: profile, updates?: (info: update) => void, MStoken?: token): Promise; - -/** - * Checks if a profile object is still valid - * @param profile Player profile. Similar to the one you'd normaly get with the Mojang login - * @return Returns a boolean stating whether a set account is still valid -*/ -export declare function validate(profile: profile): Boolean; - -/** - * 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 - * @param 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:/ as that's added for you!) - * @param getlink The URL needed to log in your user will be handled by the function you provide here. I recommend you send it off to a web browser or something similar - * @param 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 - */ -export declare function login(token: token, getlink: (info: string) => void, updates?: (info: update) => void): Promise; -/** - * @deprecated The 'auto' type parameter might cause some unforseen behaviour. It is thus marked for removal... - */ -export declare function launch(type: "auto", token: token, updates?: (info: update) => void, properties?: windowProperties): Promise; -/** - * Used with electron or nwjs to launch a pop-up that a user can use to sign in with - * @param type The GUI framework this is compatible with - * @param token Basic MS token info - * @param updates A callback that one can hook into to get updates on the login process - * @param properties See windowProperties interface for more information - * @returns A promise that will grant you a user profile and Mojang login token - */ -export declare function launch(type: framework, token: token, updates?: (info: update) => void, properties?: windowProperties): Promise; - -/** - * @deprecated This 'type' parameter might cause some unforseen behaviour. It is thus marked for removal... - */ -export declare function fastLaunch(type: "auto", updates?: (info: update) => void, prompt?: prompt, properties?: windowProperties): Promise; -/** - * Memics the vanilla launcher in how it works. Like launch in creates a popup a user can log in with - * @param type The GUI framework this is compatible with - * @param updates A callback that one can hook into to get updates on the login process - * @param prompt See the type definition for "prompt" for more information - * @param properties See windowProperties interface for more information - * @returns A promise that will grant you a user profile and Mojang login token - */ -export declare function fastLaunch(type: framework, updates?: (info: update) => void, prompt?: prompt, properties?: windowProperties): Promise; -/** - * A copy of the user object mclc uses - */ -export type mclcUser = { - access_token: string; - client_token?: string; - uuid: string; - - name?: string; - meta?: { type: "mojang" | "xbox", xuid?: string, demo?: boolean }; - user_properties?: Partial; -} - -/**Used with the Minecraft Launcher core library, special thanks for Luuxis */ -export declare function getMCLC(): { - getAuth: (info: result) => mclcUser - validate: (profile: mclcUser) => Promise - refresh: (profile: mclcUser, updates?: (info: update) => void, MStoken?: token) => Promise - toProfile: (profile: mclcUser) => profile -} -/**Xbox live specific endpoints. */ -export declare function getXbox(): { - /** - * Checks if the Xbox live access token is still valid! - * @param result The old result object - */ - validate: (result: result) => Boolean; - /** - * A partial refresh on the xbox related tokens. - * @param result The old result object - */ - refresh: (result: result, msToken: token) => Promise; - /** - * Returns a list of xprofile objects belonging to a set user's friendlist - * @param profile The xprofile who's friendlist you want to read - */ - getFriendlist: (profile: xprofile) => Promise; - /** - * Returns a list of xprofile objects belonging to a set user's friendlist - * @param auth The auth header needed to use the endpoint - * @param xuid The user's xbox ID or xprofile object you want the friendlist from - */ - getFriendlist: (auth: string | (() => string), xuid?: string | xprofile) => Promise; - /** - * Gets the xprofile from a set user based on thier xbox id - * @param auth The auth header needed to use the endpoint - * @param xuid The user's xbox ID - */ - getXProfile: (auth: string | (() => string), xuid?: string) => Promise; - -} -/**Checks if a return value is valid */ -export declare function errorCheck(result: result): Boolean; - -/**Checks if a return value is a demo account */ -export declare function isDemoUser(result: result): Boolean; - -/**Checks if a player object is a demo account */ -export declare function isDemoUser(profile: profile): Boolean; - -/** - * 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 - */ -export declare function getExceptional(): { - authenticate: (code: string, MStoken: token, updates?: (info: update) => void) => Promise - refresh: (profile: profile, updates?: (info: update) => void, MStoken?: token) => Promise - login: (token: token, getlink: (info: string) => void, updates?: (info: update) => void) => Promise - launch: (type: framework, token: token, updates?: (info: update) => void, properties?: windowProperties) => Promise - fastLaunch: (type: framework, updates?: (info: update) => void, prompt?: prompt, properties?: windowProperties) => Promise -} - -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 -} -/** - * ES 6 compatibility for typescript - * These lines of code where a royal pain in the behind to get working. - */ -import * as module from '.'; -export default module; diff --git a/index.js b/index.js deleted file mode 100644 index b0e4fd8..0000000 --- a/index.js +++ /dev/null @@ -1,183 +0,0 @@ -/*MIT License - -Copyright (c) 2021 Hanro - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE.*/ - -const BE = require("./modules/backEnd"); - -module.exports = { - //Pass through to set fetch - setFetch(fetchIn) { - console.warn("[MSMC]: The 'setFetch' method is deprecated!") - BE.setFetch(fetchIn); - }, - mojangAuthToken(prompt) { - const token = { - client_id: "00000000402b5328", - redirect: "https://login.live.com/oauth20_desktop.srf", - prompt: prompt - } - return token; - }, - //Creates a login link - createLink(token) { - if (typeof token == String) { - token = self.mojangAuthToken(token); - } - return ( - "https://login.live.com/oauth20_authorize.srf" + - "?client_id=" + - token.client_id + - "&response_type=code" + - "&redirect_uri=" + encodeURIComponent(token.redirect) + - "&scope=XboxLive.signin%20offline_access" + - (token.prompt ? "&prompt=" + token.prompt : "") - ); - }, - //Callback function used with custom login flows - authenticate(code, MStoken, updates = () => { }) { - const body = ( - "client_id=" + MStoken.client_id + - (MStoken.clientSecret ? "&client_secret=" + MStoken.clientSecret : "") + - "&code=" + code + - "&grant_type=authorization_code" + - "&redirect_uri=" + MStoken.redirect) - return BE.get(body, updates); - }, - //Used to refresh the login token of a msmc account - refresh(profile, updates = () => { }, authToken) { - if (!profile._msmc) { - console.error("[MSMC]: This is not an msmc style profile object"); - return; - } - const refreshToken = profile._msmc.refresh ? profile._msmc.refresh : profile._msmc; - authToken = authToken ? authToken : self.mojangAuthToken(); - const body = ( - "client_id=" + authToken.client_id + - (authToken.clientSecret ? "&client_secret=" + authToken.clientSecret : "") + - "&refresh_token=" + refreshToken + - "&grant_type=refresh_token") - return BE.get(body, updates); - }, - //Used to check if tokens are still valid - validate(profile) { - return profile._msmc.expires_by && profile._msmc.mcToken && ((profile._msmc.expires_by - Math.floor(Date.now() / 1000)) > 0); - }, - //Generic ms login flow - login(token, getlink, updates) { - return new Promise((resolve) => { - const app = BE.setCallback((Params) => { - self.authenticate(Params.get("code"), token, updates).then(c => { resolve(c); }); - }) - app.addListener("listening", () => { - if (String(token.redirect).startsWith("/")) { - token.redirect = String(token.redirect).substr(1); - } - if (!token.redirect || !(token.redirect.startsWith("http"))) - token.redirect = - "http://localhost:" + - app.address().port + - "/" + - (token.redirect ? token.redirect : ""); - getlink(self.createLink(token)); - }); - }); - }, - - fastLaunch(type = "auto", updates, prompt = "select_account", properties) { - return self.launch(type, self.mojangAuthToken(prompt), updates, properties) - }, - - launch(type, token, updates, Windowproperties) { - // eslint-disable-next-line no-undef - - if (type == "auto") { - console.warn("[MSMC] The 'auto' gui framework is deprecated!!") - if (!!process && !!process.versions && !!process.versions.electron) { - type = 'electron'; - } else if (!!process && !!process.versions && !!process.versions.nw) { - type = 'nwjs'; - } else { - type = 'raw'; - } - } - switch (type) { - case ("electron"): return require("./modules/gui/electron.js")(token, updates, Windowproperties); - case ("nwjs"): return require("./modules/gui/nwjs.js")(token, updates, Windowproperties); - case ("raw"): return require("./modules/gui/raw.js")(token, updates, Windowproperties); - default: return launch("auto", token, updates, Windowproperties); - - } - }, - //MCLC integration - getMCLC() { - return require("./modules/mclc"); - }, - getXbox() { - return require("./modules/xbox") - }, - getFriendlist(auth, xuid) { - console.warn("This function has been moved to the Xbox module!") - return this.getXbox().getFriendlist(auth, xuid); - }, - errorCheck(result) { - return !(result.type == "Success" || result.type == "DemoUser") - }, - isDemoUser(result) { - result = result.profile ? result.profile : result; - return result._msmc && !!result._msmc.demo; - }, - getExceptional() { - return require("./modules/wrapper").exceptional; - }, - getCallback() { - return require("./modules/wrapper").callback; - }, - mkES6() { - var es6 = "/**Generated*/\nimport msmc from \"./index.js\"\nexport default msmc;\nconsole.log(\"[MSMC]: Loading in ES6 mode!\")"; - - for (var id in self) { - try { - if (typeof (self[id]) == "function" && id != "mkES6") { - /**@type {string} */ - const func = self[id].toString(); - const strFunc = func.substr(0, func.indexOf("{\n")) - es6 += "\nexport function " - + strFunc - + "{return msmc." - + strFunc.trim().replace(/=.*?\}/g, "").replace(/=.*?".*?"/g, "") - + ";};"; - } - } catch (err) { - console.error(err) - } - } - console.log(es6); - - }, - - - default: module.exports -} -/** - * @type {this} - */ -const self = module.exports; - diff --git a/index.mjs b/index.mjs deleted file mode 100644 index c7a9f53..0000000 --- a/index.mjs +++ /dev/null @@ -1,21 +0,0 @@ -/**Generated*/ -import msmc from "./index.js" -export default msmc; -console.log("[MSMC]: Loading in ES6 mode!") -export function setFetch(fetchIn) {return msmc.setFetch(fetchIn);}; -export function mojangAuthToken(prompt) {return msmc.mojangAuthToken(prompt);}; -export function createLink(token) {return msmc.createLink(token);}; -export function authenticate(code, MStoken, updates = () => { }) {return msmc.authenticate(code, MStoken, updates );}; -export function refresh(profile, updates = () => { }, authToken) {return msmc.refresh(profile, updates , authToken);}; -export function validate(profile) {return msmc.validate(profile);}; -export function login(token, getlink, updates) {return msmc.login(token, getlink, updates);}; -export function fastLaunch(type = "auto", updates, prompt = "select_account", properties) {return msmc.fastLaunch(type , updates, prompt , properties);}; -export function launch(type, token, updates, Windowproperties) {return msmc.launch(type, token, updates, Windowproperties);}; -export function getMCLC() {return msmc.getMCLC();}; -export function getXbox() {return msmc.getXbox();}; -/**@deprecated Function moved! */ -export function getFriendlist(auth, xuid) {return msmc.getFriendlist(auth, xuid);}; -export function errorCheck(result) {return msmc.errorCheck(result);}; -export function isDemoUser(result) {return msmc.isDemoUser(result);}; -export function getExceptional() {return msmc.getExceptional();}; -export function getCallback() {return msmc.getCallback();}; \ No newline at end of file diff --git a/lexipacks/afrikaans.json b/lexipacks/afrikaans.json new file mode 100644 index 0000000..84bb7fb --- /dev/null +++ b/lexipacks/afrikaans.json @@ -0,0 +1,42 @@ +{ + "version":"4.0.0", + "name":"Afrikaans", + "error": "'n Onbekende fout het voorgekom", + "error.auth": "n Onbekende fout het voorgekom terwel ons probeer het om u identitied te verifiheer", + "error.auth.microsoft": "Kon nie aanmeld by u Microsoft-rekening nie", + "error.auth.xboxLive": "Kon nie by Xbox Live aanmeld nie", + "error.auth.xsts": "Onbekende fout het voorgekom tydens 'n poging om 'n Xbox Live Security Token te verkry", + "error.auth.xsts.userNotFound": "Die gegewe Microsoft-rekening het nie 'n Xbox-rekening nie", + "error.auth.xsts.bannedCountry": "Die gegewe Microsoft-rekening is van 'n land waar Xbox live nie beskikbaar is nie", + "error.auth.xsts.child": "Die rekening is 'n kind (onder 18) en kan nie voortgaan tensy die rekening deur 'n volwassene gesinsrekening gevoeg word nie", + "error.auth.xsts.child.SK": "Suid-Koreaanse wetgewing: Gaan na die Xbox-bladsy en verleen ouerlike regte om voort te gaan om aan te meld.", + + "error.auth.minecraft": "Onbekende fout het voorgekom toe daar geprobeer is om by Minecraft aan te meld", + "error.auth.minecraft.login": "Kon nie u identitied verifiheer met Mojang met u se gewe Xbox-rekening nie", + "error.auth.minecraft.profile": "Kon nie minecraft-profiel haal nie", + "error.auth.minecraft.entitlements": "Kon nie spelerregte gaan haal nie", + + "error.gui": "'n Onbekende gui-raamwerkfout het voorgekom", + "error.gui.closed": "Gui gesluit deur gebruiker", + "error.gui.raw.noBrowser": "geen chromiumblaaier is gestel nie, kan nie voortgaan nie!", + + "error.state.invalid": "[Intern]: Metode nie geïmplementeer nie.", + "error.state.invalid.gui": "[Intern]: Ongeldige gui-raamwerk.", + "error.state.invalid.redirect": "[Intern]: Die token moet 'n herleiding feld hê wat begin met 'http://localhost/' vir hierdie funksie om te werk!", + "error.state.invalid.electron": "[Intern]: Dit blyk dat jy probeer het om elektron op die voorkant te laai. 'n Kritieke funksie is nie beskikbaar nie!", + + "load": "Generiese laai gebeurtenis", + "load.auth": "Generiese verifikasie laai gebeurtenis", + "load.auth.microsoft": "Teken in by Microsoft-rekening", + "load.auth.xboxLive": "Teken in by Xbox-rekening", + "load.auth.xsts": "Genereer Xbox Live Security Token", + + "load.auth.minecraft": "Generiese Minecraft-aanmeldingvloeigeleentheid", + "load.auth.minecraft.login": "Verifieer met Mojang se bedieners", + "load.auth.minecraft.profile": "Haal tans spelerprofiel", + "load.auth.minecraft.gamepass": "Kontroleer of 'n gebruiker 'gamepass' het", + + "gui": "Gui komponent", + "gui.title": "Teken by Minecraft aan", + "gui.market": "af-ZA" +} \ No newline at end of file diff --git a/lexipacks/english.json b/lexipacks/english.json new file mode 100644 index 0000000..31ab377 --- /dev/null +++ b/lexipacks/english.json @@ -0,0 +1,44 @@ +{ + "version":"4.0.0", + "name":"English", + "error": "An unknown error has occured", + "error.auth": "An unknown authentication error has occured", + "error.auth.microsoft": "Failed to login to Microsoft account", + "error.auth.xboxLive": "Failed to login to Xbox Live", + "error.auth.xsts": "Unknown error occured when attempting to optain an Xbox Live Security Token", + "error.auth.xsts.userNotFound": "The given Microsoft account doesn't have an Xbox account", + "error.auth.xsts.bannedCountry": "The given Microsoft account is from a country where Xbox live is not available", + "error.auth.xsts.child": "The account is a child (under 18) and cannot proceed unless the account is added to a Family account by an adult", + "error.auth.xsts.child.SK": "South Korean law: Go to the Xbox page and grant parental rights to continue logging in.", + + "error.auth.minecraft": "Unknown error occured when attempting to login to Minecraft", + "error.auth.minecraft.login": "Failed to authenticate with Mojang with given Xbox account", + "error.auth.minecraft.profile": "Failed to fetch minecraft profile", + "error.auth.minecraft.entitlements": "Failed to fetch player entitlements", + + "error.gui": "An unknown gui framework error has occured", + "error.gui.closed": "Gui closed by user", + "error.gui.raw.noBrowser": "no chromium browser was set, cannot continue!", + + "error.state.invalid": "[Internal]: Method not implemented.", + "error.state.invalid.gui": "[Internal]: Invalid gui framework.", + "error.state.invalid.redirect": "[Internal]: The token must have a redirect starting with 'http://localhost/' for this function to work!", + "error.state.invalid.electron": "[Internal]: It seems you're attempting to load electron on the frontend. A critical function is missing!", + + "load": "Generic load event", + "load.auth": "Generic authentication load event", + "load.auth.microsoft": "Logging into Microsoft account", + "load.auth.xboxLive": "Logging into Xbox Live", + "load.auth.xboxLive.1": "Logging into Xbox Live", + "load.auth.xboxLive.2": "Authenticating with xbox live", + "load.auth.xsts": "Generating Xbox Live Security Token", + + "load.auth.minecraft": "Generic Minecraft login flow event", + "load.auth.minecraft.login": "Authenticating with Mojang's servers", + "load.auth.minecraft.profile": "Fetching player profile", + "load.auth.minecraft.gamepass": "[experimental!] Checking if a user has gamepass", + + "gui": "Gui component", + "gui.title": "Sign in to your account", + "gui.market": "en-US" +} \ No newline at end of file diff --git a/lexipacks/french.json b/lexipacks/french.json new file mode 100644 index 0000000..f91c3e9 --- /dev/null +++ b/lexipacks/french.json @@ -0,0 +1,44 @@ +{ + "version":"4.0.0", + "name":"French", + "error": "Une erreur inconnue est survenue.", + "error.auth": "Une erreur d'authentification inconnue est survenue.", + "error.auth.microsoft": "Échec de la connexion à votre compte Microsoft.", + "error.auth.xboxLive": "Échec de la connexion à Xbox Live.", + "error.auth.xsts": "Une erreur inconnue est survenue en essayant d'obtenir un Xbox Live Security Token.", + "error.auth.xsts.userNotFound": "The compte Microsoft donné n'a pas de compte Xbox.", + "error.auth.xsts.bannedCountry": "Le compte Microsoft donné vient d'un pays où Xbox Live n'est pas disponible.", + "error.auth.xsts.child": "Le compte est un enfant (sous 18) et ne peux pas continuer sauf si le compte est ajouté à un compte de famille par un adulte.", + "error.auth.xsts.child.SK": "Loi de la Corée du Sud: Allez à la page Xbox et accordez les permission parentales pour se connecter.", + + "error.auth.minecraft": "Une erreur inconnue est survenue en tentant de se connecter à Minecraft.", + "error.auth.minecraft.login": "Échec d'authentification avec Mojang avec le compte Xbox donné.", + "error.auth.minecraft.profile": "Échec de récupération du profil minecraft.", + "error.auth.minecraft.entitlements": "Échec de récupération des droits du joueur.", + + "error.gui": "Une erreur inconnue du framework de l'interface graphique est survenue.", + "error.gui.closed": "Interface graphique fermé par l'utilisateur", + "error.gui.raw.noBrowser": "Auncun navigateur chromium à été définis, impossible de continuer!", + + "error.state.invalid": "[Interne]: Methode non implémenté.", + "error.state.invalid.gui": "[Interne]: Framework d'interface graphique invalide.", + "error.state.invalid.redirect": "[Interne]: Le token doit avoir une redirection qui commence avec 'http://localhost/' pour que cette fonction fonctionne!", + "error.state.invalid.electron": "[Interne]: On dirais que vous essayez de charger electron sur le frontend. Une fonction critique est manquante!", + + "load": "Événement générique de chargement", + "load.auth": "Événement générique d'authentification", + "load.auth.microsoft": "Connexion au compte Microsoft", + "load.auth.xboxLive": "Connexion à Xbox Live", + "load.auth.xboxLive.1": "Connexion à Xbox Live", + "load.auth.xboxLive.2": "Authentification avec Xbox Live", + "load.auth.xsts": "Génération d'un nouveau Xbox Live Security Token", + + "load.auth.minecraft": "Événement générique de connexion à Minecraft", + "load.auth.minecraft.login": "Authentification avec les servers de Mojang.", + "load.auth.minecraft.profile": "Récupération du profil du joueur", + "load.auth.minecraft.gamepass": "[Experimental!] Vérification si le joueur à le Gamepass", + + "gui": "Composant GUI", + "gui.title": "Se connecter à Minecraft", + "gui.market": "fr-FR" +} diff --git a/lexipacks/readme.md b/lexipacks/readme.md new file mode 100644 index 0000000..092862e --- /dev/null +++ b/lexipacks/readme.md @@ -0,0 +1,18 @@ +# Lexipacks! +These are optional files that provide translations for msmc events and errors. They are meant to be used by launcher developers to easily change the language msmc displays errors and load events in. + +If a languagepack is missing a translation, the internal translation function msmc will map it to the next available lexicode. A lexicode is essentially a string that uses a "." to seperate between the key words held within a set code. + +# Contribution +Besides just checking for trolling via a basic google translation check. You are free to create pull requests to add more lexipacks. These won't be included by default, but can be downloaded by launcher developers seperately to add multilingual support. + +A version header is added simply to check when a set lexipack was last updated. This is here simply for launcher developers to see at a glance if they need to update their bundled lexipacks. The version string should match the version of msmc the pack was last updated against. + +# Maintainers +The people you need to bug about translation errors in a set lexipack. This will be updated as more lexipacks are added. +* Afrikaans [Hanro50](https://github.com/hanro50) +* English [Hanro50](https://github.com/hanro50) +* French [Txab](https://github.com/Txab33) + +# Note +The internal code of msmc should still be english. Mainly due to the maintainment nightmare it would be if we had 500 different languages represented in the code itself. This merely effects the output of msmc and you may be asked to switch to the default english lexipack when opening a support ticket in MCJS café. diff --git a/modules/backEnd.js b/modules/backEnd.js deleted file mode 100644 index 3793f77..0000000 --- a/modules/backEnd.js +++ /dev/null @@ -1,274 +0,0 @@ - -var http; -const FETCH = require("node-fetch"); -//try { FETCH = typeof fetch == 'function' ? fetch : require("node-fetch"); } catch (er) { console.log(er); console.warn("[MSMC]: Could not load fetch, please use setFetch to define it manually!"); } -try { http = require("http"); } catch (er) { console.warn("[MSMC]: Some sign in methods may not work due to missing http server support in enviroment"); } -//This needs to be apart or we could end up with a memory leak! -var app; - - - - -module.exports = { - //Used for the old/generic method of authentication - setCallback(callback) { - if (!http) { console.error("[MSMC]: Could not define http server, please use a different method!"); return; } - try { if (app) { app.close(); } } catch { /*Ignore*/ } - app = http.createServer((req, res) => { - res.writeHead(200, { "Content-Type": "text/plain" }); - res.end("Thank you!"); - app.close(); - - if (req.url.includes("?")) { - const urlParams = new URLSearchParams(req.url.substr(req.url.indexOf("?") + 1)); - callback(urlParams); - } - }); - return app.listen(); - }, - //Used to set the version of fetch used manually - setFetch(fetchIn) { - FETCH = fetchIn; - }, - //Used internally to get fetch when needed - - getFetch() { - return FETCH; - }, - - //Load constants - errorCheck() { - - if (typeof FETCH !== "function") { - console.error("[MSMC]: The version of fetch provided is not a function!"); - return true; - } - - return false; - }, - - parseUsr(user, auth) { - console.log(user) - return { - xuid: user.id, - gamerTag: user.settings.find(s => s.id == "Gamertag")?.value, - name: user.settings.find(s => s.id == "GameDisplayName")?.value, - profilePictureURL: user.settings.find(s => s.id == "GameDisplayPicRaw").value, - score: user.settings.find(s => s.id == "Gamerscore").value, - getFriends: () => self.getFriendList(auth, user.id) - } - }, - - async getFriendList(auth, xuid) { - const target = xuid ? `xuid(${xuid})` : "me"; - if (typeof auth == "function") auth = auth(); - let friendsRaw = await FETCH(`https://profile.xboxlive.com/users/${target}/profile/settings/people/people?settings=GameDisplayName,GameDisplayPicRaw,Gamerscore,Gamertag`, - { - headers: { - "Content-Type": "application/json", - "x-xbl-contract-version": 2, - Authorization: auth, - }, - }); - const friends = await friendsRaw.json(); - let R = []; - console.log("friends", friends, "friends") - friends.profileUsers.forEach(element => { - R.push(self.parseUsr(element, auth)); - }); - return R; - }, - //Used to get xbox profile information - async xboxProfile(XBLToken, updates = () => { }) { - const lbar = 100 / 2.5; - updates({ type: "Loading", data: "Getting xuid", percent: 0 }); - var rxsts = await FETCH("https://xsts.auth.xboxlive.com/xsts/authorize", { - method: "post", - body: JSON.stringify({ - Properties: { SandboxId: "RETAIL", UserTokens: [XBLToken] }, - RelyingParty: "http://xboxlive.com", - TokenType: "JWT", - }), - headers: { "Content-Type": "application/json", Accept: "application/json" }, - }); - const json = await rxsts.json(); - const xui = json.DisplayClaims.xui[0]; - console.log(xui) - - const auth = `XBL3.0 x=${xui.uhs};${json.Token}` - console.log(json.Token) - updates({ type: "Loading", data: "Getting profile info", percent: lbar * 1 }); - var info = await FETCH("https://profile.xboxlive.com/users/batch/profile/settings", - { - method: "post", - headers: { - "Content-Type": "application/json", - "x-xbl-contract-version": 2, - Authorization: auth, - }, - body: JSON.stringify({ "userIds": [xui.xid], "settings": ["GameDisplayName", "GameDisplayPicRaw", "Gamerscore", "Gamertag"] }), - }); - - - updates({ type: "Loading", data: "Parsing profile info", percent: lbar * 2 }); - // console.log(info); - const profile = await info.json(); - // console.log(profile); - - const user = self.parseUsr(profile.profileUsers[0], auth); - //user.friends = await self.getFriendList(auth, user.xuid) - user.getAuth = () => auth; - updates({ type: "Loading", data: "Done!", percent: 100 }); - return user - }, - - - //Main Login flow implementation - async get(body, updates = console.log) { - const percent = 100 / 5; - if (self.errorCheck()) { return Promise.reject("[MSMC]: Error : no or invalid version of fetch available!"); } - updates({ type: "Starting" }); - - //console.log(Params); //debug - function loadBar(number, asset) { - updates({ type: "Loading", data: asset, percent: number }); - } - - function error(reason, translationString, data) { - return { type: "Authentication", reason: reason, data: data, translationString: translationString } - } - - function webCheck(response) { - return (response.status >= 400) - } - - loadBar(percent * 0, "Getting Login Token"); - var MS_Raw = await FETCH("https://login.live.com/oauth20_token.srf", { - method: "post", body: body, headers: { "Content-Type": "application/x-www-form-urlencoded" } - }) - - if (webCheck(MS_Raw)) return error("Could not log into Microsoft", "Login.Fail.MS", rxboxlive); - var MS = await MS_Raw.json(); - - //console.log(MS); //debug - if (MS.error) { - return error("(" + MS.error + ") => " + MS.error_description + "\nThis is likely due to an invalid refresh token. Please relog!", "Login.Fail.Relog", { error: MS.error, disc: MS.error_description }); - } - const ms_exp = Math.floor(Date.now() / 1000) + MS["expires_in"] - 100 - - loadBar(percent * 1, "Logging into Xbox Live"); - var rxboxlive = await FETCH("https://user.auth.xboxlive.com/user/authenticate", { - method: "post", - body: JSON.stringify({ - Properties: { - AuthMethod: "RPS", - SiteName: "user.auth.xboxlive.com", - RpsTicket: `d=${MS.access_token}` // your access token from step 2 here - }, - RelyingParty: "http://auth.xboxlive.com", - TokenType: "JWT" - }), - headers: { "Content-Type": "application/json", Accept: "application/json" }, - }); - - if (webCheck(rxboxlive)) return error("Could not log into xbox", "Login.Fail.Xbox", rxboxlive); - var token = await rxboxlive.json(); - //console.log(token); //debug - var XBLToken = token.Token; - var UserHash = token.DisplayClaims.xui[0].uhs; - loadBar(percent * 2, "Getting a Xbox One Security Token"); - var rxsts = await FETCH("https://xsts.auth.xboxlive.com/xsts/authorize", { - method: "post", - body: JSON.stringify({ - Properties: { SandboxId: "RETAIL", UserTokens: [XBLToken] }, - RelyingParty: "rp://api.minecraftservices.com/", - TokenType: "JWT", - }), - //"rp://api.minecraftservices.com/", - headers: { "Content-Type": "application/json", Accept: "application/json" }, - }); - - - var XSTS = await rxsts.json(); - - loadBar(percent * 2.5, "Checking for errors"); - //console.log(XSTS); //debug - if (XSTS.XErr) { - var reason = "Unknown reason"; - var ts = "Unknown"; - switch (XSTS.XErr) { - case 2148916233: - reason = "The account doesn't have an Xbox account."; - ts = "UserNotFound"; - break; - case 2148916235: - reason = "The account is from a country where Xbox live is not available"; - ts = "BannedCountry"; - break; - case 2148916236: - case 2148916237: - //I have no idea if this translates correctly. I don't even know what has to be done...Korean law is strange - reason = "South Korean law: Go to the Xbox page and grant parental rights to continue logging in."; - ts = "ChildInSouthKorea"; - break; - case 2148916238: - //Check MSMC's wiki pages on github if you keep getting this error - reason = - "The account is a child (under 18) and cannot proceed unless the account is added to a Family account by an adult."; - ts = "UserNotAdult"; - break; - - } - return error(reason, `Account.${ts}`); - } - //console.log("XBL3.0 x=" + UserHash + ";" + XSTS.Token) //debug - loadBar(percent * 3, "Logging into Minecraft"); - var rlogin_with_xbox = await FETCH( - "https://api.minecraftservices.com/authentication/login_with_xbox", - { - method: "post", - body: JSON.stringify({ - identityToken: `XBL3.0 x=${UserHash};${XSTS.Token}` - }), - headers: { "Content-Type": "application/json", Accept: "application/json" }, - } - ); - if (webCheck(rlogin_with_xbox)) return error("Could not log into Minecraft", "Login.Fail.MC", rlogin_with_xbox); - - var MCauth = await rlogin_with_xbox.json(); - //console.log(MCauth) //debug - const experationDate = Math.floor(Date.now() / 1000) + MCauth["expires_in"] - 100 - - loadBar(percent * 4, "Fetching player profile"); - var r998 = await FETCH("https://api.minecraftservices.com/minecraft/profile", { - headers: { - "Content-Type": "application/json", - Accept: "application/json", - Authorization: `Bearer ${MCauth.access_token}`, - }, - }); - - loadBar(percent * 4.5, "Extracting XUID and parsing player object"); - var profile = await r998.json(); - const xuid = self.parseJwt(MCauth.access_token).xuid; - - profile._msmc = { refresh: MS.refresh_token, expires_by: experationDate, ms_exp: ms_exp, mcToken: MCauth.access_token }; - if (profile.error) { - profile._msmc.demo = true; - return ({ type: "DemoUser", access_token: MCauth.access_token, profile: { xuid: xuid, _msmc: profile._msmc, id: MCauth.username, name: 'Player' }, translationString: "Login.Success.DemoUser", reason: "User does not own minecraft", getXbox: () => self.xboxProfile(XBLToken) }); - } - profile.xuid = xuid; - loadBar(100, "Done!"); - return ({ type: "Success", access_token: MCauth.access_token, profile: profile, getXbox: (updates) => self.xboxProfile(XBLToken, updates), translationString: "Login.Success.User" }); - }, - parseJwt(token) { - var base64Url = token.split('.')[1]; - var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); - var jsonPayload = decodeURIComponent(Buffer.from(base64, "base64").toString("utf8").split('').map(function (c) { - return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); - }).join('')); - return JSON.parse(jsonPayload); - }, -} - -const self = module.exports; \ No newline at end of file diff --git a/modules/gui/electron.js b/modules/gui/electron.js deleted file mode 100644 index 76bc31f..0000000 --- a/modules/gui/electron.js +++ /dev/null @@ -1,44 +0,0 @@ -const MSMC = require("../.."); -const dynReq = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require; - -const { BrowserWindow } = dynReq("electron"); - -const defaultProperties = { - width: 500, - height: 650, - resizable: false, -}; - -module.exports = (token, updates = () => { }, Windowproperties = defaultProperties) => { - return new Promise(resolve => { - var ts = "Cancelled.GUI"; - var redirect = MSMC.createLink(token); - const mainWindow = new BrowserWindow(Windowproperties); - mainWindow.setMenu(null); - mainWindow.loadURL(redirect); - const contents = mainWindow.webContents; - var loading = false; - mainWindow.on("close", () => { - if (!loading) { resolve({ type: "Cancelled", translationString: ts }) }; - }); - - contents.on("did-finish-load", () => { - const loc = contents.getURL(); - if (loc.startsWith(token.redirect)) { - const urlParams = new URLSearchParams(loc.substr(loc.indexOf("?") + 1)).get("code"); - if (urlParams) { - resolve(MSMC.authenticate(urlParams, token, updates)); - loading = true; - } - else { - ts = "Cancelled.Back"; - } - try { - mainWindow.close(); - } catch { - console.error("[MSMC]: Failed to close window!"); - } - }; - }); - }); -}; diff --git a/modules/mclc.js b/modules/mclc.js deleted file mode 100644 index cba517b..0000000 --- a/modules/mclc.js +++ /dev/null @@ -1,101 +0,0 @@ -const msmc = require(".."); -const BE = require("./backEnd"); -function getUUID() { - var result = "" - for (var i = 0; i <= 4; i++) { - result += (Math.floor(Math.random() * 16777216 )+1048576).toString(16); - if (i < 4)result += "-" - } - return result; -} -console.log(getUUID()) -module.exports = { - //Converts a result or player profile object to a mclc login object - getAuth(profile) { - if (profile.type) { - if (!profile.profile) { - throw { error: "Invalid profile" } - } - profile = profile.profile; - } - return { - access_token: profile._msmc.mcToken, - client_token: getUUID(), - uuid: profile.id, - name: profile.name, - meta: { - xuid: profile.xuid, - type: "msa", - demo: profile._msmc.demo - }, - _msmc: profile._msmc, - user_properties: "{}" - }; - }, - //Converts a mclc login object to a msmc profile object - toProfile(profile) { - return { "name": profile.name, "xuid": profile.meta ? profile.meta.xuid : null, "id": profile.uuid, "_msmc": profile._msmc }; - }, - //Checks if a mclc login object is still valid - async validate(profile) { - if (profile._msmc) { - return msmc.validate(self.toProfile(profile)); - } - const FETCH = BE.getFetch(); - const req = { - "accessToken": profile.access_token, - "clientToken": profile.client_token - }; - - const r = await FETCH("https://authserver.mojang.com/validate", { - "body": JSON.stringify(req), - "method": "POST", - "headers": { - "Content-Type": "application/json" - } - }); - return r.status == 204 - }, - - async refresh(profile, updates = console.log, authToken) { - const FETCH = BE.getFetch(); - if (profile._msmc) { - return self.getAuth(await msmc.refresh(self.toProfile(profile), updates, authToken)); - } else { - updates({ type: "Starting" }); - updates({ type: "Loading", data: "Refreshing Mojang account", percent: 50 }); - const req = { - "accessToken": profile.access_token, - "clientToken": profile.client_token, - "requestUser": true - }; - - const user = await FETCH("https://authserver.mojang.com/refresh", { - "body": JSON.stringify(req), - "method": "POST", - "headers": { - "Content-Type": "application/json" - } - - }); - updates({ type: "Loading", data: "Getting user data", percent: 85 }); - const data = await user.json(); - - if (data.error) { - throw data; - } - - const userProfile = { - access_token: data.accessToken, - client_token: data.clientToken, - uuid: data.selectedProfile.id, - name: data.selectedProfile.name, - user_properties: data.user ? data.user.properties : "{}" - }; - - updates({ type: "Loading", data: "Done!", percent: 100 }); - return userProfile; - } - } -} -const self = module.exports; \ No newline at end of file diff --git a/modules/wrapper.js b/modules/wrapper.js deleted file mode 100644 index 0371856..0000000 --- a/modules/wrapper.js +++ /dev/null @@ -1,19 +0,0 @@ -const msmc = require('..'); -//Value one: Method name, Value two: Which arg is the update callback function [counting from 0]. -const methods = [["authenticate", 2], ["refresh", 1], ["login", 2], ["launch", 2], ["fastLaunch", 1]]; -module.exports.exceptional = {}; -module.exports.callback = {}; -methods.forEach((method) => { - module.exports.exceptional[method[0]] = async (...arguments) => { - const result = await msmc[method[0]](...arguments); - if (msmc.errorCheck(result)) throw result; - return result; - } - module.exports.callback[method[0]] = (callback, ...arguments) => { - this.exceptional[method[0]](...arguments).then(callback).catch(err => { - if (arguments.length > method[1]) { - arguments[method[1]]({ type: "Error", error: err }) - } - }); - } -}) \ No newline at end of file diff --git a/modules/xbox.js b/modules/xbox.js deleted file mode 100644 index d82d64f..0000000 --- a/modules/xbox.js +++ /dev/null @@ -1,89 +0,0 @@ -const BE = require("./backEnd"); - -console.warn("[MSMC]: The Xbox endpoint function calls are in a beta stage!") -console.warn("[MSMC]: They may experience slight change till they're stabilized.") -console.warn("[MSMC]: Update with care. They will be finalized by version 3.2.0 .") -module.exports = { - /** - * @param {import('..').result } result - * @returns - */ - validate(result) { - let profile = result.profile; - return result.getXbox && profile && profile._msmc.ms_exp && ((profile._msmc.ms_exp - Math.floor(Date.now() / 1000)) > 0); - }, - async refresh(result, authToken) { - function webCheck(response) { - return (response.status >= 400) - } - function error(reason, translationString, data) { - return { type: "Authentication", reason: reason, data: data, translationString: translationString } - } - if (!profile._msmc) { - console.error("[MSMC]: This is not an msmc style profile object"); - return; - } - const refreshToken = profile._msmc.refresh ? profile._msmc.refresh : profile._msmc; - authToken = authToken ? authToken : self.mojangAuthToken(); - - var MS_Raw = await BE.getFetch()("https://login.live.com/oauth20_token.srf", { - method: "post", body: "client_id=" + authToken.client_id + - (authToken.clientSecret ? "&client_secret=" + authToken.clientSecret : "") + - "&refresh_token=" + refreshToken + - "&grant_type=refresh_token", headers: { "Content-Type": "application/x-www-form-urlencoded" } - }) - - if (webCheck(MS_Raw)) return error("Could not log into Microsoft", "Login.Fail.MS", rxboxlive); - var MS = await MS_Raw.json(); - var rxboxlive = await BE.getFetch()("https://user.auth.xboxlive.com/user/authenticate", { - method: "post", - body: JSON.stringify({ - Properties: { - AuthMethod: "RPS", - SiteName: "user.auth.xboxlive.com", - RpsTicket: `d=${MS.access_token}` // your access token from step 2 here - }, - RelyingParty: "http://auth.xboxlive.com", - TokenType: "JWT" - }), - headers: { "Content-Type": "application/json", Accept: "application/json" }, - }); - - if (webCheck(rxboxlive)) return error("Could not log into xbox", "Login.Fail.Xbox", rxboxlive); - var token = await rxboxlive.json(); - console.log(token); //debug - var XBLToken = token.Token; - if (!profile._msmc.refresh) { - profile._msmc = {}; - } - profile._msmc.refresh = MS.refresh_token; - const experationDate = Math.floor(Date.now() / 1000) + MS["expires_in"] - 100 - profile._msmc.ms_exp = experationDate; - result.getXbox = (updates) => self.xboxProfile(XBLToken, updates); - return (result); - - }, - getFriendlist(auth, xuid) { - if (typeof auth == "object") { - if (!auth.auth) throw "[MSMC]: The provided xprofile object does not have a auth header!" - xuid = auth.xuid; - auth = auth.auth; - } - else if (xuid && xuid.xuid) { xuid = xuid.xuid } - return BE.getFriendList(auth, xuid); - }, - async getXProfile(auth, xuid) { - const target = xuid ? `xuid(${xuid})` : "me"; - if (typeof auth == "function") auth = auth(); - let profileRaw = await BE.getFetch()(`https://profile.xboxlive.com/users/${target}/profile/settings?settings=GameDisplayName,GameDisplayPicRaw,Gamerscore,Gamertag`, - { - headers: { - "Content-Type": "application/json", - "x-xbl-contract-version": 2, - Authorization: auth, - }, - }); - const profile = await profileRaw.json(); - return BE.parseUsr(profile.profileUsers[0], auth); - }, -} \ No newline at end of file diff --git a/package.json b/package.json index de7aee0..4fa37b1 100644 --- a/package.json +++ b/package.json @@ -1,35 +1,23 @@ { "name": "msmc", - "version": "3.1.1", + "version": "4.0.0-pre1", "description": "A bare bones login library for Minecraft based projects to authenticate individuals with a Microsoft account.", "license": "MIT", "exports": { ".": { - "import": "./index.mjs", - "require": "./index.js" + "import": "./dist/index.js", + "require": "./dist/index.js" } }, "repository": { "type": "git", "url": "git://github.com/Hanro50/MSMC.git" }, - "types": "index.d.ts", + "types": "./types/index.d.ts", "author": "Hanro50 (https://github.com/Hanro50)", "bugs": { "url": "https://github.com/Hanro50/MSMC/issues" }, - "files": [ - "index.d.ts", - "index.js", - "tests.mjs", - "index.mjs", - "modules/backEnd.js", - "modules/mclc.js", - "modules/wrapper.js", - "modules/gui/electron.js", - "modules/gui/nwjs.js", - "modules/gui/raw.js" - ], "homepage": "https://github.com/Hanro50/MSMC/", "scripts": { "test:raw": "cd ./tests/raw\nnpm run start", @@ -50,15 +38,26 @@ "node": ">=14.17.3" }, "devDependencies": { + "electron": "^18.2.4", + "@types/node": "^17.0.35", + "@types/node-fetch": "^2.6.1", "@types/webpack": "^5.28.0" }, "dependencies": { - "node-fetch": "2.x" + "node-fetch": "2.x", + "tslib": "^2.4.0" }, "funding": [ { "type": "paypal", "url": "https://paypal.me/hanro50" } + ], + "files": [ + "dist", + "types", + "README.md", + "package.json", + "LICENSE" ] } \ No newline at end of file diff --git a/src/assets.ts b/src/assets.ts new file mode 100644 index 0000000..ea9cb76 --- /dev/null +++ b/src/assets.ts @@ -0,0 +1,152 @@ +import { readFileSync } from "fs"; +import type { Response } from "node-fetch" +import { join } from "path"; +/** + * A copy of the user object mclc uses + */ +export type mclcUser = { + access_token: string; + client_token?: string; + uuid: string; + + name?: string; + meta?: { type: "mojang" | "msa" | "legacy", xuid?: string, demo?: boolean }; + user_properties?: Partial; +} +/** + * If the exact code isn't founnd. The lexicon string is split up and shaved down till it finds a description for the code. + * + * For example; error.auth.microsoft will be shortend to error.auth if error.auth.microsoft isn't found + */ +export let lexicon = { + //Error + "error": "An unknown error has occured", + "error.auth": "An unknown authentication error has occured", + "error.auth.microsoft": "Failed to login to Microsoft account", + "error.auth.xboxLive": "Failed to login to Xbox Live", + "error.auth.xsts": "Unknown error occured when attempting to optain an Xbox Live Security Token", + "error.auth.xsts.userNotFound": "The given Microsoft account doesn't have an Xbox account", + "error.auth.xsts.bannedCountry": "The given Microsoft account is from a country where Xbox live is not available", + "error.auth.xsts.child": "The account is a child (under 18) and cannot proceed unless the account is added to a Family account by an adult", + "error.auth.xsts.child.SK": "South Korean law: Go to the Xbox page and grant parental rights to continue logging in.", + + "error.auth.minecraft": "Unknown error occured when attempting to login to Minecraft", + "error.auth.minecraft.login": "Failed to authenticate with Mojang with given Xbox account", + "error.auth.minecraft.profile": "Failed to fetch minecraft profile", + "error.auth.minecraft.entitlements": "Failed to fetch player entitlements", + + "error.gui": "An unknown gui framework error has occured", + "error.gui.closed": "Gui closed by user", + "error.gui.raw.noBrowser": "no chromium browser was set, cannot continue!", + + "error.state.invalid": "[Internal]: Method not implemented.", + "error.state.invalid.gui": "[Internal]: Invalid gui framework.", + "error.state.invalid.redirect": "[Internal]: The token must have a redirect starting with 'http://localhost/' for this function to work!", + "error.state.invalid.electron": "[Internal]: It seems you're attempting to load electron on the frontend. A critical function is missing!", + //Load events + "load": "Generic load event", + "load.auth": "Generic authentication load event", + "load.auth.microsoft": "Logging into Microsoft account", + "load.auth.xboxLive": "Logging into Xbox Live", + "load.auth.xboxLive.1": "Logging into Xbox Live", + "load.auth.xboxLive.2": "Authenticating with xbox live", + "load.auth.xsts": "Generating Xbox Live Security Token", + + "load.auth.minecraft": "Generic Minecraft login flow event", + "load.auth.minecraft.login": "Authenticating with Mojang's servers", + "load.auth.minecraft.profile": "Fetching player profile", + "load.auth.minecraft.gamepass": "[experimental!] Checking if a user has gamepass", + //Gui components + "gui": "Gui component", + "gui.title": "Sign in to your account", + "gui.market": "en-US" +} + +export type lexcodes = keyof typeof lexicon; + +export function lst(lexcodes: lexcodes) { + const lex = lexcodes.split("."); + do { + const l = lex.join('.'); + if (l in lexicon) { return lexicon[l]; } + lex.pop(); + } while (lex.length > 0) + return lexcodes; +} + +export interface exptOpts { + ts: lexcodes; + response: Response; +} + +export function err(ts: lexcodes) { + throw ts; +} + +export function errResponse(response: Response, ts: lexcodes) { + if (!response.ok) throw { response, ts } +} +export function wrapError(code: string | exptOpts | any) { + let name: lexcodes; + let opt: exptOpts | null; + if (typeof code == 'string') { + name = code as lexcodes; + } + else { + opt = code; + name = opt.ts; + } + let message = lst(name || "error"); + return { name, opt, message }; +} + +/** + * Used by graphical Electron and NW.js integrations to set the properties of the generated pop-up + */ +export interface windowProperties { + width: number, + height: number, + /**Raw ignores this property!*/ + resizable?: boolean, + /**Raw only: Stops MSMC from passing through the browser console log*/ + suppress?: boolean, + [key: string]: any +} + +export interface mcProfile { + id: string, + name: string, + skins: Array< + { + id: string, + state: 'ACTIVE', + url: string, + variant: 'SLIM' | 'CLASSIC' + } + >, + capes: Array< + { + id: string, + state: 'ACTIVE', + url: string, + alias: string + } + >, + demo?: boolean +} + + +export function getDefaultWinProperties(): windowProperties { + return { + width: 500, + height: 650, + resizable: false, + title: lst("gui.title") + }; +} + +export function loadLexiPack(...file: string[]): typeof lexicon { + const pack: typeof lexicon = JSON.parse(readFileSync(join(...file)).toString()); + lexicon = pack + return pack +} \ No newline at end of file diff --git a/src/auth/auth.ts b/src/auth/auth.ts new file mode 100644 index 0000000..9dadc4b --- /dev/null +++ b/src/auth/auth.ts @@ -0,0 +1,162 @@ +import EventEmitter from "events"; + +import fetch from "node-fetch"; +import { lexcodes, windowProperties, lst, errResponse, err } from "../assets.js"; +import type xbox from "./xbox.js"; +/** + * This library's supported gui frameworks. + * (Raw requires no extra dependencies, use it if you're using some unknown framework!) + */ +export type framework = "electron" | "nwjs" | "raw"; +/** + * For more information. Check out Microsoft's support page: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code
+ * + * 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 + */ +export type prompt = "login" | "none" | "consent" | "select_account"; +/** + * 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 + * + */ +export interface MStoken { + client_id: string, + redirect: string, + clientSecret?: string, + prompt?: prompt +} +export interface msAuthToken { + token_type: string, + expires_in: number, + scope: string, + access_token: string, + refresh_token: string, + user_id: string, + foci: string +} + + +function mojangAuthToken(prompt?: prompt) { + const token = { + client_id: "00000000402b5328", + redirect: "https://login.live.com/oauth20_desktop.srf", + prompt: prompt + } + return token; +} +export class loader { + auth: auth + constructor(auth: auth) { + this.auth = auth; + } + + load(code: lexcodes) { + this.auth.emit("load", code); + } +} + +export declare interface auth extends EventEmitter { + on(event: "load", listener: (asset: lexcodes, message: string) => void): this + once(event: "load", listener: (asset: lexcodes, message: string) => void): this + emit(event: "load", asset: lexcodes): boolean; +} + +export class auth extends EventEmitter { + token: MStoken; + constructor(prompt?: prompt) + constructor(token: MStoken) + constructor(token?: MStoken | prompt) { + super(); + this.token = (!token || typeof token == "string") ? mojangAuthToken(token as prompt) : token; + } + createLink() { + return ( + "https://login.live.com/oauth20_authorize.srf" + + "?client_id=" + + this.token.client_id + + "&response_type=code" + + "&redirect_uri=" + encodeURIComponent(this.token.redirect) + + "&scope=XboxLive.signin%20offline_access" + + (this.token.prompt ? "&prompt=" + this.token.prompt : "") + + "&mkt=" + lst('gui.market') + ); + } + emit(eventName: string | symbol, ...args: any[]): boolean { + return super.emit(eventName, args[0], lst(args[0])) + } + load(code: lexcodes) { + this.emit("load", code); + } + login(code: string) { + const body = ( + "client_id=" + this.token.client_id + + (this.token.clientSecret ? "&client_secret=" + this.token.clientSecret : "") + + "&code=" + code + + "&grant_type=authorization_code" + + "&redirect_uri=" + this.token.redirect) + return this._get(body); + } + refresh(MS: msAuthToken): Promise + refresh(refreshToken: string): Promise + refresh(MS: msAuthToken | string) { + const refresh = typeof MS == 'string' ? MS : MS.refresh_token; + const body = ( + "client_id=" + this.token.client_id + + (this.token.clientSecret ? "&client_secret=" + this.token.clientSecret : "") + + "&refresh_token=" + refresh + + "&grant_type=refresh_token") + return this._get(body) + } + + async luanch(framework: framework, windowProperties?: windowProperties) { + switch (framework) { + case "raw": + return await this.login(await (require("../gui/raw.js")).default(this, windowProperties)) + case "nwjs": + return await this.login(await (require("../gui/nwjs.js")).default(this, windowProperties)) + case "electron": + return await this.login(await (require("../gui/electron.js")).default(this, windowProperties)) + default: + err('error.state.invalid.gui') + } + } + + async server(port = 0) { + if (this.token.redirect.startsWith('http://localhost/')) err("error.state.invalid.redirect") + throw "error.state.invalid" + } + + private async _get(body: string): Promise { + this.load('load.auth.microsoft') + var MS_Raw = await fetch("https://login.live.com/oauth20_token.srf", { + method: "post", body: body, headers: { "Content-Type": "application/x-www-form-urlencoded" } + }) + errResponse(MS_Raw, "error.auth.microsoft") + + var MS = await MS_Raw.json(); + this.load('load.auth.xboxLive.1') + var rxboxlive = await fetch("https://user.auth.xboxlive.com/user/authenticate", { + method: "post", + body: JSON.stringify({ + Properties: { + AuthMethod: "RPS", + SiteName: "user.auth.xboxlive.com", + RpsTicket: `d=${MS.access_token}` // your access token from step 2 here + }, + RelyingParty: "http://auth.xboxlive.com", + TokenType: "JWT" + }), + headers: { "Content-Type": "application/json", Accept: "application/json" }, + }); + errResponse(rxboxlive, "error.auth.xboxLive") + var token = await rxboxlive.json(); + return new (require("./xbox.js").default)(this, MS, token); + } +} +export default auth; + + diff --git a/src/auth/minecraft.ts b/src/auth/minecraft.ts new file mode 100644 index 0000000..8e63da8 --- /dev/null +++ b/src/auth/minecraft.ts @@ -0,0 +1,87 @@ +import fetch from "node-fetch"; +import { errResponse, mclcUser, mcProfile } from "../assets.js"; +import xbox from "./xbox.js"; +export interface mcJWTDecoded { xuid: string, agg: string, sub: string, nbf: number, auth: string, roles: [], iss: string, exp: number, iat: number, platform: string, yuid: string } +export type entitlements = "game_minecraft" | "game_minecraft_bedrock" | "game_dungeons" | "product_minecraft" | "product_minecraft_bedrock" | "product_dungeons" + +export default class minecraft { + + readonly mcToken: string; + readonly profile: mcProfile; + readonly parent: xbox; + readonly xuid: string; + readonly exp: number; + + constructor(parent: xbox, mcToken: string, profile: mcProfile) { + this.parent = parent; + this.mcToken = mcToken; + this.profile = profile; + this.xuid = this._parseLoginToken().xuid; + this.exp = new Date().getTime() + (1000 * 60 * 60 * 23); + } + async entitlements() { + var r998 = await fetch("https://api.minecraftservices.com/minecraft/profile", { + headers: { + "Content-Type": "application/json", + Accept: "application/json", + Authorization: `Bearer ${this.mcToken}`, + }, + }); + errResponse(r998, "error.auth.minecraft.entitlements"); + const json = await r998.json() as { items: [{ name: entitlements, signature: string }] } + const r: entitlements[] = []; + json.items.forEach(e => { + r.push(e.name) + }) + return r; + } + isDemo() { + return this.profile.demo; + } + mclc() { + return { + access_token: this.mcToken, + client_token: getUUID(), + uuid: this.profile.id, + name: this.profile.name, + meta: { + xuid: this.xuid, + type: "msa", + demo: this.profile.demo + }, + user_properties: {} + } as mclcUser + } + + async refresh(force?: boolean) { + //@ts-ignore + this.parent = await this.parent.refresh(force); + if (this.validate() && !force) return this + let tkn = await this.parent.getMinecraft(); + //Copy back objects + Object.keys(tkn).forEach(e => { + this[e] = tkn[e]; + }) + return this; + } + validate() { + return this.exp > Date.now(); + } + _parseLoginToken() { + var base64Url = this.mcToken.split('.')[1]; + var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); + var jsonPayload = decodeURIComponent(Buffer.from(base64, "base64").toString("utf8").split('').map(function (c) { + return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); + }).join('')); + return JSON.parse(jsonPayload) as mcJWTDecoded; + } +} + +function getUUID() { + var result = "" + for (var i = 0; i <= 4; i++) { + result += (Math.floor(Math.random() * 16777216) + 1048576).toString(16); + if (i < 4) result += "-" + } + return result; +} \ No newline at end of file diff --git a/src/auth/social.ts b/src/auth/social.ts new file mode 100644 index 0000000..d07d585 --- /dev/null +++ b/src/auth/social.ts @@ -0,0 +1,48 @@ +import fetch from "node-fetch"; +export class xplayer { + auth: social; + score: number; + xuid: string; + gamerTag: string; + name: string; + profilePictureURL: string; + constructor(user: { id: string; settings: any[]; }, auth: social) { + this.xuid = user.id; + this.gamerTag = user.settings.find(s => s.id == "Gamertag")?.value; + this.name = user.settings.find(s => s.id == "GameDisplayName")?.value; + this.profilePictureURL = user.settings.find(s => s.id == "GameDisplayPicRaw").value; + this.score = user.settings.find(s => s.id == "Gamerscore").value; + this.auth = auth; + } + getFriends() { return this.auth.getFriends(this.xuid) } +} +export default class social { + + auth: string; + constructor(auth: string) { + this.auth = auth; + } + async getProfile(xuid?: string) { + const profile = await this.xGet("/profile/settings?settings=GameDisplayName,GameDisplayPicRaw,Gamerscore,Gamertag", xuid); + return new xplayer(profile.profileUsers[0], this); + } + async getFriends(xuid?: string) { + const friends = await this.xGet("/profile/settings/people/people?settings=GameDisplayName,GameDisplayPicRaw,Gamerscore,Gamertag", xuid) + let R: xplayer[] = []; + friends.profileUsers.forEach((element: { id: string; settings: any[]; }) => { + R.push(new xplayer(element, this)); + }); + return R; + } + async xGet(enpoint: string, xuid?: string) { + const target = xuid ? `xuid(${xuid})` : "me"; + let profileRaw = await fetch(`https://profile.xboxlive.com/users/${target}/${enpoint}`, { + headers: { + "Content-Type": "application/json", + "x-xbl-contract-version": '2', + Authorization: this.auth, + } + }); + return await profileRaw.json(); + } +} \ No newline at end of file diff --git a/src/auth/xbox.ts b/src/auth/xbox.ts new file mode 100644 index 0000000..30c5042 --- /dev/null +++ b/src/auth/xbox.ts @@ -0,0 +1,131 @@ +import { err, errResponse, lexcodes, mcProfile } from "../assets.js"; +import { auth, msAuthToken } from "./auth.js"; +import fetch from "node-fetch"; + +import social from "./social.js"; +import minecraft from "./minecraft.js"; + +export interface mcAuthToken { + username: string, + roles: [], + access_token: string + token_type: string, + expires_in: number +} +export interface xblAuthToken { + IssueInstant: string + NotAfter: string + Token: string + DisplayClaims: { xui: [{ uhs: string }] } +} + + + +export default class xbox { + readonly parent: auth; + readonly msToken: msAuthToken; + readonly xblToken: xblAuthToken; + readonly exp: number; + + constructor(parent: auth, MStoken: msAuthToken, xblToken: xblAuthToken) { + this.parent = parent; + this.msToken = MStoken; + this.xblToken = xblToken; + + this.exp = new Date().getTime() + (60 * 60 * 1000) - 1000; + } + load(code: lexcodes) { + this.parent.emit("load", code); + } + async xAuth(RelyingParty = "http://xboxlive.com") { + this.load('load.auth.xsts'); + let rxsts = await fetch("https://xsts.auth.xboxlive.com/xsts/authorize", { + method: "post", + body: JSON.stringify({ + Properties: { SandboxId: "RETAIL", UserTokens: [this.xblToken.Token] }, + RelyingParty, + TokenType: "JWT", + }), + headers: { "Content-Type": "application/json", Accept: "application/json" }, + }); + + var XSTS = await rxsts.json(); + if (XSTS.XErr) { + var ts = "error.auth.xsts" as lexcodes; + switch (XSTS.XErr) { + case 2148916233: ts = "error.auth.xsts.userNotFound"; break; + case 2148916235: ts = "error.auth.xsts.bannedCountry"; break; + case 2148916236: + case 2148916237: ts = "error.auth.xsts.child.SK"; break; + case 2148916238: ts = "error.auth.xsts.child"; break; + } + err(ts); + } + console.log(XSTS.DisplayClaims) + return `XBL3.0 x=${XSTS.DisplayClaims.xui[0].uhs};${XSTS.Token}` + } + //infxbox + async refresh(force?: boolean) { + if (this.validate() && !force) return this + let tkn = await this.parent.refresh(this.msToken); + //Copy back objects + Object.keys(tkn).forEach(e => { + this[e] = tkn[e]; + }) + return this; + } + + async getSocial() { + const header = await this.xAuth(); + const _social = new social(header) + return _social; + } + + async getMinecraft() { + const auth = await this.xAuth("rp://api.minecraftservices.com/"); + this.load('load.auth.minecraft.login') + var rlogin_with_xbox = await fetch( + "https://api.minecraftservices.com/authentication/login_with_xbox", + { + method: "post", + body: JSON.stringify({ + identityToken: auth + }), + headers: { "Content-Type": "application/json", Accept: "application/json" }, + } + ); + errResponse(rlogin_with_xbox, "error.auth.minecraft.login") + var MCauth = await rlogin_with_xbox.json() as mcAuthToken; + this.load('load.auth.minecraft.profile') + var r998 = await fetch("https://api.minecraftservices.com/minecraft/profile", { + headers: { + "Content-Type": "application/json", + Accept: "application/json", + Authorization: `Bearer ${MCauth.access_token}`, + }, + }); + errResponse(r998, "error.auth.minecraft.profile") + var MCprofile = await r998.json() as mcProfile & { error?: string }; + const profile = MCprofile.error ? { id: MCauth.username, capes: [], skins: [], name: "player", demo: true } : MCprofile; + let mc = new minecraft(this, MCauth.access_token, profile); + if (mc.isDemo()) { + this.load('load.auth.minecraft.gamepass'); + const entitlements = await mc.entitlements() + if (entitlements.includes("game_minecraft") || entitlements.includes("product_minecraft")) { + const social = await (await this.getSocial()).getProfile(); + mc = new minecraft(this, MCauth.access_token, { id: MCauth.username, capes: [], skins: [], name: social.gamerTag }); + } + } + return mc + } + validate() { + return this.exp > Date.now(); + } + /** + * Feed this into the refresh funtion in the auth object that generated it. + * @returns The refresh token + */ + save() { + return this.msToken.refresh_token; + } +} \ No newline at end of file diff --git a/src/gui/electron.ts b/src/gui/electron.ts new file mode 100644 index 0000000..da83323 --- /dev/null +++ b/src/gui/electron.ts @@ -0,0 +1,40 @@ +import { err, getDefaultWinProperties, lexcodes } from "../assets.js"; +import { auth } from "../auth/auth.js"; +import type { BrowserWindow as TBrowser } from 'electron'; +//@ts-ignore +const dynReq = (typeof __webpack_require__ === "function" ? __non_webpack_require__ : require) as NodeRequire; + +const BrowserWindow = dynReq("electron").BrowserWindow; + +if (!BrowserWindow){ + err("error.state.invalid.electron") +} + +export default (auth: auth, Windowproperties = getDefaultWinProperties()) => { + return new Promise((resolve, reject: (e: lexcodes) => void) => { + var redirect = auth.createLink(); + const mainWindow: TBrowser = new BrowserWindow(Windowproperties); + mainWindow.setMenu(null); + mainWindow.loadURL(redirect); + const contents = mainWindow.webContents; + var loading = false; + mainWindow.on("close", () => { + if (!loading) { reject("error.gui.closed") }; + }); + contents.on("did-finish-load", () => { + const loc = contents.getURL(); + if (loc.startsWith(auth.token.redirect)) { + const urlParams = new URLSearchParams(loc.substr(loc.indexOf("?") + 1)).get("code"); + if (urlParams) { + resolve(urlParams); + loading = true; + } + try { + mainWindow.close(); + } catch { + console.error("[MSMC]: Failed to close window!"); + } + }; + }); + }); +}; diff --git a/modules/gui/nwjs.js b/src/gui/nwjs.ts similarity index 55% rename from modules/gui/nwjs.js rename to src/gui/nwjs.ts index b566d35..f8fcc9f 100644 --- a/modules/gui/nwjs.js +++ b/src/gui/nwjs.ts @@ -1,27 +1,23 @@ -const MSMC = require("../.."); -const defaultProperties = { - width: 500, - height: 650, - resizable: false, - title: "Microsoft Login" -} +import { getDefaultWinProperties, lexcodes } from "../assets.js"; +import { auth } from "../auth/auth.js"; -module.exports = (token, updates = () => { }, Windowproperties = defaultProperties) => { - return new Promise(resolve => { - var redirect = MSMC.createLink(token); +export default (auth: auth, Windowproperties = getDefaultWinProperties()) => { + return new Promise((resolve, rejects: (e: lexcodes) => void) => { + var redirect = auth.createLink(); + //@ts-ignore nw.Window.open(redirect, Windowproperties, function (new_win) { new_win.on('close', function () { - resolve({ type: "Cancelled", translationString:"Cancelled.GUI" }) + rejects('error.gui.closed') new_win.close(true); }); new_win.on('loaded', function () { const loc = new_win.window.location.href; - if (loc.startsWith(token.redirect)) { + if (loc.startsWith(auth.token.redirect)) { const urlParams = new URLSearchParams(loc.substr(loc.indexOf("?") + 1)).get("code"); if (urlParams) { - resolve(MSMC.authenticate(urlParams, token, updates)); + resolve(urlParams); } else { - resolve({ type: "Cancelled", translationString:"Cancelled.Back" }); + rejects('error.gui.closed'); } try { new_win.close(true); diff --git a/modules/gui/raw.js b/src/gui/raw.ts similarity index 74% rename from modules/gui/raw.js rename to src/gui/raw.ts index f281056..024cad8 100644 --- a/modules/gui/raw.js +++ b/src/gui/raw.ts @@ -1,22 +1,16 @@ -/** - * EXPERIMENTAL! - */ -const MSMC = require("../.."); -const BE = require("../backEnd"); -const path = require('path') -const fs = require('fs') -const os = require("os"); +import path from 'path'; +import fs from 'fs'; +import os from "os"; const temp = path.join(os.tmpdir(), "msmc"); -const { spawn, execSync: exec, ChildProcess } = require('child_process'); - +import { spawn, execSync as exec, ChildProcessWithoutNullStreams } from 'child_process'; +import { err, getDefaultWinProperties, lexcodes } from '../assets.js'; +import auth from '../auth/auth'; +import fetch from 'node-fetch'; var firefox = false; -const defaultProperties = { - width: 500, - height: 650, -} -var start + +var start: string console.log("[MSMC]: OS Type => " + os.type()); switch (os.type()) { case 'Windows_NT': @@ -30,9 +24,9 @@ switch (os.type()) { console.log("reg query \"" + locW + compatibleW[i2] + "\"") var out = exec("\"C:\\Windows\\System32\\reg.exe\" query \"" + locW + compatibleW[i2] + "\"").toString(); if (!out.startsWith("ERROR")) { - out = out.substr(out.indexOf("REG_SZ") + "REG_SZ".length).trim(); + out = out.substring(out.indexOf("REG_SZ") + "REG_SZ".length).trim(); if (out.indexOf("\n") > 0) - out = out.substr(0, out.indexOf("\n") - 1); + out = out.substring(0, out.indexOf("\n") - 1); if (fs.existsSync(out)) { start = out; break WE; } else console.log("[MSMC]: cannot find " + out) } @@ -68,14 +62,14 @@ switch (os.type()) { console.error("[MSMC]: No compatible browser was found") } } -/** - * @param {ChildProcess} browser - */ -function browserLoop(token, port, updates, browser) { - return new Promise((resolve) => { + +function browserLoop(auth: auth, port: string, browser: ChildProcessWithoutNullStreams) { + + return new Promise((resolve, error: (e: lexcodes) => void) => { const call = () => { try { clearInterval(f3); + //@ts-ignore process.removeListener("exit", call); if (os.type() == "Windows_NT") { exec("taskkill /pid " + browser.pid); @@ -86,39 +80,38 @@ function browserLoop(token, port, updates, browser) { console.error("[MSMC]: Failed to close window!"); } } - const end = process.on("exit", call) + process.on("exit", call) const f3 = setInterval(() => { - BE.getFetch()("http://127.0.0.1:" + port + "/json/list").then(r => r.json()).then(out => { + fetch("http://127.0.0.1:" + port + "/json/list").then(r => r.json()).then(out => { for (var i = 0; i < out.length; i++) { const loc = out[i].url; - if (loc && loc.startsWith(token.redirect)) { + if (loc && loc.startsWith(auth.token.redirect)) { const urlParams = new URLSearchParams(loc.substr(loc.indexOf("?") + 1)).get("code"); if (urlParams) - resolve(MSMC.authenticate(urlParams, token, updates)); + resolve(urlParams); else - resolve({ type: "Cancelled", translationString: "Cancelled.Back" }); + error("error.gui.closed"); call(); } } }).catch((err) => { call(); - resolve({ type: "Cancelled", translationString: "Cancelled.GUI" }) + console.error("[msmc]: " + err) + error("error.gui.closed"); }) }, 500); }); } - -module.exports = (token, updates = () => { }, Windowproperties = defaultProperties) => { +export default (auth: auth, Windowproperties = getDefaultWinProperties()) => { const cmd = Windowproperties.browserCMD ? Windowproperties.browserCMD : start; if (!cmd) { - throw new Error("[MSMC]: Error : no chromium browser was set, cannot continue!"); + err("error.gui.raw.noBrowser"); } - console.warn("[MSMC]: This setting is experimental"); - console.warn("[MSMC]: Using \"" + cmd + "\""); - var redirect = MSMC.createLink(token); - return new Promise(resolve => { - var browser; + console.log("[MSMC]: Using \"" + cmd + "\""); + var redirect = auth.createLink(); + return new Promise((resolve, error) => { + var browser: ChildProcessWithoutNullStreams; if (firefox || Windowproperties.firefox) { console.log("[MSMC]: Using firefox fallback {Linux only!}"); if (fs.existsSync(temp)) exec("rm -R " + temp); fs.mkdirSync(temp); @@ -126,16 +119,16 @@ module.exports = (token, updates = () => { }, Windowproperties = defaultProperti } else browser = spawn(cmd, ["--disable-restore-session-state", "--disable-first-run-ui", "--disable-component-extensions-with-background-pages", "--no-first-run", "--disable-extensions", "--window-size=" + Windowproperties.width + "," + Windowproperties.height, "--remote-debugging-port=0", "--no-default-browser-check", "--user-data-dir=" + temp, "--force-app-mode", "--app=" + redirect]); var firstrun = true; - const ouput = (out) => { + const ouput = (out: { toString: () => any; }) => { const cout = String(out.toString()).toLocaleLowerCase().trim(); console.log("[MSMC][Browser]: " + cout) if (firstrun && cout.startsWith("devtools listening on ws://")) { firstrun = false; - var data = cout.substr("devtools listening on ws://".length); + var data = cout.substring("devtools listening on ws://".length); const n = data.indexOf(":") + 1; - const port = data.substr(n, data.indexOf("/") - n); + const port = data.substring(n, data.indexOf("/")); console.log("[MSMC]: Debug hook => http://127.0.0.1:" + port); - resolve(browserLoop(token, port, updates, browser)); + browserLoop(auth, port, browser).then(resolve).catch(error) } } if (!Windowproperties.suppress) { diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..7f85ad8 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,12 @@ +import auth from "./auth/auth.js"; + +import * as assets from "./assets.js"; +import { wrapError, lst } from "./assets.js"; + +import social from "./auth/social"; +import type xbox from "./auth/xbox.js"; +import type minecraft from "./auth/minecraft.js"; + + +export { social, auth, assets, wrapError, lst }; +export type { xbox, minecraft }; \ No newline at end of file diff --git a/src/tests.ts b/src/tests.ts new file mode 100644 index 0000000..9fba841 --- /dev/null +++ b/src/tests.ts @@ -0,0 +1,7 @@ +import { execSync } from "child_process"; +console.log("Testing raw framework") +execSync('npm run start',{cwd:"tests/raw"}) +console.log("Testing nwjs framework") +execSync('npm run start',{cwd:"tests/nwjs"}) +console.log("Testing electron framework") +execSync('npm run start',{cwd:"tests/electron"}) \ No newline at end of file diff --git a/tests/electron/index.html b/tests/electron/index.html index 023b68f..e55d1c3 100755 --- a/tests/electron/index.html +++ b/tests/electron/index.html @@ -1,18 +1,43 @@ - - - - - - Hello World! - - -

Hello World!

- We are using Node.js , - Chromium , - and Electron . - - - - + + + + + + + Hello World! + + + +

MIT License

+

+ Copyright (c) 2021 Hanro
+ + Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+ + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE. +

+
+ We are using Node.js , + Chromium , + and Electron . + + + + + \ No newline at end of file diff --git a/tests/electron/main.js b/tests/electron/main.js index 9001a66..2fbaf97 100755 --- a/tests/electron/main.js +++ b/tests/electron/main.js @@ -18,27 +18,21 @@ function createWindow() { // Open the DevTools. // mainWindow.webContents.openDevTools() } -const { fastLaunch, getMCLC } = require("msmc"); +const { auth, wrapError } = require('msmc'); // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.whenReady().then(() => { - createWindow() - - console.log("Testing NWJS. This should test most of the underlying code") - - fastLaunch('electron', console.log).then(async L => { - console.log(L); - - const mclc = getMCLC().getAuth(L); - console.log(mclc); - const r = await getMCLC().refresh(mclc, console.log); - console.log(r); - - console.log("Completed tests!"); - + const msmc = new auth('select_account'); + msmc.on('load', console.log).luanch('electron').then(async e => { + const t = await e.getMinecraft() + console.log(t.mclc()) + const a = await t.refresh(true) + console.log(t.mclc()) + }).catch(e => { + console.log(wrapError(e)) }) app.on('activate', function () { diff --git a/tests/nwjs/index.js b/tests/nwjs/index.js index 9582ce4..c069c88 100755 --- a/tests/nwjs/index.js +++ b/tests/nwjs/index.js @@ -1,6 +1,20 @@ -const { fastLaunch, getMCLC } = require("msmc"); +const { readFileSync } = require("fs"); +const { auth, wrapError } = require("msmc"); +const { assets } = require("msmc"); +assets.loadLexiPack("../../lexipacks/french.json") console.log("Testing NWJS. This should test most of the underlying code") +new auth('select_account').on('load',console.log).luanch('nwjs').then(async e => { + const t = await e.getMinecraft() + console.log(t.mclc()) + const a = await t.refresh(true) + console.log(t.mclc()) +}).catch(e => { + console.log(wrapError(e)) +}) + +/* + fastLaunch('nwjs', console.log).then(async L => { console.log(L); @@ -12,5 +26,5 @@ fastLaunch('nwjs', console.log).then(async L => { console.log("Completed tests!"); }) - +*/ diff --git a/tests/nwjs/package.json b/tests/nwjs/package.json index 5fe9b18..3e375fa 100755 --- a/tests/nwjs/package.json +++ b/tests/nwjs/package.json @@ -7,9 +7,9 @@ }, "dependencies": { "msmc": "file:../../.", - "nw": "0.59.1-sdk" + "nw": "^0.59.1-sdk" }, "scripts": { "start": "nw" } -} \ No newline at end of file +} diff --git a/tests/raw/package.json b/tests/raw/package.json index 4a2377c..c82205e 100755 --- a/tests/raw/package.json +++ b/tests/raw/package.json @@ -6,7 +6,8 @@ "nwjs-types": "^1.0.0" }, "dependencies": { - "msmc": "file:../../." + "@types/node": "^17.0.35", + "msmc": "file:../../" }, "scripts": { "start": "node tests.mjs" diff --git a/tests/raw/tests.mjs b/tests/raw/tests.mjs index b3cfa67..c4cbbab 100644 --- a/tests/raw/tests.mjs +++ b/tests/raw/tests.mjs @@ -1,14 +1,18 @@ -import { fastLaunch, getFriendlist, getMCLC } from "msmc"; -import msmc from "msmc"; -msmc.mkES6(); -console.log("Testing Raw. This should test most of the underlying code") -const L = await fastLaunch('raw', console.log); -const P = await L.getXbox(console.log); -console.log(P, await P.getFriends(), await getFriendlist(P.getAuth)); - +import msmc, { wrapError,assets } from "msmc"; +console.log(msmc) +const auth = new msmc.auth(); +assets.loadLexiPack("..","..","lexipacks","afrikaans.json") +console.log(auth.createLink()) +auth.on('load', console.log).luanch('raw').then(async e => { + const t = await e.getMinecraft() + console.log(t.mclc()) +}).catch((e) => { + console.log(wrapError(e)) +}) let R = []; -console.log(msmc.getXbox().getXProfile(P.getAuth)) + +//console.log(msmc.getXbox().getXProfile(P.getAuth)) /* console.log(L); diff --git a/tsconfig.dist.json b/tsconfig.dist.json new file mode 100644 index 0000000..53e96a3 --- /dev/null +++ b/tsconfig.dist.json @@ -0,0 +1,23 @@ +{ // Change this to match your project + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist" + ], + "compilerOptions": { + "outDir": "dist", + "moduleResolution": "Node", + "target": "ESNext", + "allowJs": true, + "importHelpers": true, + "esModuleInterop": true, + "module": "CommonJS", + "rootDir": "src/", + "removeComments": true, + + // Generate d.ts files + + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d5f103c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ // Change this to match your project + "extends": "./tsconfig.dist.json", + "compilerOptions": { + "sourceMap": true, + "outDir": "dist", + "watch": true, + "declarationMap": true, + "declaration": true, + "declarationDir": "types" + } +} \ No newline at end of file diff --git a/tsconfig.types.json b/tsconfig.types.json new file mode 100644 index 0000000..ea96795 --- /dev/null +++ b/tsconfig.types.json @@ -0,0 +1,9 @@ +{ // Change this to match your project + "extends": "./tsconfig.dist.json", + "compilerOptions": { + "removeComments": false, + "emitDeclarationOnly": true, + "declaration": true, + "declarationDir": "types" + } +} \ No newline at end of file