Difference between revisions of "Microsoft Authentication Scheme"

From wiki.vg
Jump to navigation Jump to search
m (→‎Checking Game Ownership: Fix formatting)
m (Syntax highlighting)
Line 28: Line 28:
 
  POST https://login.live.com/oauth20_token.srf
 
  POST https://login.live.com/oauth20_token.srf
 
Content:
 
Content:
 +
 +
<syntaxhighlight lang="java" line='line'>
 
  Map<Object, Object> data = Map.of(
 
  Map<Object, Object> data = Map.of(
 
     "client_id", "00000000402b5328", // minecrafts client id again
 
     "client_id", "00000000402b5328", // minecrafts client id again
Line 35: Line 37:
 
     "scope", "service::user.auth.xboxlive.com::MBI_SSL"
 
     "scope", "service::user.auth.xboxlive.com::MBI_SSL"
 
  );
 
  );
 +
</syntaxhighlight>
  
 
Don't forget to set <code>Content-Type: application/x-www-form-urlencoded</code>
 
Don't forget to set <code>Content-Type: application/x-www-form-urlencoded</code>
  
The response will look like this
+
The response will look like this:
 +
 
 +
<syntaxhighlight lang="json" line='line'>
 
  {
 
  {
 
   "token_type":"bearer",
 
   "token_type":"bearer",
Line 48: Line 53:
 
   "foci":"1"
 
   "foci":"1"
 
  }
 
  }
 +
</syntaxhighlight>
  
 
We care about the access_token here. (TODO: check what we can do with the refresh token)
 
We care about the access_token here. (TODO: check what we can do with the refresh token)
Line 56: Line 62:
  
 
To do that, we send
 
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 66: Line 74:
 
     "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>
  
 
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 82: Line 93:
 
   }
 
   }
 
  }
 
  }
 +
</syntaxhighlight>
  
 
We need to save token and uhs. I have no idea what uhs stands for. (TODO: find out)
 
We need to save token and uhs. I have no idea what uhs stands for. (TODO: find out)
Line 89: Line 101:
 
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 113:
 
     "TokenType": "JWT"
 
     "TokenType": "JWT"
 
  }
 
  }
 +
</syntaxhighlight>
  
 
Again, set content type and accept to json.
 
Again, set content type and accept to json.
  
 
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 116: Line 132:
 
   }
 
   }
 
}
 
}
 +
</syntaxhighlight>
  
 
== 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 to 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=<uhs>;<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 154:
 
   "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!
Line 144: Line 166:
  
 
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 179:
 
   "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 191:
 
   "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 211:
 
   "signerId": "2535416586892404"
 
   "signerId": "2535416586892404"
 
  }.[Signature]
 
  }.[Signature]
 +
</syntaxhighlight>
  
 
If the account doesn't own the game, the items array will be empty.
 
If the account doesn't own the game, the items array will be empty.
Line 193: Line 223:
  
 
The responce will look like this, if the account owns the game:
 
The responce 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 237:
 
   "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 249:
 
   "developerMessage" : "The server has not found anything matching the request URI"
 
   "developerMessage" : "The server has not found anything matching the request URI"
 
  }
 
  }
 +
</syntaxhighlight>
  
 
You should know have all necessary data (the mc access token, the username and the uuid) to launch the game. Well done!
 
You should know have all necessary data (the mc access token, the username and the uuid) to launch the game. Well done!

Revision as of 01:27, 8 December 2020

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 token required, but in the end, you get a normal minecraft token back. Launching the game itself hasn't changed.


Microsoft OAuth Flow

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.

https://login.live.com/oauth20_authorize.srf
 ?client_id=00000000402b5328
 &response_type=code
 &scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL
 &redirect_uri=https%3A%2F%2Flogin.live.com%2Foauth20_desktop.srf

Example of the login page: https://i.imgur.com/gy8uKGs.png (TODO: embed image)

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!

The redirect will looks something like this

https://login.live.com/oauth20_desktop.srf?code=codegoeshere&lc=1033

You have to extract the code param, it's your Microsoft Authorization Code.

Authorization Code -> Authorization Token

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:

1  Map<Object, Object> data = Map.of(
2     "client_id", "00000000402b5328", // minecrafts client id again
3     "code", authcode, // the code from step 1
4     "grant_type", "authorization_code",
5     "redirect_uri", "https://login.live.com/oauth20_desktop.srf",
6     "scope", "service::user.auth.xboxlive.com::MBI_SSL"
7  );

Don't forget to set Content-Type: application/x-www-form-urlencoded

The response will look like this:

1  {
2    "token_type":"bearer",
3    "expires_in":86400,
4    "scope":"service::user.auth.xboxlive.com::MBI_SSL",
5    "access_token":"token here",
6    "refresh_token":"M.R3_BAY.token here",
7    "user_id":"889ed4a3d844f672",
8    "foci":"1"
9  }

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

 1  POST https://user.auth.xboxlive.com/user/authenticate
 2  {
 3     "Properties": {
 4         "AuthMethod": "RPS",
 5         "SiteName": "user.auth.xboxlive.com",
 6         "RpsTicket": "access_token" // your access token from step 2 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

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":"uhs" // save this
 9          }
10       ]
11    }
12  }

We need to save token and uhs. I have no idea what uhs stands for. (TODO: find out)

Authenticate with XSTS

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.

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":"" // same as last request
 9          }
10       ]
11    }
12 }

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

1  POST https://api.minecraftservices.com/authentication/login_with_xbox
2  {
3     "identityToken": "XBL3.0 x=<uhs>;<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 token

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.

Get the profile

Now that we know that the account owns the game, lets get his profile so we get uuid:

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

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

The responce 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  }

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

Sample Implementations

The author of this page provided a (shitty) sample implementation in Java (using javafx and its webview) here