Difference between revisions of "Microsoft Authentication Scheme"

From wiki.vg
Jump to navigation Jump to search
m (→‎Checking Game Ownership: Fix formatting)
 
(58 intermediate revisions by 31 users not shown)
Line 1: Line 1:
 
Minecraft is moving to Microsoft accounts. Starting December 2020, all new Accounts already use the new system, old accounts will be migrated later, see [https://www.minecraft.net/en-us/article/java-edition-moving-house this blog post]
 
Minecraft is moving to Microsoft accounts. Starting December 2020, all new Accounts already use the new system, old accounts will be migrated later, see [https://www.minecraft.net/en-us/article/java-edition-moving-house this blog post]
  
There are multiple steps and different token required, but in the end, you get a normal minecraft token back. Launching the game itself hasn't changed.
+
There are multiple steps and different tokens required, but in the end, you get a normal Minecraft token back. Launching the game itself hasn't changed.
  
 +
== Microsoft OAuth2 Flow ==
  
== Microsoft OAuth Flow ==
+
Prior to any of these steps, you will first need to obtain an OAuth 2.0 client ID by [https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app creating a Microsoft Azure application]. You will ''not'' need to obtain a client secret.
  
In the first step, we are logging into the microsoft account. This has to be done in a browser/webview! Other redirect urls have not been tested. The client id is hardcoded, it's Minecrafts id.
+
You can then use the [https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow OAuth2 authorization code flow] to obtain an access token. You'll need to present the user with a login page that, once completed, will redirect to a specified URL with the token in the query parameters. In non-web applications this typically involves spinning up a temporary HTTP server to handle the redirect. If you'd rather not do that, consider using the (slightly less automatic) [https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code device code flow] instead.
  
https://login.live.com/oauth20_authorize.srf
+
In any case, you'll need to include <code>XboxLive.signin</code> in the <code>scope</code> parameter of the authorization request; otherwise the next endpoint will complain, and not in a helpful way.
  ?client_id=00000000402b5328
 
  &response_type=code
 
  &scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL
 
  &redirect_uri=https%3A%2F%2Flogin.live.com%2Foauth20_desktop.srf
 
  
Example of the login page: https://i.imgur.com/gy8uKGs.png (TODO: embed image)
+
According  to [https://help.minecraft.net/hc/en-us/articles/16254801392141p this support Article], new created Azure Apps must apply for the Permission to use the Minecraft API using [https://aka.ms/mce-reviewappid this form]. If your App don't have the Permission <code>api.minecraftservices.com</code> will return a 403.
  
The user will be prompted to enter username (E-Mail, Skype ID, Phone number, whatever) and their password. If those are legal, the user will be redirected. The user doesn't need to own MC, that check comes way later!
+
<i>Note: You <b>must</b> use the <code>consumers</code> AAD tenant to sign in with the <code>XboxLive.signin</code> scope. Using an Azure AD tenant ID or the <code>common</code> scope will just give errors. This also means you cannot sign in with users that are in the AAD tenant, only with consumer Microsoft accounts.</i>
  
The redirect will looks something like this
+
== Authenticate with Xbox Live ==
https://login.live.com/oauth20_desktop.srf?code=codegoeshere&lc=1033
 
  
You have to extract the code param, it's your Microsoft Authorization Code.
+
Now that we are authenticated with Microsoft, we can authenticate with Xbox Live.
  
== Authorization Code -> Authorization Token ==
+
To do that, we send
 
 
The next step is to get a auth token from the auth code. This isn't done in the browser for security reasons.
 
POST https://login.live.com/oauth20_token.srf
 
Content:
 
Map<Object, Object> data = Map.of(
 
    "client_id", "00000000402b5328", // minecrafts client id again
 
    "code", authcode, // the code from step 1
 
    "grant_type", "authorization_code",
 
    "redirect_uri", "https://login.live.com/oauth20_desktop.srf",
 
    "scope", "service::user.auth.xboxlive.com::MBI_SSL"
 
);
 
 
 
Don't forget to set <code>Content-Type: application/x-www-form-urlencoded</code>
 
 
 
The response will look like this
 
{
 
  "token_type":"bearer",
 
  "expires_in":86400,
 
  "scope":"service::user.auth.xboxlive.com::MBI_SSL",
 
  "access_token":"token here",
 
  "refresh_token":"M.R3_BAY.token here",
 
  "user_id":"889ed4a3d844f672",
 
  "foci":"1"
 
}
 
 
 
We care about the access_token here. (TODO: check what we can do with the refresh token)
 
 
 
== Authenticate with XBL ==
 
 
 
Now that we are authenticated with microsoft, we can authenticate to xbox live.
 
  
To do that, we send
+
<syntaxhighlight lang="json" line='line'>
 
  POST https://user.auth.xboxlive.com/user/authenticate
 
  POST https://user.auth.xboxlive.com/user/authenticate
 
  {
 
  {
Line 61: Line 27:
 
         "AuthMethod": "RPS",
 
         "AuthMethod": "RPS",
 
         "SiteName": "user.auth.xboxlive.com",
 
         "SiteName": "user.auth.xboxlive.com",
         "RpsTicket": "access_token" // your access token from step 2 here
+
         "RpsTicket": "d=<access token>" // your access token from the previous step here
 
     },
 
     },
 
     "RelyingParty": "http://auth.xboxlive.com",
 
     "RelyingParty": "http://auth.xboxlive.com",
 
     "TokenType": "JWT"
 
     "TokenType": "JWT"
 
  }
 
  }
 +
</syntaxhighlight>
  
Again, it will complain if you don't set <code>Content-Type: application/json</code> and <code>Accept: application/json</code>
+
Again, it will complain if you don't set <code>Content-Type: application/json</code> and <code>Accept: application/json</code>.
 +
It will also complain if your SSL implementation does not support SSL renegotiations.
  
 
The response will look like this:
 
The response will look like this:
 +
 +
<syntaxhighlight lang="json" line='line'>
 
  {
 
  {
 
   "IssueInstant":"2020-12-07T19:52:08.4463796Z",
 
   "IssueInstant":"2020-12-07T19:52:08.4463796Z",
Line 77: Line 47:
 
       "xui":[
 
       "xui":[
 
         {
 
         {
             "uhs":"uhs" // save this
+
             "uhs":"userhash" // save this
 
         }
 
         }
 
       ]
 
       ]
 
   }
 
   }
 
  }
 
  }
 +
