Difference between revisions of "Zh:Authentication"

From wiki.vg
Jump to navigation Jump to search
m
 
(4 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Minecraft 1.6引入了一种全新的叫作'''Yggdrasil'''的认证方案,它彻底地取代了[[ZH:旧版认证|先前的认证系统]]。Mojang的其他游戏,Scrolls,同样也使用了该认证方法。Mojang曾经说过[https://twitter.com/KrisJelbring/status/453573406341206016 每个人都应使用此认证系统来进行自定义登录],但是[https://twitter.com/KrisJelbring/status/461390585086361600 永远不会从用户收集凭据]。
+
Minecraft 1.6引入了一种全新的叫作'''Yggdrasil'''的认证方案,它彻底地取代了[[ZH:Legacy Authentication|先前的认证系统]]。Mojang的其他游戏,Scrolls,同样也使用了该认证方法。Mojang曾经说过[https://twitter.com/KrisJelbring/status/453573406341206016 每个人都应使用此认证系统来进行自定义登录],但是[https://twitter.com/KrisJelbring/status/461390585086361600 永远不会从用户收集凭据]。
  
 
== 请求格式 ==
 
== 请求格式 ==
Line 123: Line 123:
 
<syntaxhighlight lang="javascript">
 
<syntaxhighlight lang="javascript">
 
{
 
{
     "accessToken": "随机访问令牌",              // 十六进制
+
     "accessToken": "随机访问令牌",              // 十六进制或JSON-Web-Token(未确认)[普通accessToken可以在JWT的响应中找到(使用‘.’分隔的第二部分,以Base64编码的JSON对象),在“yggt”键中]
 
     "clientToken": "客户端标识符",              // 与接收到的相同
 
     "clientToken": "客户端标识符",              // 与接收到的相同
 
     "availableProfiles": [                    // 仅在接收到agent字段时出现
 
     "availableProfiles": [                    // 仅在接收到agent字段时出现
 
         {
 
         {
 +
            "agent": "minecraft",              // 可能与之前值相同
 
             "id": "档案标识符",                  // 十六进制
 
             "id": "档案标识符",                  // 十六进制
 
             "name": "玩家名称",
 
             "name": "玩家名称",
             "legacy": true或false              // 事实上它仅为true时出现。默认为false。
+
            "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重复…
 
         }
 
         }
 
     ],
 
     ],
Line 135: Line 142:
 
         "id": "不含分隔符的uuid",
 
         "id": "不含分隔符的uuid",
 
         "name": "玩家名称",
 
         "name": "玩家名称",
 +
        "userId": "十六进制字符串",
 +
        "createdAt": 1325376000000,
 +
        "legacyProfile": true或false,
 +
        "suspended": true或false,
 +
        "paid": true或false,
 +
        "migrated": true或false,
 
         "legacy": true或false
 
         "legacy": true或false
 
     },
 
     },
 
     "user": {                                  // 仅在请求负载中的requestUser为true出现
 
     "user": {                                  // 仅在请求负载中的requestUser为true出现
 
         "id": "用户标识符",                      // 十六进制
 
         "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": [
 
         "properties": [
 
             {
 
             {
Line 285: Line 313:
 
见[[ZH:协议加密#认证|协议加密#认证]]
 
见[[ZH:协议加密#认证|协议加密#认证]]
  
[[Category:ZH:协议细节]]
+
[[Category:ZH:Protocol Details]]
 
[[Category:ZH:Minecraft Modern]]
 
[[Category:ZH:Minecraft Modern]]

Latest 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的那个相同
}

响应

若成功返回一个空负载。

加入服务器

协议加密#认证