Difference between revisions of "Zh:Microsoft Authentication Scheme"

From wiki.vg
Jump to navigation Jump to search
(Add Minecraft API Apply)
 
(2 intermediate revisions by one other user not shown)
Line 1: Line 1:
 
Minecraft正在迁移至微软账号。自2020年12月起,所有新账号已经使用了新版系统,旧的账号也将在之后迁移,详见[https://www.minecraft.net/en-us/article/java-edition-moving-house 这篇博文]。
 
Minecraft正在迁移至微软账号。自2020年12月起,所有新账号已经使用了新版系统,旧的账号也将在之后迁移,详见[https://www.minecraft.net/en-us/article/java-edition-moving-house 这篇博文]。
  
新版系统需要多个步骤以及不同的令牌,但是最后你还是回获得一个普通的Mineceaft令牌。启动游戏本身并没有改变。
+
新版系统需要多个步骤以及不同的令牌,但是最后你还是回获得一个普通的Minecraft令牌。启动游戏本身并没有改变。
  
 
== 微软OAuth流程 ==
 
== 微软OAuth流程 ==
[[File:minecraft_login.png|frame|Example of the login page]]
 
  
第一步,我们登入微软账号。这一步必须在浏览器/网页视图中完成!并没有测试其他的重定向链接,客户端id(<code>client_id</code>)被硬编码为(<code>00000000402b5328</code>),也就是Minecraft这个游戏的id。
+
在所有这些步骤之前,你需要先通过创建一个 [https://learn.microsoft.com/zh-cn/azure/active-directory/develop/quickstart-register-app 微软 Azure 应用程序] 来获得一个客户端 ID。你''不''需要获得一个客户端密码。
  
https://login.live.com/oauth20_authorize.srf
+
你可以通过 [https://learn.microsoft.com/zh-cn/azure/active-directory/develop/v2-oauth2-auth-code-flow OAuth 2.0 授权代码流] 来获取一个访问令牌。你需要向用户显示一个登录页面,一旦完成,会重定向到一个指定的 URL,并在查询参数里传递 token。在非 web 应用程序中这需要通过搭建一个临时 HTTP 服务器来处理这个重定向。如果你不希望这样,可以使用(不那么自动化的)[https://learn.microsoft.com/zh-cn/azure/active-directory/develop/v2-oauth2-device-code 设备代码流] 代替。
  ?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
 
  
用户需要在此处输入用户名(邮箱、手机号或别的登录方法)以及密码。如果用户名和密码正确,那么用户会被重定向。在这一步中用户不需要拥有MC,那会在之后再检查。
+
不论如何,你需要把 <code>XboxLive.signin</code> 包含在认证请求的 <code>scope</code> 参数内,否则下一个终结点会报错。
 
 
重定向链接看起来会像这样:
 
https://login.live.com/oauth20_desktop.srf?code=codegoeshere&lc=1033
 
 
 
你需要提取出其中的<code>code</code>参数,这是你的微软授权码。
 
 
 
== 授权码 -> 授权令牌 ==
 
 
 
下一步是从授权码中获取授权令牌。由于安全原因,这一步不在浏览器中完成。
 
POST https://login.live.com/oauth20_token.srf
 
Content:
 
 
 
<syntaxhighlight lang="java" line='line'>
 
Map<Object, Object> data = Map.of(
 
    "client_id", "00000000402b5328", // 还是Minecraft客户端id
 
    "code", authcode, // 第一步中获取的代码
 
    "grant_type", "authorization_code",
 
    "redirect_uri", "https://login.live.com/oauth20_desktop.srf",
 
    "scope", "service::user.auth.xboxlive.com::MBI_SSL"
 
);
 
</syntaxhighlight>
 
 
 
不要忘记设置<code>Content-Type: application/x-www-form-urlencoded</code>
 
 
 
响应看起来会像这样:
 
 
 
<syntaxhighlight lang="json" line='line'>
 
{
 
  "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"
 
}
 
</syntaxhighlight>
 
 
 
我们需要关注到这里的<code>access_token</code>
 
 
 
== 用于刷新的token(<code>refresh_token</code>) ==
 
您可以使用您在之前请求中收到的refresh_token获取新access_token。只需调用与以前相同的API,请切换刷新的令牌<code>authorization_code</code>和授予类型的代码<code>refresh_token<code>
 
POST https://login.live.com/oauth20_token.srf
 
  client_id=<your client id>
 
  &client_secret=<your client secret>
 
  &refresh_token=<refresh token from previous step>
 
  &grant_type=refresh_token
 
  &redirect_uri=<your redirect uri>
 
 
 
(响应跟请求access_token一模一样)
 
  
 +
<b>请注意</b>:根据 Mojang 发布的 [https://help.minecraft.net/hc/en-us/articles/16254801392141 新Java版游戏服务API审核或应用程序流程] 规定,新建的 OAuth 应用程序必须使用此表单申请使用 Minecraft API 的使用权限,如果您的应用程序没有对应权限 <code>api.minecraftservices.com</code> 将返回 403
  
 
== XBL身份验证 ==
 
== XBL身份验证 ==
Line 79: Line 25:
 
         "AuthMethod": "RPS",
 
         "AuthMethod": "RPS",
 
         "SiteName": "user.auth.xboxlive.com",
 
         "SiteName": "user.auth.xboxlive.com",
         "RpsTicket": "d=<access_token>" // 第二步中获取的访问令牌
+
         "RpsTicket": "d=<access_token>" // 第一步中获取的访问令牌
 
     },
 
     },
 
     "RelyingParty": "http://auth.xboxlive.com",
 
     "RelyingParty": "http://auth.xboxlive.com",

Latest revision as of 07:39, 24 January 2024

Minecraft正在迁移至微软账号。自2020年12月起,所有新账号已经使用了新版系统,旧的账号也将在之后迁移,详见这篇博文

新版系统需要多个步骤以及不同的令牌,但是最后你还是回获得一个普通的Minecraft令牌。启动游戏本身并没有改变。

微软OAuth流程

在所有这些步骤之前,你需要先通过创建一个 微软 Azure 应用程序 来获得一个客户端 ID。你需要获得一个客户端密码。

你可以通过 OAuth 2.0 授权代码流 来获取一个访问令牌。你需要向用户显示一个登录页面,一旦完成,会重定向到一个指定的 URL,并在查询参数里传递 token。在非 web 应用程序中这需要通过搭建一个临时 HTTP 服务器来处理这个重定向。如果你不希望这样,可以使用(不那么自动化的)设备代码流 代替。

不论如何,你需要把 XboxLive.signin 包含在认证请求的 scope 参数内,否则下一个终结点会报错。

请注意:根据 Mojang 发布的 新Java版游戏服务API审核或应用程序流程 规定,新建的 OAuth 应用程序必须使用此表单申请使用 Minecraft API 的使用权限,如果您的应用程序没有对应权限 api.minecraftservices.com 将返回 403

XBL身份验证

现在,我们已通过微软身份验证,可以向xbox live进行身份验证。

我们需要发送:

 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>" // 第一步中获取的访问令牌
 7     },
 8     "RelyingParty": "http://auth.xboxlive.com",
 9     "TokenType": "JWT"
10  }

同样,也得设置Content-Type: application/json以及Accept: application/json

响应看起来会像这样:

 1  {
 2    "IssueInstant":"2020-12-07T19:52:08.4463796Z",
 3    "NotAfter":"2020-12-21T19:52:08.4463796Z",
 4    "Token":"token", // 保存它,这是你的xbl令牌
 5    "DisplayClaims":{
 6       "xui":[
 7          {
 8             "uhs":"uhs" // 保存它
 9          }
10       ]
11    }
12  }

我们需要保存这里的Tokenuhs。uhs是user hash的缩写,为用户信息哈希值。

XSTS身份验证

现在,我们已经通过XBL进行了身份验证,我们需要获取XSTS令牌,我们可以使用它来登录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  }

