-
-
Save Plagiatus/ce5f18bc010395fc45d8553905e10f55 to your computer and use it in GitHub Desktop.
| import { XMLHttpRequest } from "xmlhttprequest"; | |
| interface AuthorizationTokenResponse { | |
| token_type: string, | |
| expires_in: number, | |
| scope: string, | |
| access_token: string, | |
| refresh_token: string, | |
| user_id: string, | |
| foci: string | |
| } | |
| interface XboxServiceTokenResponse { | |
| IssueInstant: string, | |
| NotAfter: string, | |
| Token: string, | |
| DisplayClaims: DisplayClaim; | |
| } | |
| interface MCTokenResponse { | |
| username: string, | |
| roles: any[], | |
| access_token: string, | |
| token_type: string, | |
| expires_in: number | |
| } | |
| interface MCUserInfo { | |
| id: string, | |
| name: string, | |
| skins: MCSkinInfo[], | |
| capes: MCCapeInfo[] | |
| } | |
| interface MCInfo { | |
| id: string, | |
| state: "ACTIVE" | "INACTIVE", | |
| url: string, | |
| } | |
| interface MCSkinInfo extends MCInfo { | |
| variant: string, | |
| } | |
| interface MCCapeInfo extends MCInfo { | |
| alias: string, | |
| } | |
| interface AuthInfo { | |
| auth_token: AuthorizationTokenResponse, | |
| xbox_token: XboxServiceTokenResponse, | |
| xsts_token: XboxServiceTokenResponse, | |
| mc_token: MCTokenResponse, | |
| mc_info: MCUserInfo | |
| } | |
| interface DisplayClaim { | |
| xui: { | |
| uhs: string; | |
| }[] | |
| } | |
| export default class AuthenticationHandler { | |
| private clientId: string; | |
| private clientSecret: string; | |
| private redirectUri: string; | |
| constructor(clientID: string, clientSecret: string, redirectUri: string) { | |
| if (!clientID) throw new Error("clientID is required"); | |
| this.clientId = clientID; | |
| if (!clientSecret) throw new Error("clientSecret is required"); | |
| this.clientSecret = clientSecret; | |
| if (!redirectUri) throw new Error("redirectUri is required"); | |
| this.redirectUri = redirectUri; | |
| } | |
| public get forwardUrl(): string { | |
| let url: string = `https://login.live.com/oauth20_authorize.srf?client_id=${this.clientId}&response_type=code&redirect_uri=${this.redirectUri}&scope=XboxLive.signin%20offline_access`; | |
| return url; | |
| } | |
| public async getAuthCodes(code: string, refresh: boolean = false): Promise<AuthInfo> { | |
| if (!code) throw Error("No Code provided."); | |
| let authToken: AuthorizationTokenResponse = await this.authCodeToAuthToken(code, refresh); | |
| let xbl: XboxServiceTokenResponse = await this.authTokenToXBL(authToken); | |
| let xsts: XboxServiceTokenResponse = await this.xblToXsts(xbl); | |
| let mcToken: MCTokenResponse = await this.xstsToMc(xsts); | |
| let mcInfo: MCUserInfo = await this.getMCInfo(mcToken); | |
| return { | |
| auth_token: authToken, | |
| mc_info: mcInfo, | |
| mc_token: mcToken, | |
| xbox_token: xbl, | |
| xsts_token: xsts | |
| } | |
| } | |
| private async authCodeToAuthToken(code: string, refresh: boolean): Promise<AuthorizationTokenResponse> { | |
| let request = new XMLHttpRequest(); | |
| let data = "client_id=" + this.clientId + | |
| "&client_secret=" + this.clientSecret + | |
| "&redirect_uri=" + this.redirectUri; | |
| if (refresh) { | |
| data += "&refresh_token=" + code + | |
| "&grant_type=refresh_token"; | |
| } else { | |
| data += "&code=" + code + | |
| "&grant_type=authorization_code"; | |
| } | |
| request.open("POST", "https://login.live.com/oauth20_token.srf", false); | |
| request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); | |
| let promise: Promise<AuthorizationTokenResponse> = this.waitForRequestText(request); | |
| request.send(data); | |
| return promise; | |
| } | |
| private async authTokenToXBL(authToken: AuthorizationTokenResponse): Promise<XboxServiceTokenResponse> { | |
| let request = new XMLHttpRequest(); | |
| let data = `{ | |
| "Properties": { | |
| "AuthMethod": "RPS", | |
| "SiteName": "user.auth.xboxlive.com", | |
| "RpsTicket": "d=${authToken.access_token}" | |
| }, | |
| "RelyingParty": "http://auth.xboxlive.com", | |
| "TokenType": "JWT" | |
| }`; | |
| request.open("POST", "https://user.auth.xboxlive.com/user/authenticate", false); | |
| request.setRequestHeader("Content-Type", "application/json"); | |
| request.setRequestHeader("Accept", "application/json"); | |
| let promise: Promise<XboxServiceTokenResponse> = this.waitForRequestText(request); | |
| request.send(data); | |
| return promise; | |
| } | |
| private async xblToXsts(token: XboxServiceTokenResponse): Promise<XboxServiceTokenResponse> { | |
| let request = new XMLHttpRequest(); | |
| let data = `{ | |
| "Properties": { | |
| "SandboxId": "RETAIL", | |
| "UserTokens": [ | |
| "${token.Token}" | |
| ] | |
| }, | |
| "RelyingParty": "rp://api.minecraftservices.com/", | |
| "TokenType": "JWT" | |
| }`; | |
| request.open("POST", "https://xsts.auth.xboxlive.com/xsts/authorize", false); | |
| request.setRequestHeader("Content-Type", "application/json"); | |
| request.setRequestHeader("Accept", "application/json"); | |
| let promise: Promise<XboxServiceTokenResponse> = this.waitForRequestText(request); | |
| request.send(data); | |
| return promise; | |
| } | |
| private async xstsToMc(token: XboxServiceTokenResponse): Promise<MCTokenResponse> { | |
| let request = new XMLHttpRequest(); | |
| let data = `{ | |
| "identityToken": "XBL3.0 x=${token.DisplayClaims.xui[0].uhs};${token.Token}" | |
| }`; | |
| request.open("POST", "https://api.minecraftservices.com/authentication/login_with_xbox", false); | |
| request.setRequestHeader("Content-Type", "application/json"); | |
| request.setRequestHeader("Accept", "application/json"); | |
| let promise: Promise<MCTokenResponse> = this.waitForRequestText(request); | |
| request.send(data); | |
| return promise; | |
| } | |
| private async getMCInfo(mc_token: MCTokenResponse): Promise<MCUserInfo> { | |
| let request = new XMLHttpRequest(); | |
| request.open("GET", "https://api.minecraftservices.com/minecraft/profile"); | |
| request.setRequestHeader("Authorization", "Bearer " + mc_token.access_token); | |
| let promise: Promise<MCUserInfo> = this.waitForRequestText(request); | |
| request.send(); | |
| return promise; | |
| } | |
| private async waitForRequestText(req: XMLHttpRequest): Promise<any> { | |
| return new Promise((resolve, reject) => { | |
| req.addEventListener("readystatechange", () => { | |
| if (req.readyState == 4) { | |
| if (req.status == 200) { | |
| resolve(JSON.parse(req.responseText)); | |
| } else { | |
| console.error(req.status, req.statusText); | |
| reject(); | |
| } | |
| } | |
| }); | |
| }); | |
| } | |
| } |
nah you're fine. i just recommend switching one day
Thanks for creating this! Microsoft seems to have a case of bad documentation so nothing helped until i found this.
@Redstoneguy129 https://wiki.vg/ is the best documentation for this.
@javajuicer it's a nodejs implementation, so you'll need to install the xmlhttprequest module. See https://www.npmjs.com/package/xmlhttprequest.
Though xmlhttprequest is sorta outdated and unmaintained, so you might want to substitute it for a more modern approach like the xhr2 module or the natively implemented fetch: https://nodejs.org/en/blog/announcements/v18-release-announce/#fetch-experimental
@Redstoneguy129 https://wiki.vg/ is the best documentation for this.
The wiki is offline :(
Waybackmachine has saved it though:
https://web.archive.org/web/20241130182011/https://wiki.vg/Main_Page
@FelixSche The wiki is offline :(
It was sunset and its contents merged into the minecraft wiki.

@jluims You can use any other request option that you want, all the relevant data and headers are layed out above. I used xmlhttprequest because it works for me. Nobody says you have to copy my code 1:1. ;)