Difference between revisions of "Microsoft Authentication Scheme"
(Make changes for using your own Client ID & Secret. since the user should be using their own URL, we should specify that the client secret should be kept secret. Add an example of a token exchange) |
(add info for refreshing microsoft tokens) |
||
Line 63: | Line 63: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | We care about the access_token here. | + | We care about the access_token here. |
An example of this exchange, in Go, can be found here: | An example of this exchange, in Go, can be found here: | ||
https://gist.github.com/rbrick/be8ed86864fc5d77aa6c979053cfc892#file-msa-go-L35-L49 | https://gist.github.com/rbrick/be8ed86864fc5d77aa6c979053cfc892#file-msa-go-L35-L49 | ||
+ | |||
+ | |||
+ | == Refreshing Tokens == | ||
+ | You can use the refresh_token you got in the previous request to acquire a new access_token. Just call the same endpoint as before, switching out code for the refresh token and grant type <code>authorization_code</code> for <code>refresh_token</code> | ||
+ | |||
+ | POST https://login.live.com/oauth20_token.srf | ||
+ | Content: | ||
+ | |||
+ | <syntaxhighlight lang="java" line='line'> | ||
+ | Map<Object, Object> data = Map.of( | ||
+ | "client_id", "YOUR_CLIENT_ID", | ||
+ | "client_secret": "YOUR_CLIENT_SECRET" | ||
+ | "refresh_token", refreshToken, // from the previous request | ||
+ | "grant_type", "refresh_token", | ||
+ | "redirect_uri", "YOUR_REDIRECT_URI", | ||
+ | ); | ||
+ | </syntaxhighlight> | ||
== Authenticate with XBL == | == Authenticate with XBL == |
Revision as of 19:50, 10 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 tokens required, but in the end, you get a normal Minecraft token back. Launching the game itself hasn't changed.
Contents
Microsoft OAuth Flow
Prior to any of these steps, you will first need to obtain an OAuth 2.0 Client ID & secret by creating a Microsoft Azure application.
In the first step, we are logging into the Microsoft account. This has to be done in a browser/webview!
The URL generated, either manually, or through an OAuth2 library, would look something like this:
https://login.live.com/oauth20_authorize.srf ?client_id=<your Azure client ID> &response_type=code &redirect_uri=<your redirect uri> &scope=XboxLive.signin &state=<optional; used to prevent CSRF & restoring previous application states>
Note: You may also use the Azure Active Directory endpoints which would look like https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize but this just redirects to the live.com URL.
The user will be prompted to enter a username (E-Mail, Skype ID, Phone number, whatever) and their password. If those are valid, the user will be redirected. The user doesn't need to own MC, that check comes way later!
The redirect will look something like this
https://<your redirect URL>?code=codegoeshere&state=<optional; only if provided>
You have to extract the code param, it's your Microsoft Authorization Code.
Authorization Code -> Authorization Token
The next step is to get an access token from the auth code. This isn't done in the browser for security reasons.
This exchange should be done on the server-side as it requires the use of your client secret which, as the name implies, should be kept secret.
POST https://login.live.com/oauth20_token.srf
Content:
1 Map<Object, Object> data = Map.of(
2 "client_id", "YOUR_CLIENT_ID",
3 "client_secret": "YOUR_CLIENT_SECRET"
4 "code", authcode, // the code from step 1
5 "grant_type", "authorization_code",
6 "redirect_uri", "YOUR_REDIRECT_URI",
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":"XboxLive.signin",
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.
An example of this exchange, in Go, can be found here:
https://gist.github.com/rbrick/be8ed86864fc5d77aa6c979053cfc892#file-msa-go-L35-L49
Refreshing Tokens
You can use the refresh_token you got in the previous request to acquire a new access_token. Just call the same endpoint as before, switching out code for the refresh token and grant type authorization_code
for refresh_token
POST https://login.live.com/oauth20_token.srf
Content:
1 Map<Object, Object> data = Map.of(
2 "client_id", "YOUR_CLIENT_ID",
3 "client_secret": "YOUR_CLIENT_SECRET"
4 "refresh_token", refreshToken, // from the previous request
5 "grant_type", "refresh_token",
6 "redirect_uri", "YOUR_REDIRECT_URI",
7 );
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 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 }
You should know have all necessary data (the mc access token, the username and the uuid) to launch the game. Well done!
Sample Implementations
There is a rough sample implementation in Java (using javafx and its webview) here.