同样还是设置Content-Type: application/json以及Accept: application/json

响应看起来会像这样:

 1  {
 2    "IssueInstant":"2020-12-07T19:52:09.2345095Z",
 3    "NotAfter":"2020-12-08T11:52:09.2345095Z",
 4    "Token":"token", // 保存它,这是你的xsts令牌
 5    "DisplayClaims":{
 6       "xui":[
 7          {
 8             "uhs":"" // 与上个请求相同
 9          }
10       ]
11    }
12 }

Minecraft身份验证

现在,我们终于可以回到Minecraft身上了,上一各请求中获取的XSTS令牌允许我们验证Minecraft。

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

响应:

1  {
2   "username" : "some uuid", // 这并不是账号的uuid
3   "roles" : [ ],
4   "access_token" : "minecraft access token", // jwt,你的老朋友Minecraft访问令牌
5   "token_type" : "Bearer",
6   "expires_in" : 86400
7  }

这个访问令牌(access_token)允许我们启动游戏,但是,我们甚至还没有检查该账号是否拥有游戏。之前我们做的所有工作都是与微软账号进行的!

检查游戏拥有情况

那么现在让我们使用MC的访问令牌来检查该账号是否包含产品许可。

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

访问令牌在验证文件头中:Authorization: Bearer token。请注意,Bearer必须保留,token是上一步获取到的访问令牌。

如果用户拥有游戏,那么响应会看起来像这样:

 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  }

第一个jwts会包含值:

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

最后一个jwt看起来是这样解码的:

 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]

如果该账号没有拥有游戏,那么项目为空。

获取档案

现在我们知道了该账号拥有游戏,那么可以获取他的档案来得到uuid:

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

同样,访问令牌在验证文件头中:Authorization: Bearer token

如果账号拥有游戏,响应看起来会像这样:

 1  {
 2   "id" : "986dec87b7ec47ff89ff033fdb95c4b5", // 账号的真实uuid
 3   "name" : "HowDoesAuthWork", // 该账号的mc用户名
 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  }

否则会看起来像这样:

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  }

那么这样就可以知道所有必要的数据(MC访问令牌、用户名和uuid)来启动游戏了。干得漂亮!

示例实现

这里有个简易的Java示例实现(使用了javafx和它的webview):https://github.com/MiniDigger/MiniLauncher/blob/master/launcher/src/main/java/me/minidigger/minecraftlauncher/launcher/gui/MsaFragmentController.java