Difference between revisions of "Zh:Authentication"

From wiki.vg
Jump to navigation Jump to search
(暴力更新不可取)
Line 1: Line 1:
Minecraft 1.6 引入了一个名为 '''Yggdrasil'''验证方案 来完全替代 [[Legacy Authentication|先前的验证系统]]. Mojang的其他游戏, Scrolls, 也使用此验证方法. Mojang 说 [https://twitter.com/KrisJelbring/status/453573406341206016 每个人都应该使用这个验证系统进行自定义登录], 但是 [https://twitter.com/KrisJelbring/status/461390585086361600 不会向用户索要凭据].
+
Minecraft 1.6引入了一种全新的叫作'''Yggdrasil'''的认证方案,它彻底地取代了[[ZH:旧版认证|先前的认证系统]]。Mojang的其他游戏,Scrolls,同样也使用了该认证方法。Mojang曾经说过[https://twitter.com/KrisJelbring/status/453573406341206016 每个人都应使用此认证系统来进行自定义登录],但是[https://twitter.com/KrisJelbring/status/461390585086361600 永远不会从用户收集凭据]
  
 
== 请求格式 ==
 
== 请求格式 ==
 
+
所有对Yggdrasil的请求都会发送到以下服务器:
所有的 Yggdrasil 请求都发送到下列服务器
 
  
 
  https://authserver.mojang.com
 
  https://authserver.mojang.com
  
此外,他们应该遵循以下规则
+
此外,它们应满足以下规则:
  
* 使用 <code>POST</code> 请求
+
* <code>POST</code>请求
* 包含 <code>Content-Type</code> 头,并设置为 <code>application/json</code>
+
* <code>Content-Type</code>头设置为<code>application/json</code>
* 包含一个使用 [[wikipedia:JSON|JSON]] 格式编码的有效载荷
+
* 以包含[[wikipedia:zh:JSON|JSON]]编码的字典作为负载
  
 +
如果请求成功,服务器将响应:
  
如果请求成功,服务器将做出如下响应
+
* 状态码<code>200</code>
 +
* 根据以下规范使用[[wikipedia:zh:JSON|JSON]]编码的字典
  
* 状态码 <code>200</code>
+
但如果请求失败,服务器会响应:
* 一个符合下文规范的 [[wikipedia:JSON|JSON]]数据
 
如果请求失败,服务器将做出如下响应
 
  
* 恰当的非200状态码 [[wikipedia:List of HTTP status codes|HTTP status code]]
+
* 适当的非200[[wikipedia:zh:HTTP状态码|HTTP状态码]]
* 一个包含如下信息的[[wikipedia:JSON|JSON]]数据
+
* 拥有以下格式的[[wikipedia:zh:JSON|JSON]]编码的字典:
  
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
{
 
{
     "error": "简短的描述(机器可读)",
+
     "error": "错误的简短描述",
     "errorMessage": "一个向用户展示的错误消息(人类可读)",
+
     "errorMessage": "用于向用户显示的更长的描述",
     "cause": "引起错误的原因" // 可选
+
     "cause": "错误原因" // 可选的
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 33: Line 32:
 
== 错误 ==
 
== 错误 ==
  
以下是一些可能遇到的错误:
+
这些是可能遇到的一些错误:
  
 
{| class="wikitable"
 
{| class="wikitable"
Line 39: Line 38:
 
  ! 错误
 
  ! 错误
 
  ! 原因
 
  ! 原因
  ! 错误消息
+
  ! 错误信息
 
  ! 注释
 
  ! 注释
 
  |-
 
  |-
 
  | <code>Method Not Allowed</code>
 
  | <code>Method Not Allowed</code>
 
  |
 
  |
  | 请求所使用的方法不允许访问当前URI,这里通常是由于非POST方法引起的。
+
  | The method specified in the request is not allowed for the resource identified by the request URI<br/>请求URI中标识的资源不允许使用请求中指定的方法
  |
+
  | 收到了除POST请求以外的信息。
 
  |-
 
  |-
 
  | <code>Not Found</code>
 
  | <code>Not Found</code>
 
  |
 
  |
  | 服务器没有找到URI所对应的资源
+
  | The server has not found anything matching the request URI<br/>服务器未找到与请求URI匹配的任何内容
  |
+
  | 调用了不存在的端点。
 
  |-
 
  |-
 
  | <code>ForbiddenOperationException</code>
 
  | <code>ForbiddenOperationException</code>
 
  | <code>UserMigratedException</code>
 
  | <code>UserMigratedException</code>
  | 无效的凭证。账户已迁移。请使用电子邮件作为用户名。
+
  | Invalid credentials. Account migrated, use e-mail as username.<br/>无效凭据。账号已迁移,使用邮箱作为用户名。
 
  |  
 
  |  
 
  |-
 
  |-
 
  | <code>ForbiddenOperationException</code>
 
  | <code>ForbiddenOperationException</code>
 
  |  
 
  |  
  | 无效的凭证。用户名或密码无效。
+
  | Invalid credentials. Invalid username or password.<br/>无效凭据。无效的用户名或密码。
 
  |  
 
  |  
 
  |-
 
  |-
 
  | <code>ForbiddenOperationException</code>
 
  | <code>ForbiddenOperationException</code>
 
  |  
 
  |  
  | 无效的凭证。
+
  | Invalid credentials.<br/>无效凭据。
  | 使用同一用户名进行了太多的登录尝试(参见 <code>/authenticate</code>)。注释:用户名密码也许是有效的。
+
  | 该用户名最近的尝试登录过多(见<code>/authenticate</code>)。注意用户名和密码可能仍然有效!
 
  |-
 
  |-
 
  | <code>ForbiddenOperationException</code>
 
  | <code>ForbiddenOperationException</code>
 
  |  
 
  |  
  | Invalid token.
+
  | Invalid token.<br/>无效凭据。
  | <code>accessToken</code> was invalid.
+
  | <code>accessToken</code>失效了。
 
  |-
 
  |-
 
  | <code>IllegalArgumentException</code>
 
  | <code>IllegalArgumentException</code>
 
  |  
 
  |  
  | Access token already has a profile assigned.
+
  | Access token already has a profile assigned.<br/>访问令牌已经被分配了档案。
  | Selecting profiles isn't implemented yet.
+
  | 还没有实现选择档案。
 
  |-
 
  |-
 
  | <code>IllegalArgumentException</code>
 
  | <code>IllegalArgumentException</code>
 
  |  
 
  |  
  | credentials is null
+
  | credentials is null<br/>凭据为空
  | Username/password was not submitted.
+
  | 用户名/密码未提交。
 
  |-
 
  |-
 
  | <code>IllegalArgumentException</code>
 
  | <code>IllegalArgumentException</code>
 
  |  
 
  |  
  | Invalid salt version
+
  | Invalid salt version<br/>无效的盐版本
  | ???
+
  | ???
 
  |-
 
  |-
 
  | <code>Unsupported Media Type</code>
 
  | <code>Unsupported Media Type</code>
 
  |  
 
  |  
  | The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method
+
  | The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method<br/>服务器拒绝为请求提供服务,因为请求实体的格式不受请求方法所请求的资源支持
  | Data was not submitted as application/json
+
  | 数据未提交为application / json
 
  |}
 
  |}
  
== Authenticate ==
+
== 认证 ==
  
Authenticates a user using their password.
+
使用密码认证用户。
  
=== Endpoint ===
+
=== 端点 ===
  
 
  /authenticate
 
  /authenticate
  
=== Payload ===
+
=== 负载 ===
  
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
{
 
{
     "agent": {                              // defaults to Minecraft
+
     "agent": {                              // 默认为Minecraft
         "name": "Minecraft",                // For Mojang's other game Scrolls, "Scrolls" should be used
+
         "name": "Minecraft",                // 对于Mojang的其他游戏Scrolls,则应该使用"Scrolls"
         "version": 1                        // This number might be increased
+
         "version": 1                        // 以后的原版客户端
                                             // by the vanilla client in the future
+
                                             // 可能会增加该数字
 
     },
 
     },
     "username": "mojang account name",     // Can be an email address or player name for
+
     "username": "mojang帐号名",             // 可以是电子邮箱地址或
                                             // unmigrated accounts
+
                                             // 玩家名称(对于为迁移的账号)
     "password": "mojang account password",
+
     "password": "mojang帐号密码",
     "clientToken": "client identifier",     // optional
+
     "clientToken": "客户端标识符",           // 可选的
     "requestUser": true                    // optional; default: false; true adds the user object to the response
+
     "requestUser": true                    // 可选的,默认为false,若为true则将user对象加入到响应中
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The <code>clientToken</code> should be a randomly generated identifier and must be identical for each request. The vanilla launcher generates a random (version 4) UUID on first run and saves it, reusing it for every subsequent request. In case it is omitted the server will generate a random token based on Java's [http://docs.oracle.com/javase/7/docs/api/java/util/UUID.html#toString() <code>UUID.toString()</code>] which should then be stored by the client. This will however also invalidate all previously acquired <code>accessToken</code>s for this user across all clients.
+
<code>clientToken</code>应该是一个随机生成的标识符而且必须每次请求都是相同的。原版启动器会在第一次运行时生成一个随机的(v4)UUID并保存,在后续每次请求中复用它。如果省略,那么服务器会生成一个基于Java的[http://docs.oracle.com/javase/7/docs/api/java/util/UUID.html#toString() <code>UUID.toString()</code>]的随机令牌,它应该由客户端保存下来。然而这也会使用户之前在所有客户端上获取的<code>accessToken</code>失效。
  
=== Response ===
+
=== 响应 ===
  
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
{
 
{
     "accessToken": "random access token",     // hexadecimal or JSON-Web-Token (unconfirmed) [The normal accessToken can be found in the payload of the JWT (second by '.' separated part as Base64 encoded JSON object), in key "yggt"]
+
     "accessToken": "随机访问令牌",               // 十六进制或JSON-Web-Token(未确认)[普通accessToken可以在JWT的响应中找到(使用‘.’分隔的第二部分,以Base64编码的JSON对象),在“yggt”键中]
     "clientToken": "client identifier",       // identical to the one received
+
     "clientToken": "客户端标识符",               // 与接收到的相同
     "availableProfiles": [                    // only present if the agent field was received
+
     "availableProfiles": [                    // 仅在接收到agent字段时出现
 
         {
 
         {
             "agent": "minecraft",              // Presumably same value as before
+
             "agent": "minecraft",              // 可能与之前值相同
             "id": "profile identifier",       // hexadecimal
+
             "id": "档案标识符",                 // 十六进制
             "name": "player name",
+
             "name": "玩家名称",
             "userId": "hex string",
+
             "userId": "十六进制字符串",
             "createdAt": 1325376000000,        // Milliseconds since Jan 1 1970
+
             "createdAt": 1325376000000,        // 自1970年1月1日起的毫秒数
             "legacyProfile": true or false,   // Present even when false
+
             "legacyProfile": true或false,     // 即使为false也出现
             "suspended": true or false,       // probably false
+
             "suspended": true或false,         // 可能为false
             "paid": true or false,             // probably true
+
             "paid": true或false,               // 可能为true
             "migrated": true or false,        // Seems to be false even for migrated accounts...?  (https://bugs.mojang.com/browse/WEB-1461)
+
             "migrated": true或false,        // 似乎即使是已迁移账号也为false…?(https://bugs.mojang.com/browse/WEB-1461)
             "legacy": true or false            // Only appears in the response if true. Default to false.  Redundant to the newer legacyProfile...
+
             "legacy": true或false              // 它仅为true时出现。默认为false。与较新的legacyProfile重复…
 
         }
 
         }
 
     ],
 
     ],
     "selectedProfile": {                      // only present if the agent field was received
+
     "selectedProfile": {                      // 仅在接收到agent字段时出现
         "id": "uuid without dashes",
+
         "id": "不含分隔符的uuid",
         "name": "player name",
+
         "name": "玩家名称",
         "userId": "hex string",
+
         "userId": "十六进制字符串",
 
         "createdAt": 1325376000000,
 
         "createdAt": 1325376000000,
         "legacyProfile": true or false,
+
         "legacyProfile": true或false,
         "suspended": true or false,
+
         "suspended": true或false,
         "paid": true or false,
+
         "paid": true或false,
         "migrated": true or false,
+
         "migrated": true或false,
         "legacy": true or false
+
         "legacy": true或false
 
     },
 
     },
     "user": {                                  // only present if requestUser was true in the request payload
+
     "user": {                                  // 仅在请求负载中的requestUser为true出现
         "id": "user identifier",               // hexadecimal
+
         "id": "用户标识符",                     // 十六进制
         "email": "user@email.example",        // Hashed(?) value for unmigrated accounts
+
         "email": "user@email.example",        // 未迁移账号的哈希(?)值
         "username": "user@email.example",      // Regular name for unmigrated accounts, email for migrated ones
+
         "username": "user@email.example",      // 未迁移账号的正常名称或已迁移账号的电子邮箱
         "registerIp": "198.51.100.*",          // IP address with the last digit censored
+
         "registerIp": "198.51.100.*",          // 最后一位打码的IP地址
 
         "migratedFrom": "minecraft.net",
 
         "migratedFrom": "minecraft.net",
 
         "migratedAt": 1420070400000,
 
         "migratedAt": 1420070400000,
         "registeredAt": 1325376000000,        // May be a few minutes earlier than createdAt for profile
+
         "registeredAt": 1325376000000,        // 也许比profile的createdAt要早几分钟
 
         "passwordChangedAt": 1569888000000,
 
         "passwordChangedAt": 1569888000000,
 
         "dateOfBirth": -2208988800000,
 
         "dateOfBirth": -2208988800000,
Line 164: Line 163:
 
         "blocked": false,
 
         "blocked": false,
 
         "secured": true,
 
         "secured": true,
         "migrated": false,                    // Seems to be false even when migratedAt and migratedFrom are present...
+
         "migrated": false,                    // 似乎即使migratedAt和migratedFrom出现时也为false…
 
         "emailVerified": true,
 
         "emailVerified": true,
 
         "legacyUser": false,
 
         "legacyUser": false,
Line 170: Line 169:
 
         "properties": [
 
         "properties": [
 
             {
 
             {
                 "name": "preferredLanguage",  // might not be present for all accounts
+
                 "name": "preferredLanguage",  // 也许不会对所有账号显示
                 "value": "en"                  // Java locale format (https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#toString--)
+
                 "value": "en"                  // Java locale格式 (https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#toString--
 
             },
 
             },
 
             {
 
             {
                 "name": "twitch_access_token", // only present if a twitch account is associated (see https://account.mojang.com/me/settings)
+
                 "name": "twitch_access_token", // 仅在关联twitch账号时出现(见https://account.mojang.com/me/settings)
                 "value": "twitch oauth token"  // OAuth 2.0 Token; alphanumerical; e.g. https://api.twitch.tv/kraken?oauth_token=[...]
+
                 "value": "twitch oauth token"  // OAuth 2.0令牌,字母+数字,如https://api.twitch.tv/kraken?oauth_token=[...]
                                               // the Twitch API is documented here: https://github.com/justintv/Twitch-API
+
                                               // Twitch API的文档:https://github.com/justintv/Twitch-API
 
             }
 
             }
 
         ]
 
         ]
Line 183: Line 182:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
'''Note:''' If a user wishes to stay logged in on their computer you are strongly advised to store the received <code>accessToken</code> instead of the password itself.
+
'''注意:'''如果用户希望能在他们的电脑上保存登录状态,那么强烈建议应该存储<code>accessToken</code>而不是密码本身。
  
Currently each account will only have one single profile, multiple profiles per account are however planned in the future. If a user attempts to log into a valid Mojang account with no attached Minecraft license, the authentication will be successful, but the response will not contain a <code>selectedProfile</code> field, and the <code>availableProfiles</code> array will be empty.
+
当前每个账号只拥有一个档案,一个账号拥有多个档案还在未来计划中。如果用户尝试登入一个没有附加Minecraft许可的Mojang账号,那么认证将会成功,但是响应将不包含<code>selectedProfile</code>字段,而且<code>availableProfiles</code>数组也是空的。
  
Some instances in the wild have been observed of Mojang returning a flat <code>null</code> for failed refresh attempts against legacy accounts. It's not clear what the actual error tied to the null response is and it is extremely rare, but implementations should be wary of null output from the response.
+
有一些实例曾观察到Mojang对于旧版账号失败的刷新请求返回了一个平坦的<code>null</code>。还不清楚什么实际错误绑定了这个空响应,而且它极为罕见,但作为实现应该注意对该响应的空输出。
  
This endpoint is severely rate-limited: multiple <code>/authenticate</code> requests for the same account in a short amount of time (think 3 requests in a few seconds), even with the correct password, will eventually lead to an <code>Invalid credentials.</code> response. This error clears up a few seconds later.
+
这个端点是严格速率限制的:短时间内同一账号的多次<code>/authenticate</code>请求(例如在几秒内3次请求),即使密码正确也会导致一个<code>Invalid credentials.</code>响应。该错误会在几秒后被清除。
  
== Refresh ==
+
== 刷新 ==
  
Refreshes a valid <code>accessToken</code>. It can be used to keep a user logged in between gaming sessions and is preferred over storing the user's password in a file (see [[lastlogin]]).
+
刷新一个有效的<code>accessToken</code>。它可以用于在游戏会话间保持登录状态,这优于在文件中保存用户的密码(见[[ZH:lastlogin|lastlogin]])。
  
=== Endpoint ===
+
=== 端点 ===
  
 
  /refresh
 
  /refresh
  
=== Payload ===
+
=== 负载 ===
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
{
 
{
     "accessToken": "valid accessToken",
+
     "accessToken": "有效的accessToken",
     "clientToken": "client identifier", // This needs to be identical to the one used
+
     "clientToken": "客户端标识符",         // 这需要与第一处用来获取
                                         // to obtain the accessToken in the first place
+
                                         // accessToken的那个相同
     "selectedProfile": {                // optional; sending it will result in an error
+
     "selectedProfile": {                // 可选的,发送它将导致错误
         "id": "profile identifier",     // hexadecimal
+
         "id": "档案标识符",               // 十六进制
         "name": "player name"
+
         "name": "玩家名称"
 
     },
 
     },
     "requestUser": true                  // optional; default: false; true adds the user object to the response
+
     "requestUser": true                  // 可选的,默认为false,若为true则将user对象加入到响应中
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Note: The provided <code>accessToken</code> gets invalidated.
+
注意:提供的<code>accessToken</code>将失效。
  
=== Response ===
+
=== 响应 ===
  
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
{
 
{
     "accessToken": "random access token",     // hexadecimal
+
     "accessToken": "随机访问令牌",               // 十六进制
     "clientToken": "client identifier",       // identical to the one received
+
     "clientToken": "客户端标识符",               // 与接收到的相同
 
     "selectedProfile": {
 
     "selectedProfile": {
         "id": "profile identifier",           // hexadecimal
+
         "id": "档案标识符",                     // 十六进制
         "name": "player name"
+
         "name": "玩家名称"
 
     },
 
     },
     "user": {                                  // only present if requestUser was true in the request payload
+
     "user": {                                  // 仅在请求负载中的requestUser为true出现
         "id": "user identifier",               // hexadecimal
+
         "id": "用户标识符",                     // 十六进制
 
         "properties": [
 
         "properties": [
 
             {
 
             {
                 "name": "preferredLanguage",  // might not be present for all accounts
+
                 "name": "preferredLanguage",  // 也许不会对所有账号显示
                 "value": "en"                  // Java locale format (https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#toString--)
+
                 "value": "en"                  // Java locale格式(https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#toString--
 
             },
 
             },
 
             {
 
             {
                 "name": "twitch_access_token", // only present if a twitch account is associated (see https://account.mojang.com/me/settings)
+
                 "name": "twitch_access_token", // 仅在关联twitch账号时出现(见https://account.mojang.com/me/settings)
                 "value": "twitch oauth token"  // OAuth 2.0 Token; alphanumerical; e.g. https://api.twitch.tv/kraken?oauth_token=[...]
+
                 "value": "twitch oauth token"  // OAuth 2.0令牌,字母+数字,如https://api.twitch.tv/kraken?oauth_token=[...]
                                               // the Twitch API is documented here: https://github.com/justintv/Twitch-API
+
                                               // Twitch API的文档:https://github.com/justintv/Twitch-API
 
             }
 
             }
 
         ]
 
         ]
Line 242: Line 241:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
== Validate ==
+
== 验证 ==
  
Checks if an <code>accessToken</code> is usable for authentication with a Minecraft server. The Minecraft Launcher (as of version 1.6.13) calls this endpoint on startup to verify that its saved token is still usable, and calls <code>/refresh</code> if this returns an error.
+
检查<code>accessToken</code>是否可用于Minecraft服务器的认证。Minecraft启动器(自1.6.13版本起)会在启动器调用此端点来验证保存的令牌是否仍然可用,并会在返回错误时调用<code>/refresh</code>
  
Note that an <code>accessToken</code> may be unusable for authentication with a Minecraft server, but still be good enough for <code>/refresh</code>. This mainly happens when one has used another client (e.g. played Minecraft on another PC with the same account). It seems only the most recently obtained <code>accessToken</code> for a given account can reliably be used for authentication (the next-to-last token also seems to remain valid, but don't rely on it).
+
请注意<code>accessToken</code>可能会不可用与Minecraft服务器的认证,而对于<code>/refresh</code>来说足够可用。这主要会发生在一个人使用了另一个客户端(如在别的PC上使用相同的帐号游玩了Minecraft)。看起来只有给定帐号最新获得的<code>accessToken</code>才能可靠地用于认证(第二新的令牌看起来也仍然有效,但请不要依赖它)。
  
<code>/validate</code> may be called with or without a <code>clientToken</code>. If a <code>clientToken</code> is provided, it should match the one used to obtain the <code>accessToken</code>. The Minecraft Launcher does send a <code>clientToken</code> to <code>/validate</code>.
+
<code>/validate</code>可以在有或没有<code>clientToken</code>时调用。如果提供了<code>clientToken</code>,它应当与获取<code>accessToken</code>的那个相匹配。Minecraft启动器会向<code>/validate</code>发送<code>clientToken</code>
  
=== Endpoint ===
+
=== 端点 ===
  
 
  /validate
 
  /validate
  
=== Payload ===
+
=== 负载 ===
  
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
{
 
{
     "accessToken": "valid accessToken",
+
     "accessToken": "有效的accessToken",
     "clientToken": "associated clientToken" // optional, see above
+
     "clientToken": "关联的clientToken"   // 可选的,见上
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Response ===
+
=== 响应 ===
  
Returns an empty payload (<code>204 No Content</code>) if successful, an error JSON with status <code>403 Forbidden</code> otherwise.
+
若成功返回空响应(<code>204 No Content</code>),否则返回错误JSON和状态码<code>403 Forbidden</code>
  
== Signout ==
+
== 登出 ==
  
Invalidates <code>accessToken</code>s using an account's username and password.
+
使用帐号的用户名和密码使<code>accessToken</code>失效。
  
=== Endpoint ===
+
=== 端点 ===
  
 
  /signout
 
  /signout
  
=== Payload ===
+
=== 负载 ===
  
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
{
 
{
     "username": "mojang account name",
+
     "username": "mojang帐号名称",
     "password": "mojang account password"
+
     "password": "mojang帐号密码"
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Response ===
+
=== 响应 ===
  
Returns an empty payload if successful.
+
若成功返回一个空负载。
  
== Invalidate ==
+
== 使失效 ==
  
Invalidates <code>accessToken</code>s using a client/access token pair.
+
使用client/access令牌对使<code>accessToken</code>失效。
  
=== Endpoint ===
+
=== 端点 ===
  
 
  /invalidate
 
  /invalidate
  
=== Payload ===
+
=== 负载 ===
  
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
{
 
{
     "accessToken": "valid accessToken",
+
     "accessToken": "有效的accessToken",
     "clientToken": "client identifier"   // This needs to be identical to the one used
+
     "clientToken": "客户端标识符"         // 这需要与第一处用来获取
                                         // to obtain the accessToken in the first place
+
                                         // accessToken的那个相同
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Response ===
+
=== 响应 ===
  
Returns an empty payload if successful.
+
若成功返回一个空负载。
  
== Joining a Server ==
+
== 加入服务器  ==
  
See [[Protocol Encryption#Authentication]]
+
[[ZH:协议加密#认证|协议加密#认证]]
  
[[Category:Protocol Details]]
+
[[Category:ZH:Protocol Details]]
[[Category:Minecraft Modern]]
+
[[Category:ZH:Minecraft Modern]]

Revision as of 09:30, 11 November 2019

Minecraft 1.6引入了一种全新的叫作Yggdrasil的认证方案,它彻底地取代了先前的认证系统。Mojang的其他游戏,Scrolls,同样也使用了该认证方法。Mojang曾经说过每个人都应使用此认证系统来进行自定义登录,但是永远不会从用户收集凭据

请求格式

所有对Yggdrasil的请求都会发送到以下服务器:

https://authserver.mojang.com

此外,它们应满足以下规则:

  • POST请求
  • Content-Type头设置为application/json
  • 以包含JSON编码的字典作为负载

如果请求成功,服务器将响应:

  • 状态码200
  • 根据以下规范使用JSON编码的字典

但如果请求失败,服务器会响应:

{
    "error": "错误的简短描述",
    "errorMessage": "用于向用户显示的更长的描述",
    "cause": "错误原因" // 可选的
}

错误

这些是可能遇到的一些错误:

错误 原因 错误信息 注释
Method Not Allowed The method specified in the request is not allowed for the resource identified by the request URI
请求URI中标识的资源不允许使用请求中指定的方法
收到了除POST请求以外的信息。
Not Found The server has not found anything matching the request URI
服务器未找到与请求URI匹配的任何内容
调用了不存在的端点。
ForbiddenOperationException UserMigratedException Invalid credentials. Account migrated, use e-mail as username.
无效凭据。账号已迁移,使用邮箱作为用户名。
ForbiddenOperationException Invalid credentials. Invalid username or password.
无效凭据。无效的用户名或密码。
ForbiddenOperationException Invalid credentials.
无效凭据。
该用户名最近的尝试登录过多(见/authenticate)。注意用户名和密码可能仍然有效!
ForbiddenOperationException Invalid token.
无效凭据。
accessToken失效了。
IllegalArgumentException Access token already has a profile assigned.
访问令牌已经被分配了档案。
还没有实现选择档案。
IllegalArgumentException credentials is null
凭据为空
用户名/密码未提交。
IllegalArgumentException Invalid salt version
无效的盐版本
???
Unsupported Media Type The server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method
服务器拒绝为请求提供服务,因为请求实体的格式不受请求方法所请求的资源支持
数据未提交为application / json

认证

使用密码认证用户。

端点

/authenticate

负载

{
    "agent": {                              // 默认为Minecraft
        "name": "Minecraft",                // 对于Mojang的其他游戏Scrolls,则应该使用"Scrolls"
        "version": 1                        // 以后的原版客户端
                                            // 可能会增加该数字
    },
    "username": "mojang帐号名",              // 可以是电子邮箱地址或
                                            // 玩家名称(对于为迁移的账号)
    "password": "mojang帐号密码",
    "clientToken": "客户端标识符",            // 可选的
    "requestUser": true                     // 可选的,默认为false,若为true则将user对象加入到响应中
}

clientToken应该是一个随机生成的标识符而且必须每次请求都是相同的。原版启动器会在第一次运行时生成一个随机的(v4)UUID并保存,在后续每次请求中复用它。如果省略,那么服务器会生成一个基于Java的UUID.toString()的随机令牌,它应该由客户端保存下来。然而这也会使用户之前在所有客户端上获取的accessToken失效。

响应

{
    "accessToken": "随机访问令牌",               // 十六进制或JSON-Web-Token(未确认)[普通accessToken可以在JWT的响应中找到(使用‘.’分隔的第二部分,以Base64编码的JSON对象),在“yggt”键中]
    "clientToken": "客户端标识符",               // 与接收到的相同
    "availableProfiles": [                     // 仅在接收到agent字段时出现
        {
            "agent": "minecraft",              // 可能与之前值相同
            "id": "档案标识符",                  // 十六进制
            "name": "玩家名称",
            "userId": "十六进制字符串",
            "createdAt": 1325376000000,        // 自1970年1月1日起的毫秒数
            "legacyProfile": true或false,      // 即使为false也出现
            "suspended": true或false,          // 可能为false
            "paid": true或false,               // 可能为true
            "migrated": true或false,         // 似乎即使是已迁移账号也为false…?(https://bugs.mojang.com/browse/WEB-1461)
            "legacy": true或false              // 它仅为true时出现。默认为false。与较新的legacyProfile重复…
        }
    ],
    "selectedProfile": {                       // 仅在接收到agent字段时出现
        "id": "不含分隔符的uuid",
        "name": "玩家名称",
        "userId": "十六进制字符串",
        "createdAt": 1325376000000,
        "legacyProfile": true或false,
        "suspended": true或false,
        "paid": true或false,
        "migrated": true或false,
        "legacy": true或false
    },
    "user": {                                  // 仅在请求负载中的requestUser为true出现
        "id": "用户标识符",                      // 十六进制
        "email": "user@email.example",         // 未迁移账号的哈希(?)值
        "username": "user@email.example",      // 未迁移账号的正常名称或已迁移账号的电子邮箱
        "registerIp": "198.51.100.*",          // 最后一位打码的IP地址
        "migratedFrom": "minecraft.net",
        "migratedAt": 1420070400000,
        "registeredAt": 1325376000000,         // 也许比profile的createdAt要早几分钟
        "passwordChangedAt": 1569888000000,
        "dateOfBirth": -2208988800000,
        "suspended": false,
        "blocked": false,
        "secured": true,
        "migrated": false,                     // 似乎即使migratedAt和migratedFrom出现时也为false…
        "emailVerified": true,
        "legacyUser": false,
        "verifiedByParent": false,
        "properties": [
            {
                "name": "preferredLanguage",   // 也许不会对所有账号显示
                "value": "en"                  // Java locale格式 (https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#toString--)
            },
            {
                "name": "twitch_access_token", // 仅在关联twitch账号时出现(见https://account.mojang.com/me/settings)
                "value": "twitch oauth token"  // OAuth 2.0令牌,字母+数字,如https://api.twitch.tv/kraken?oauth_token=[...]
                                               // Twitch API的文档:https://github.com/justintv/Twitch-API
            }
        ]
    }
}

注意:如果用户希望能在他们的电脑上保存登录状态,那么强烈建议应该存储accessToken而不是密码本身。

当前每个账号只拥有一个档案,一个账号拥有多个档案还在未来计划中。如果用户尝试登入一个没有附加Minecraft许可的Mojang账号,那么认证将会成功,但是响应将不包含selectedProfile字段,而且availableProfiles数组也是空的。

有一些实例曾观察到Mojang对于旧版账号失败的刷新请求返回了一个平坦的null。还不清楚什么实际错误绑定了这个空响应,而且它极为罕见,但作为实现应该注意对该响应的空输出。

这个端点是严格速率限制的:短时间内同一账号的多次/authenticate请求(例如在几秒内3次请求),即使密码正确也会导致一个Invalid credentials.响应。该错误会在几秒后被清除。

刷新

刷新一个有效的accessToken。它可以用于在游戏会话间保持登录状态,这优于在文件中保存用户的密码(见lastlogin)。

端点

/refresh

负载

{
    "accessToken": "有效的accessToken",
    "clientToken": "客户端标识符",         // 这需要与第一处用来获取
                                         // accessToken的那个相同
    "selectedProfile": {                 // 可选的,发送它将导致错误
        "id": "档案标识符",                // 十六进制
        "name": "玩家名称"
    },
    "requestUser": true                  // 可选的,默认为false,若为true则将user对象加入到响应中
}

注意:提供的accessToken将失效。

响应

{
    "accessToken": "随机访问令牌",               // 十六进制
    "clientToken": "客户端标识符",               // 与接收到的相同
    "selectedProfile": {
        "id": "档案标识符",                      // 十六进制
        "name": "玩家名称"
    },
    "user": {                                  // 仅在请求负载中的requestUser为true出现
        "id": "用户标识符",                      // 十六进制
        "properties": [
            {
                "name": "preferredLanguage",   // 也许不会对所有账号显示
                "value": "en"                  // Java locale格式(https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html#toString--)
            },
            {
                "name": "twitch_access_token", // 仅在关联twitch账号时出现(见https://account.mojang.com/me/settings)
                "value": "twitch oauth token"  // OAuth 2.0令牌,字母+数字,如https://api.twitch.tv/kraken?oauth_token=[...]
                                               // Twitch API的文档:https://github.com/justintv/Twitch-API
            }
        ]
    }
}

验证

检查accessToken是否可用于Minecraft服务器的认证。Minecraft启动器(自1.6.13版本起)会在启动器调用此端点来验证保存的令牌是否仍然可用,并会在返回错误时调用/refresh

请注意accessToken可能会不可用与Minecraft服务器的认证,而对于/refresh来说足够可用。这主要会发生在一个人使用了另一个客户端(如在别的PC上使用相同的帐号游玩了Minecraft)。看起来只有给定帐号最新获得的accessToken才能可靠地用于认证(第二新的令牌看起来也仍然有效,但请不要依赖它)。

/validate可以在有或没有clientToken时调用。如果提供了clientToken,它应当与获取accessToken的那个相匹配。Minecraft启动器会向/validate发送clientToken

端点

/validate

负载

{
    "accessToken": "有效的accessToken",
    "clientToken": "关联的clientToken"    // 可选的,见上
}

响应

若成功返回空响应(204 No Content),否则返回错误JSON和状态码403 Forbidden

登出

使用帐号的用户名和密码使accessToken失效。

端点

/signout

负载

{
    "username": "mojang帐号名称",
    "password": "mojang帐号密码"
}

响应

若成功返回一个空负载。

使失效

使用client/access令牌对使accessToken失效。

端点

/invalidate

负载

{
    "accessToken": "有效的accessToken",
    "clientToken": "客户端标识符"          // 这需要与第一处用来获取
                                         // accessToken的那个相同
}

响应

若成功返回一个空负载。

加入服务器

协议加密#认证