</syntaxhighlight>
  
We need to save token and uhs. I have no idea what uhs stands for. (TODO: find out)
+
== Obtain XSTS token for Minecraft ==
  
== Authenticate with XSTS ==
+
Now that we are authenticated with XBL, we need to get a XSTS token, we can use to login to Minecraft.
 
 
Now that we are authenticated with XBL, we need to get a XSTS token, we can use to login to minecraft.
 
  
 +
<syntaxhighlight lang="json" line='line'>
 
  POST https://xsts.auth.xboxlive.com/xsts/authorize
 
  POST https://xsts.auth.xboxlive.com/xsts/authorize
 
  {
 
  {
Line 100: Line 70:
 
     "TokenType": "JWT"
 
     "TokenType": "JWT"
 
  }
 
  }
 +
</syntaxhighlight>
  
Again, set content type and accept to json.
+
Again, set content type and accept to json and ensure SSL renegotiation is supported by your client.
 +
 
 +
<blockquote>
 +
''Note:'' When trying to get the XSTS token for the '''[[Bedrock_Realms|bedrock realms]] API''', you need to change the following: 
 +
<syntaxhighlight lang="json">"RelyingParty": "https://pocket.realms.minecraft.net/"</syntaxhighlight>
 +
 
 +
also you can stop at this point, as the [[Bedrock_Realms|bedrock realms]] API uses the XSTS token directly instead of a seperate auth scheme.
 +
</blockquote>
  
 
Response will look like this:
 
Response will look like this:
 +
 +
<syntaxhighlight lang="json" line='line'>
 
  {
 
  {
 
   "IssueInstant":"2020-12-07T19:52:09.2345095Z",
 
   "IssueInstant":"2020-12-07T19:52:09.2345095Z",
Line 111: Line 91:
 
       "xui":[
 
       "xui":[
 
         {
 
         {
             "uhs":"" // same as last request
+
             "uhs":"userhash" // same as last request
 
         }
 
         }
 
       ]
 
       ]
 
   }
 
   }
 
}
 
}
 +
</syntaxhighlight>
 +
 +
The endpoint can return a 401 error with the below response:
 +
 +
<syntaxhighlight lang="json" line='line'>
 +
{
 +
    "Identity":"0",
 +
    "XErr":2148916238,
 +
    "Message":"",
 +
    "Redirect":"https://start.ui.xboxlive.com/AddChildToFamily"
 +
}
 +
</syntaxhighlight>
 +
 +
The Redirect parameter usually will not resolve or go anywhere in a browser, likely they're targeting Xbox consoles.
 +
 +
Noted XErr codes and their meanings:
 +
 +
* '''2148916233''': The account doesn't have an Xbox account. Once they sign up for one (or login through minecraft.net to create one) then they can proceed with the login. This shouldn't happen with accounts that have purchased Minecraft with a Microsoft account, as they would've already gone through that Xbox signup process.
 +
* '''2148916235''': The account is from a country where Xbox Live is not available/banned
 +
* '''2148916236''': The account needs adult verification on Xbox page. (South Korea)
 +
* '''2148916237''': The account needs adult verification on Xbox page. (South Korea)
 +
* '''2148916238''': The account is a child (under 18) and cannot proceed unless the account is added to a Family by an adult. This only seems to occur when using a custom Microsoft Azure application. When using the Minecraft launchers client id, this doesn't trigger.
  
 
== Authenticate with Minecraft ==
 
== Authenticate with Minecraft ==
  
Now we can finally start talking to minecraft. The XSTS token from the last request allows us to authenticate to minecraft using
+
Now we can finally start talking to Minecraft. The XSTS token from the last request allows us to authenticate with Minecraft using
 +
 
 +
<syntaxhighlight lang="json" line='line'>
 
  POST https://api.minecraftservices.com/authentication/login_with_xbox
 
  POST https://api.minecraftservices.com/authentication/login_with_xbox
 
  {
 
  {
     "identityToken": "XBL3.0 x=<uhs>;<xsts_token>"
+
     "identityToken": "XBL3.0 x=<userhash>;<xsts_token>"
 
  }
 
  }
 +
</syntaxhighlight>
  
 
Response:
 
Response:
 +
<syntaxhighlight lang="json" line='line'>
 
  {
 
  {
 
   "username" : "some uuid", // this is not the uuid of the account
 
   "username" : "some uuid", // this is not the uuid of the account
Line 133: Line 139:
 
   "expires_in" : 86400
 
   "expires_in" : 86400
 
  }
 
  }
 +
</syntaxhighlight>
  
This access token allows us to launch the game, but, we haven't actually checked if the account owns the game. Everything until here works with a normal microsoft account!
+
This access token allows us to launch the game, but, we haven't actually checked if the account owns the game. Everything until here works with a normal Microsoft account!
  
 
== Checking Game Ownership ==
 
== Checking Game Ownership ==
Line 141: Line 148:
 
  GET https://api.minecraftservices.com/entitlements/mcstore
 
  GET https://api.minecraftservices.com/entitlements/mcstore
  
The access token goes into the auth header: <code>Authorization: Bearer token</code>
+
The access token goes into the auth header: <code>Authorization: Bearer <Minecraft Access Token></code>. (Keep in mind that <code>Bearer </code> is actually the prefix you must include!)
  
 
If the account owns the game, the response will look like this:
 
If the account owns the game, the response will look like this:
 +
 +
<syntaxhighlight lang="json" line='line'>
 
  {
 
  {
 
   "items" : [ {
 
   "items" : [ {
Line 155: Line 164:
 
   "keyId" : "1"
 
   "keyId" : "1"
 
  }
 
  }
 +
</syntaxhighlight>
  
 
The first jwts contain the values:
 
The first jwts contain the values:
 +
<syntaxhighlight lang="json" line='line'>
 
  {
 
  {
 
   "typ": "JWT",
 
   "typ": "JWT",
Line 165: Line 176:
 
   "name": "product_minecraft"
 
   "name": "product_minecraft"
 
  }.[Signature]
 
  }.[Signature]
 +
</syntaxhighlight>
  
 
the last jwt looks like this decoded:
 
the last jwt looks like this decoded:
 +
 +
<syntaxhighlight lang="json" line='line'>
 
  {
 
  {
 
   "typ": "JWT",
 
   "typ": "JWT",
Line 182: Line 196:
 
   "signerId": "2535416586892404"
 
   "signerId": "2535416586892404"
 
  }.[Signature]
 
  }.[Signature]
 +
</syntaxhighlight>
 +
If the account doesn't own the game, the items array will be empty.
 +
 +
Note that Xbox Game Pass users don't technically own the game, and therefore will not show any ownership here, but will indeed have a Minecraft profile attached to their account.
  
If the account doesn't own the game, the items array will be empty.
+
Note that the signature should always be checked with the public key from Mojang to verify that it is a legitimate response from the official servers:
 +
<syntaxhighlight>
 +
-----BEGIN PUBLIC KEY-----
 +
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtz7jy4jRH3psj5AbVS6W
 +
NHjniqlr/f5JDly2M8OKGK81nPEq765tJuSILOWrC3KQRvHJIhf84+ekMGH7iGlO
 +
4DPGDVb6hBGoMMBhCq2jkBjuJ7fVi3oOxy5EsA/IQqa69e55ugM+GJKUndLyHeNn
 +
X6RzRzDT4tX/i68WJikwL8rR8Jq49aVJlIEFT6F+1rDQdU2qcpfT04CBYLM5gMxE
 +
fWRl6u1PNQixz8vSOv8pA6hB2DU8Y08VvbK7X2ls+BiS3wqqj3nyVWqoxrwVKiXR
 +
kIqIyIAedYDFSaIq5vbmnVtIonWQPeug4/0spLQoWnTUpXRZe2/+uAKN1RY9mmaB
 +
pRFV/Osz3PDOoICGb5AZ0asLFf/qEvGJ+di6Ltt8/aaoBuVw+7fnTw2BhkhSq1S/
 +
va6LxHZGXE9wsLj4CN8mZXHfwVD9QG0VNQTUgEGZ4ngf7+0u30p7mPt5sYy3H+Fm
 +
sWXqFZn55pecmrgNLqtETPWMNpWc2fJu/qqnxE9o2tBGy/MqJiw3iLYxf7U+4le4
 +
jM49AUKrO16bD1rdFwyVuNaTefObKjEMTX9gyVUF6o7oDEItp5NHxFm3CqnQRmch
 +
HsMs+NxEnN4E9a8PDB23b4yjKOQ9VHDxBxuaZJU60GBCIOF9tslb7OAkheSJx5Xy
 +
EYblHbogFGPRFU++NrSQRX0CAwEAAQ==
 +
-----END PUBLIC KEY-----
 +
</syntaxhighlight>
 +
See the JWT standard[https://auth0.com/docs/tokens/json-web-tokens/validate-json-web-tokens] for more details.
 +
 
 +
In case the public key ever changes, it can be extracted from the launcher library:
 +
 
 +
<syntaxhighlight lang="bash">
 +
strings ~/.minecraft/launcher/liblauncher.so > launcher-strings.txt
 +
</syntaxhighlight>
 +
 
 +
The created file <code>launcher-strings.txt</code> will include 2 strings which begin with <code>-----BEGIN PUBLIC KEY-----</code> and end with <code>-----END PUBLIC KEY-----</code>.
 +
The first key seems to be the one used for the JWT tokens, use of the second key is unknown.
  
== Get the profile ==
+
== Getting the profile ==
  
Now that we know that the account owns the game, lets get his profile so we get uuid:
+
Now that we know that the account owns the game, we can get their profile in order to fetch the UUID:
 
  GET https://api.minecraftservices.com/minecraft/profile
 
  GET https://api.minecraftservices.com/minecraft/profile
  
 
Again, the access token goes into the auth header: <code>Authorization: Bearer token</code>
 
Again, the access token goes into the auth header: <code>Authorization: Bearer token</code>
  
The responce will look like this, if the account owns the game:
+
The response will look like this, if the account owns the game:
 +
 
 +
<syntaxhighlight lang="json" line='line'>
 
  {
 
  {
 
   "id" : "986dec87b7ec47ff89ff033fdb95c4b5", // the real uuid of the account, woo
 
   "id" : "986dec87b7ec47ff89ff033fdb95c4b5", // the real uuid of the account, woo
Line 205: Line 251:
 
   "capes" : [ ]
 
   "capes" : [ ]
 
  }
 
  }
 +
</syntaxhighlight>
  
 
Else it will look like this:
 
Else it will look like this:
 +
 +
<syntaxhighlight lang="json" line='line'>
 
  {
 
  {
 
   "path" : "/minecraft/profile",
 
   "path" : "/minecraft/profile",
Line 214: Line 263:
 
   "developerMessage" : "The server has not found anything matching the request URI"
 
   "developerMessage" : "The server has not found anything matching the request URI"
 
  }
 
  }
 +
</syntaxhighlight>
 +
 +
Note that Xbox Game Pass users who haven't logged into the new Minecraft Launcher at least once will not return a profile, and will need to login once after activating Xbox Game Pass to setup their Minecraft username.
  
You should know have all necessary data (the mc access token, the username and the uuid) to launch the game. Well done!
+
You should now have all necessary data (the mc access token, the username and the uuid) to launch the game. Well done!
  
 
== Sample Implementations ==
 
== Sample Implementations ==
  
The author of this page provided a (shitty) sample implementation in Java (using javafx and its webview) [https://github.com/MiniDigger/MiniLauncher/blob/master/launcher/src/main/java/me/minidigger/minecraftlauncher/launcher/gui/MsaFragmentController.java here]
+
A fully working kotlin implementation can be found [https://gitlab.bixilon.de/bixilon/minosoft/-/blob/master/src/main/java/de/bixilon/minosoft/util/account/microsoft/MicrosoftOAuthUtils.kt] here using device tokens.
 +
 
 +
A fully working cli wrapper in Java using device tokens [https://github.com/covers1624/DevLogin here]
 +
 
 +
A rough sample implementation in Java (using javafx and its webview) [https://github.com/MiniDigger/MiniLauncher/blob/master/launcher/src/main/java/me/minidigger/minecraftlauncher/launcher/gui/MsaFragmentController.java here].
 +
 
 +
An implementation in Go [https://gist.github.com/rbrick/be8ed86864fc5d77aa6c979053cfc892 here]. 
 +
 
 +
An implementation in JS can be found [https://github.com/PrismarineJS/node-minecraft-protocol/blob/master/src/client/microsoftAuth.js here] and one using JS/TS [https://gist.github.com/Plagiatus/ce5f18bc010395fc45d8553905e10f55 here]
 +
 
 +
An implementation in Python can be found [https://codeberg.org/JakobDev/minecraft-launcher-lib/src/branch/master/minecraft_launcher_lib/microsoft_account.py here]
 +
 
 +
An implementation in Rust can be found [https://gist.github.com/OverHash/a71b32846612ba09d8f79c9d775bfadf here].
 +
 
 +
A Kotlin library (JVM + JS) can be found [https://github.com/TheNullicorn/ms-to-mca here].
 +
 
 +
A C# library using webview and [https://github.com/AzureAD/microsoft-authentication-library-for-dotnet MSAL.NET] can be found [https://github.com/CmlLib/CmlLib.Core.Auth.Microsoft here].
 +
 
 +
A Rust library can be found [https://crates.io/crates/minecraft-msa-auth here].
 +
 
 +
A PHP library can be found [https://github.com/Aberdeener/minecraft-oauth/ here].
 +
 
 +
[[Category:Minecraft Modern]]

Latest revision as of 18:43, 26 February 2024

Minecraft is moving to Microsoft accounts. Starting December 2020, all new Accounts already use the new system, old accounts will be migrated later, see this blog post

There are multiple steps and different tokens required, but in the end, you get a normal Minecraft token back. Launching the game itself hasn't changed.

Microsoft OAuth2 Flow

Prior to any of these steps, you will first need to obtain an OAuth 2.0 client ID by creating a Microsoft Azure application. You will not need to obtain a client secret.

You can then use the OAuth2 authorization code flow to obtain an access token. You'll need to present the user with a login page that, once completed, will redirect to a specified URL with the token in the query parameters. In non-web applications this typically involves spinning up a temporary HTTP server to handle the redirect. If you'd rather not do that, consider using the (slightly less automatic) device code flow instead.

In any case, you'll need to include XboxLive.signin in the scope parameter of the authorization request; otherwise the next endpoint will complain, and not in a helpful way.

According to this support Article, new created Azure Apps must apply for the Permission to use the Minecraft API using this form. If your App don't have the Permission api.minecraftservices.com will return a 403.

Note: You must use the consumers AAD tenant to sign in with the XboxLive.signin scope. Using an Azure AD tenant ID or the common scope will just give errors. This also means you cannot sign in with users that are in the AAD tenant, only with consumer Microsoft accounts.

Authenticate with Xbox Live

Now that we are authenticated with Microsoft, we can authenticate with Xbox Live.

To do that, we send

 1  POST https://user.auth.xboxlive.com/user/authenticate
 2  {
 3     "Properties": {
 4         "AuthMethod": "RPS",
 5         "SiteName": "user.auth.xboxlive.com",
 6         "RpsTicket": "d=<access token>" // your access token from the previous step here
 7     },
 8     "RelyingParty": "http://auth.xboxlive.com",
 9     "TokenType": "JWT"
10  }

Again, it will complain if you don't set Content-Type: application/json and Accept: application/json. It will also complain if your SSL implementation does not support SSL renegotiations.

The response will look like this:

 1  {
 2    "IssueInstant":"2020-12-07T19:52:08.4463796Z",
 3    "NotAfter":"2020-12-21T19:52:08.4463796Z",
 4    "Token":"token", // save this, this is your xbl token
 5    "DisplayClaims":{
 6       "xui":[
 7          {
 8             "uhs":"userhash" // save this
 9          }
10       ]
11    }
12  }

Obtain XSTS token for Minecraft

Now that we are authenticated with XBL, we need to get a XSTS token, we can use to login to Minecraft.

 1  POST https://xsts.auth.xboxlive.com/xsts/authorize
 2  {
 3     "Properties": {
 4         "SandboxId": "RETAIL",
 5         "UserTokens": [
 6             "xbl_token" // from above
 7         ]
 8     },
 9     "RelyingParty": "rp://api.minecraftservices.com/",
10     "TokenType": "JWT"
11  }

Again, set content type and accept to json and ensure SSL renegotiation is supported by your client.

Note: When trying to get the XSTS token for the bedrock realms API, you need to change the following:

"RelyingParty": "https://pocket.realms.minecraft.net/"

also you can stop at this point, as the bedrock realms API uses the XSTS token directly instead of a seperate auth scheme.

Response will look like this:

 1  {
 2    "IssueInstant":"2020-12-07T19:52:09.2345095Z",
 3    "NotAfter":"2020-12-08T11:52:09.2345095Z",
 4    "Token":"token", // save this, this is your xsts token
 5    "DisplayClaims":{
 6       "xui":[
 7          {
 8             "uhs":"userhash" // same as last request
 9          }
10       ]
11    }
12 }

The endpoint can return a 401 error with the below response:

1  {
2     "Identity":"0",
3     "XErr":2148916238,
4     "Message":"",
5     "Redirect":"https://start.ui.xboxlive.com/AddChildToFamily"
6  }

The Redirect parameter usually will not resolve or go anywhere in a browser, likely they're targeting Xbox consoles.

Noted XErr codes and their meanings:

  • 2148916233: The account doesn't have an Xbox account. Once they sign up for one (or login through minecraft.net to create one) then they can proceed with the login. This shouldn't happen with accounts that have purchased Minecraft with a Microsoft account, as they would've already gone through that Xbox signup process.
  • 2148916235: The account is from a country where Xbox Live is not available/banned
  • 2148916236: The account needs adult verification on Xbox page. (South Korea)
  • 2148916237: The account needs adult verification on Xbox page. (South Korea)
  • 2148916238: The account is a child (under 18) and cannot proceed unless the account is added to a Family by an adult. This only seems to occur when using a custom Microsoft Azure application. When using the Minecraft launchers client id, this doesn't trigger.

Authenticate with Minecraft

Now we can finally start talking to Minecraft. The XSTS token from the last request allows us to authenticate with Minecraft using

1  POST https://api.minecraftservices.com/authentication/login_with_xbox
2  {
3     "identityToken": "XBL3.0 x=<userhash>;<xsts_token>"
4  }

Response:

1  {
2   "username" : "some uuid", // this is not the uuid of the account
3   "roles" : [ ],
4   "access_token" : "minecraft access token", // jwt, your good old minecraft access token
5   "token_type" : "Bearer",
6   "expires_in" : 86400
7  }

This access token allows us to launch the game, but, we haven't actually checked if the account owns the game. Everything until here works with a normal Microsoft account!

Checking Game Ownership

So let's use our mc access token to check if a product licence is attached to the account.

GET https://api.minecraftservices.com/entitlements/mcstore

The access token goes into the auth header: Authorization: Bearer <Minecraft Access Token>. (Keep in mind that Bearer is actually the prefix you must include!)

If the account owns the game, the response will look like this:

 1  {
 2   "items" : [ {
 3     "name" : "product_minecraft",
 4     "signature" : "jwt sig"
 5   }, {
 6     "name" : "game_minecraft",
 7     "signature" : "jwt sig"
 8   } ],
 9   "signature" : "jwt sig",
10   "keyId" : "1"
11  }

The first jwts contain the values:

1  {
2   "typ": "JWT",
3   "alg": "RS256",
4   "kid": "1"
5  }.{
6   "signerId": "2535416586892404",
7   "name": "product_minecraft"
8  }.[Signature]

the last jwt looks like this decoded:

 1  {
 2   "typ": "JWT",
 3   "alg": "RS256",
 4   "kid": "1"
 5  }.{
 6   "entitlements": [
 7     {
 8       "name": "product_minecraft"
 9     },
10     {
11       "name": "game_minecraft"
12     }
13   ],
14   "signerId": "2535416586892404"
15  }.[Signature]

If the account doesn't own the game, the items array will be empty.

Note that Xbox Game Pass users don't technically own the game, and therefore will not show any ownership here, but will indeed have a Minecraft profile attached to their account.

Note that the signature should always be checked with the public key from Mojang to verify that it is a legitimate response from the official servers:

-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtz7jy4jRH3psj5AbVS6W
NHjniqlr/f5JDly2M8OKGK81nPEq765tJuSILOWrC3KQRvHJIhf84+ekMGH7iGlO
4DPGDVb6hBGoMMBhCq2jkBjuJ7fVi3oOxy5EsA/IQqa69e55ugM+GJKUndLyHeNn
X6RzRzDT4tX/i68WJikwL8rR8Jq49aVJlIEFT6F+1rDQdU2qcpfT04CBYLM5gMxE
fWRl6u1PNQixz8vSOv8pA6hB2DU8Y08VvbK7X2ls+BiS3wqqj3nyVWqoxrwVKiXR
kIqIyIAedYDFSaIq5vbmnVtIonWQPeug4/0spLQoWnTUpXRZe2/+uAKN1RY9mmaB
pRFV/Osz3PDOoICGb5AZ0asLFf/qEvGJ+di6Ltt8/aaoBuVw+7fnTw2BhkhSq1S/
va6LxHZGXE9wsLj4CN8mZXHfwVD9QG0VNQTUgEGZ4ngf7+0u30p7mPt5sYy3H+Fm
sWXqFZn55pecmrgNLqtETPWMNpWc2fJu/qqnxE9o2tBGy/MqJiw3iLYxf7U+4le4
jM49AUKrO16bD1rdFwyVuNaTefObKjEMTX9gyVUF6o7oDEItp5NHxFm3CqnQRmch
HsMs+NxEnN4E9a8PDB23b4yjKOQ9VHDxBxuaZJU60GBCIOF9tslb7OAkheSJx5Xy
EYblHbogFGPRFU++NrSQRX0CAwEAAQ==
-----END PUBLIC KEY-----

See the JWT standard[1] for more details.

In case the public key ever changes, it can be extracted from the launcher library:

strings ~/.minecraft/launcher/liblauncher.so > launcher-strings.txt

The created file launcher-strings.txt will include 2 strings which begin with -----BEGIN PUBLIC KEY----- and end with -----END PUBLIC KEY-----. The first key seems to be the one used for the JWT tokens, use of the second key is unknown.

Getting the profile

Now that we know that the account owns the game, we can get their profile in order to fetch the UUID:

GET https://api.minecraftservices.com/minecraft/profile

Again, the access token goes into the auth header: Authorization: Bearer token

The response will look like this, if the account owns the game:

 1  {
 2   "id" : "986dec87b7ec47ff89ff033fdb95c4b5", // the real uuid of the account, woo
 3   "name" : "HowDoesAuthWork", // the mc user name of the account
 4   "skins" : [ {
 5     "id" : "6a6e65e5-76dd-4c3c-a625-162924514568",
 6     "state" : "ACTIVE",
 7     "url" : "http://textures.minecraft.net/texture/1a4af718455d4aab528e7a61f86fa25e6a369d1768dcb13f7df319a713eb810b",
 8     "variant" : "CLASSIC",
 9     "alias" : "STEVE"
10   } ],
11   "capes" : [ ]
12  }

Else it will look like this:

1  {
2   "path" : "/minecraft/profile",
3   "errorType" : "NOT_FOUND",
4   "error" : "NOT_FOUND",
5   "errorMessage" : "The server has not found anything matching the request URI",
6   "developerMessage" : "The server has not found anything matching the request URI"
7  }

Note that Xbox Game Pass users who haven't logged into the new Minecraft Launcher at least once will not return a profile, and will need to login once after activating Xbox Game Pass to setup their Minecraft username.

You should now have all necessary data (the mc access token, the username and the uuid) to launch the game. Well done!

Sample Implementations

A fully working kotlin implementation can be found [2] here using device tokens.

A fully working cli wrapper in Java using device tokens here

A rough sample implementation in Java (using javafx and its webview) here.

An implementation in Go here.

An implementation in JS can be found here and one using JS/TS here

An implementation in Python can be found here

An implementation in Rust can be found here.

A Kotlin library (JVM + JS) can be found here.

A C# library using webview and MSAL.NET can be found here.

A Rust library can be found here.

A PHP library can be found here.