Skip to content

Yggdrasil 服务端技术规范

Haowei Wen edited this page Jul 2, 2022 · 33 revisions

概述

本文旨在为实现 Yggdrasil 服务端提供非官方的技术规范。

本规范中所描述的服务端行为不一定与 Mojang 服务端的相同。 这客观上是因为 Mojang 的服务端是闭源的,我们只能推测其内部逻辑,而所作出的推测难免会与实际存在出入。 但事实上只要客户端能够正确理解并处理服务端的响应,那么其行为是否与 Mojang 服务端的相同,也就无关紧要了。

基本约定

字符编码

本文中字符编码一律使用 UTF-8。

请求与响应格式

若无特殊说明,请求与响应均为 JSON 格式(如果有 body),Content-Type 均为 application/json; charset=utf-8

所有 API 都应该使用 HTTPS 协议。

错误信息格式

{
	"error":"错误的简要描述(机器可读)",
	"errorMessage":"错误的详细信息(人类可读)",
	"cause":"该错误的原因(可选)"
}

当遇到本文中已说明的异常情况时,返回的错误信息应符合对应的要求。

下表列举了常见异常情况下的错误信息。除特殊说明外,cause 一般不包含。

非标准指由于无法使 Mojang 的 Yggdrasil 服务器触发对应异常,而只能推测该种情况下的错误信息。

未定义指该项并没有明确要求。

异常情况 HTTP状态码 Error Error Message
一般 HTTP 异常(非业务异常,如 Not FoundMethod Not Allowed 未定义 该 HTTP 状态对应的 Reason Phrase(于 HTTP/1.1 中定义) 未定义
令牌无效 403 ForbiddenOperationException Invalid token.
密码错误,或短时间内多次登录失败而被暂时禁止登录 403 ForbiddenOperationException Invalid credentials. Invalid username or password.
试图向一个已经绑定了角色的令牌指定其要绑定的角色 400 IllegalArgumentException Access token already has a profile assigned.
试图向一个令牌绑定不属于其对应用户的角色 (非标准) 403 ForbiddenOperationException 未定义
试图使用一个错误的角色加入服务器 403 ForbiddenOperationException Invalid token.

数据格式

我们约定以下数据格式

  • 无符号 UUID: 指去掉所有 - 字符后的 UUID 字符串

模型

用户

一个系统中可以存在若干个用户,用户具有以下属性:

  • ID
  • 邮箱
  • 密码

其中 ID 为一个无符号 UUID。邮箱可以变更,但需要保证唯一。

用户信息的序列化

用户信息序列化后符合以下格式:

{
	"id":"用户的 ID",
	"properties":[ // 用户的属性(数组,每一元素为一个属性)
		{ // 一项属性
			"name":"属性的名称",
			"value":"属性的值",
		}
		// ,...(可以有更多)
	]
}

用户属性中目前已知的项目如下:

名称
preferredLanguage (可选) 用户的偏好语言,例如 enzh_CN

角色(Profile)

Mojang 当前不支持多角色,不保证多角色部分内容的正确性。

角色与账号为多对一关系。一个角色对应 Minecraft 中的一个实体玩家。角色具有以下属性:

  • UUID
  • 名称
  • 材质模型,可选值有:defaultslim
    • default:正常手臂宽度(4px)的皮肤
    • slim:细手臂(3px)的皮肤
  • 材质
    • 类型为映射
    • key 可选值有:SKIN、CAPE
    • value 类型为 URL

UUID 和名称均为全局唯一,但名称可变。应避免使用名称作为标识。

角色 UUID 的生成

若不考虑兼容性,角色的 UUID 一般为随机生成(Version 4)。

但 Minecraft 仅使用 UUID 作为角色标识符,不同 UUID 的角色即使名称相同也被认为是不同的。如果一个 Minecraft 服务器从其他登录系统(正版验证、离线验证或其他)迁移到本登录系统,并且角色的 UUID 发生了变化,则该角色的数据将丢失。为了避免这种情况,必须保证对于同一个角色,本系统生成的 UUID 与其在先前系统中的 UUID 是相同的。

兼容离线验证

若 Minecraft 服务器原先采用的是离线验证,则角色 UUID 是角色名称的一元函数。如果 Yggdrasil 服务端使用此方法生成角色 UUID,就可以实现与离线验证系统之间的双向兼容,即可以在不丢失角色数据的情况下,在离线验证系统和本登录系统之间切换。

从角色名称计算角色 UUID 的代码如下(Java):

UUID.nameUUIDFromBytes(("OfflinePlayer:" + characterName).getBytes(StandardCharsets.UTF_8))

在其他语言中的实现:

角色信息的序列化

角色信息序列化后符合以下格式:

{
	"id":"角色 UUID(无符号)",
	"name":"角色名称",
	"properties":[ // 角色的属性(数组,每一元素为一个属性)(仅在特定情况下需要包含)
		{ // 一项属性
			"name":"属性的名称",
			"value":"属性的值",
			"signature":"属性值的数字签名(仅在特定情况下需要包含)"
		}
		// ,...(可以有更多)
	]
}

角色属性(properties)及数字签名(signature)在无特殊说明的情况下不需要包含。

signature 是属性值的数字签名,使用 Base64 编码。签名算法为 SHA1withRSA,见 PKCS #1。关于签名密钥的详细介绍,见 签名密钥对

角色属性中可以包含以下项目:

名称
textures (可选)Base64 编码的 JSON 字符串,包含了角色的材质信息,详见 §textures 材质信息属性
uploadableTextures (可选)该角色可以上传的材质类型,为 authlib-injector 自行规定的属性,详见 §uploadableTextures 可上传的材质类型

textures 材质信息属性

以下为材质信息的格式,将这段 JSON 进行 Base64 编码后,即为 textures 角色属性的值。

{
	"timestamp":该属性值被生成时的时间戳(Java 时间戳格式,即自 1970-01-01 00:00:00 UTC 至今经过的毫秒数),
	"profileId":"角色 UUID(无符号)",
	"profileName":"角色名称",
	"textures":{ // 角色的材质
		"材质类型(如 SKIN)":{ // 若角色不具有该项材质,则不必包含
			"url":"材质的 URL",
			"metadata":{ // 材质的元数据,若没有则不必包含
				"名称":"值"
				// ,...(可以有更多)
			}
		}
		// ,...(可以有更多)
	}
}

材质元数据中目前已知的项目有 model,其对应该角色的材质模型,取值为 defaultslim

uploadableTextures 可上传的材质类型

注意: 这一角色属性是由 authlib-injector 文档规定的,Mojang 返回的角色属性是不包含这一项的。Mojang 仅允许用户上传皮肤,不允许上传披风。

考虑到并非所有验证服务器都允许用户上传皮肤和披风,因此 authlib-injector 规定了 uploadableTextures 角色属性,其表示角色可以上传的材质类型。

该属性的值是一个逗号分隔的列表,包含了可以上传的材质类型。材质类型目前有 skincape 两种。

例如,uploadableTextures 属性的值若为 skin,则表示可以为该角色上传皮肤,但不能上传披风;值若为 skin,cape,则既可以上传皮肤,又可以上传披风。

如果不存在 uploadableTextures 属性,则不能为该角色上传任何类型的材质。

关于材质上传接口的介绍,请参考 §材质上传

材质 URL 规范

Minecraft 将材质 hash 作为材质的标识。每当客户端下载一个材质后,便会将其缓存在本地,以后若需要相同 hash 的材质,则会直接使用缓存。 而这个 hash 并不是由客户端计算的。Yggdrasil 服务端应先计算好材质 hash,将其作为材质 URL 的文件名,即从 URL 最后一个 /(不包括)开始一直到结尾的这一段子串。 而客户端会直接将 URL 的文件名作为材质的 hash。

例如下面这个 URL,它所代表的材质的 hash 为 e051c27e803ba15de78a1d1e83491411dffb6d7fd2886da0a6c34a2161f7ca99

https://yggdrasil.example.com/textures/e051c27e803ba15de78a1d1e83491411dffb6d7fd2886da0a6c34a2161f7ca99

安全警告:

  • 材质 URL 响应头中的 Content-Type 必须为 image/png。若未指定,则存在 MIME Sniffing Attack 的风险。

材质 hash 的计算方法服务端可自行选择,建议使用 SHA-256 或者更加安全的 hash 算法。作为参考,Mojang 官方的方法是计算图像文件的 SHA-256。

用户上传材质的安全性

安全警告:

  • 若不对用户上传材质进行处理,则可能导致远程代码执行
  • 在读取材质前,若不先检查图像大小,则可导致拒绝服务攻击

关于此安全缺陷的详细信息:未经检查的用户上传材质可能导致远程代码执行 #10

除了位图数据外,PNG 文件还可以存储其他数据。如果 Yggdrasil 服务端不对用户上传的材质进行检查,则攻击者可以在其中藏匿恶意代码,并通过 Yggdrasil 服务端分发到客户端。因此,Yggdrasil 服务端必须对用户上传的材质进行处理,除去其中任何与位图无关的数据。具体做法如下:

  1. 读取该 PNG 文件中图像的大小,如果过大则应拒绝。
    • 即使是非常小的 PNG 文件也可以存储一幅足以消耗计算机所有内存的图像(即 PNG Bomb),因此切不可在检查图像大小前就将其完整读入。
  2. 检查图像是否为合法的皮肤/披风材质。
    • 皮肤的宽高为 64x32 的整数倍或 64x64 的整数倍,披风的宽高为 64x32 的整数倍或 22x17 的整数倍。宽高为 22x17 整数倍的披风并非标准尺寸的披风,服务端需要用透明像素将其宽高补足至 64x32 的整数倍。
  3. 将图像文件重新保存,去除无关的元数据。

实现提示:在 Java 中可使用 ImageReader.getWidth() 在不读入整个图像的情况下获取其尺寸。

令牌(Token)

令牌与账号为多对一关系。令牌是一种登录凭证,具有时效性。令牌具有以下属性:

  • accessToken
  • clientToken
  • 绑定的角色
  • 颁发时间

其中 accessTokenclientToken 为任意字符串(可以是无符号 UUID 或 JWT)。accessToken 由服务端随机生成,clientToken 由客户端提供。

介于 accessToken 的随机性,它可以被作为主键。而 clientToken 不具有唯一性。

绑定的角色可以为空。它代表了能使用该令牌进行游戏的角色。

一个用户可以同时有多个令牌,但服务端也应该对令牌数量加以限制。当令牌数量超出限制(如 10 个)时,则应先吊销最旧的令牌,之后再颁发新的令牌。

令牌的状态

令牌有以下三种状态:

  • 有效
    • 处于该状态的令牌可以进行各项操作,如进服验证刷新
    • 新颁发的令牌(即通过登录刷新颁发的令牌)处于该状态。
  • 暂时失效
    • 处于该状态的令牌除了进行刷新操作外,无权进行任何操作。
    • 当令牌绑定的角色改名后,令牌应被标记为暂时失效状态。
      • 这是为了让启动器刷新令牌,从而获取到新的角色名称。(见 #40
    • 该状态并不是必须要实现的(详细介绍见下)。
  • 无效
    • 处于该状态的令牌无权进行任何操作。
    • 令牌被吊销后处于该状态。这里的吊销包括显式吊销登出刷新后吊销原令牌、令牌过期。

令牌的状态只能由有效变为无效,或是由有效变为暂时失效再变为无效,这个过程是不可逆的。 刷新操作仅颁发一个新的令牌,并不能使原令牌重新回到有效状态。

令牌应当有一个过期时限(如 15 天)。当自颁发起所经过的时间超过该时限时,令牌过期。

关于暂时失效状态

Mojang 对暂时失效状态的实现是这样的: 对启动器而言,若令牌处于暂时失效状态,则会刷新令牌,获得一个新的处于有效状态的令牌; 对 Yggdrasil 服务端而言,仅最后颁发的令牌才是有效的,先前颁发的其它令牌都处于暂时失效状态。

Mojang 之所以这么做,可能是为了防止用户多地同时登录(仅使最后一个 session 有效)。但事实上,即使服务端没有实现暂时失效状态,启动器的逻辑也是可以正常工作的。

当然,就算我们要实现暂时失效状态,也并不需要以 Mojang 的实现为范本。只需要启动器能够正确处理,任何实现都是可以的。下面给出一个不同于 Mojang 的实现的例子:

取一个短于令牌过期时限的时间段作为有效和暂时失效的分界点。若自颁发起经过的时间在该时限内,则令牌有效;若超过该时限,但仍在过期时限内,则令牌暂时失效。

这种做法实现了这样的功能:玩家如果经常进行登录操作,除了第一次登录就不需要输入密码了。而当他长时间未登录时则需要重新输入密码。

Yggdrasil API

用户部分

登录

POST /authserver/authenticate

使用密码进行身份验证,并分配一个新的令牌。

请求格式:

{
	"username":"邮箱(或其他凭证,详见 §使用角色名称登录)",
	"password":"密码",
	"clientToken":"由客户端指定的令牌的 clientToken(可选)",
	"requestUser":true/false, // 是否在响应中包含用户信息,默认 false
	"agent":{
		"name":"Minecraft",
		"version":1
	}
}

若请求中未包含 clientToken,服务端应该随机生成一个无符号 UUID 作为 clientToken。但需要注意 clientToken 可以为任何字符串,即请求中提供任何 clientToken 都是可以接受的,不一定要为无符号 UUID。

对于令牌要绑定的角色:若用户没有任何角色,则为空;若用户仅有一个角色,那么通常绑定到该角色;若用户有多个角色,通常为空,以便客户端进行选择。也就是说如果绑定的角色为空,则需要客户端进行角色选择。

响应格式:

{
	"accessToken":"令牌的 accessToken",
	"clientToken":"令牌的 clientToken",
	"availableProfiles":[ // 用户可用角色列表
		// ,... 每一项为一个角色(格式见 §角色信息的序列化)
	],
	"selectedProfile":{
		// ... 绑定的角色,若为空,则不需要包含(格式见 §角色信息的序列化)
	},
	"user":{
		// ... 用户信息(仅当请求中 requestUser 为 true 时包含,格式见 §用户信息的序列化)
	}
}

安全提示: 该 API 可以被用于密码暴力破解,应受到速率限制。限制应针对用户,而不是客户端 IP。

使用角色名称登录

除使用邮箱登录外,验证服务器还可以允许用户使用角色名称登录。要实现这一点,验证服务器需要进行以下工作:

当用户使用角色名称登录时,验证服务器应自动将令牌绑定到相应角色,即上文响应中的 selectedProfile 应为用户登录时所用的角色。

这种情况下,如果用户拥有多个角色,那么他可以省去选择角色的操作。考虑到某些程序不支持多角色(例如 Geyser),还可以通过上述方法绕过角色选择。

刷新

POST /authserver/refresh

吊销原令牌,并颁发一个新的令牌。

请求格式:

{
	"accessToken":"令牌的 accessToken",
	"clientToken":"令牌的 clientToken(可选)",
	"requestUser":true/false, // 是否在响应中包含用户信息,默认 false
	"selectedProfile":{
		// ... 要选择的角色(可选,格式见 §角色信息的序列化)
	}
}

当指定 clientToken 时,服务端应检查 accessTokenclientToken 是否有效,否则只需要检查 accessToken

颁发的新令牌的 clientToken 应与原令牌的相同。

如果请求中包含 selectedProfile,那么这就是一个选择角色的操作。此操作要求原令牌所绑定的角色为空,而新令牌则将绑定到 selectedProfile 所指定的角色上。如果不包含 selectedProfile,那么新令牌所绑定的角色和原令牌相同。

刷新操作在令牌暂时失效时依然可以执行。若请求失败,原令牌依然有效。

响应格式:

{
	"accessToken":"新令牌的 accessToken",
	"clientToken":"新令牌的 clientToken",
	"selectedProfile":{
		// ... 新令牌绑定的角色,若为空,则不需要包含(格式见 §角色信息的序列化)
	},
	"user":{
		// ... 用户信息(仅当请求中 requestUser 为 true 时包含,格式见 §用户信息的序列化)
	}
}

验证令牌

POST /authserver/validate

检验令牌是否有效。

请求格式:

{
	"accessToken":"令牌的 accessToken",
	"clientToken":"令牌的 clientToken(可选)"
}

当指定 clientToken 时,服务端应检查 accessTokenclientToken 是否有效,否则只需要检查 accessToken

若令牌有效,服务端应返回 HTTP 状态 204 No Content,否则作为令牌无效的异常情况处理。

吊销令牌

POST /authserver/invalidate

吊销给定令牌。

请求格式:

{
	"accessToken":"令牌的 accessToken",
	"clientToken":"令牌的 clientToken(可选)"
}

服务端只需要检查 accessToken,即无论 clientToken 为何值都不会造成影响。

无论操作是否成功,服务端应返回 HTTP 状态 204 No Content

登出

POST /authserver/signout

吊销用户的所有令牌。

请求格式:

{
	"username":"邮箱",
	"password":"密码"
}

若操作成功,服务端应返回 HTTP 状态 204 No Content

安全提示: 该 API 也可用于判断密码的正确性,因此应受到和登录 API 一样的速率限制。

会话部分

Minecraft 玩家进服原理

上图使用 ProcessOn 绘制,导出为 SVG。原始图像

该部分用于角色进入服务器时的验证。主要流程如下:

  1. Minecraft 服务端Minecraft 客户端共同生成一段字符串(serverId),其可以被认为是随机的
  2. Minecraft 客户端serverId 及令牌发送给 Yggdrasil 服务端(要求令牌有效)
  3. Minecraft 服务端请求 Yggdrasil 服务端检查客户端会话的有效性,即客户端是否成功进行第 2 步

客户端进入服务器

POST /sessionserver/session/minecraft/join

记录服务端发送给客户端的 serverId,以备服务端检查。

请求格式:

{
	"accessToken":"令牌的 accessToken",
	"selectedProfile":"该令牌绑定的角色的 UUID(无符号)",
	"serverId":"服务端发送给客户端的 serverId"
}

仅当 accessToken 有效,且 selectedProfile 与令牌所绑定的角色一致时,操作才成功。

服务端应记录以下信息:

  • serverId
  • accessToken
  • 发送该请求的客户端 IP

实现时请注意:以上信息应记录在内存数据库中(如 Redis),且应该设置过期时间(如 30 秒)。 介于 serverId 的随机性,可以将其作为主键。

若操作成功,服务端应返回 HTTP 状态 204 No Content

服务端验证客户端

GET /sessionserver/session/minecraft/hasJoined?username={username}&serverId={serverId}&ip={ip}

检查客户端会话的有效性,即数据库中是否存在该 serverId 的记录,且信息正确。

请求参数:

参数
username 角色的名称
serverId 服务端发送给客户端的 serverId
ip (可选) Minecraft 服务端获取到的客户端 IP,仅当 prevent-proxy-connections 选项开启时包含

username 需要与 serverId 所对应令牌所绑定的角色的名称相同。

响应格式:

{
	// ... 令牌所绑定角色的完整信息(包含角色属性及数字签名,格式见 §角色信息的序列化)
}

若操作失败,服务端应返回 HTTP 状态 204 No Content

角色部分

该部分用于角色信息的查询。

查询角色属性

GET /sessionserver/session/minecraft/profile/{uuid}?unsigned={unsigned}

查询指定角色的完整信息(包含角色属性)。

请求参数:

参数
uuid 角色的 UUID(无符号)
unsigned (可选) truefalse。是否在响应中不包含数字签名,默认为 true

响应格式:

{
	// ... 角色信息(包含角色属性。若 unsigned 为 false,还需要包含数字签名。格式见 §角色信息的序列化)
}

若角色不存在,服务端应返回 HTTP 状态 204 No Content

按名称批量查询角色

POST /api/profiles/minecraft

批量查询角色名称所对应的角色。

请求格式:

[
	"角色名称"
	// ,... 还可以有更多
]

服务端查询各个角色名称所对应的角色信息,并将其包含在响应中。不存在的角色不需要包含。响应中角色信息的先后次序无要求。

响应格式:

[
	{
		// 角色信息(注意:不包含角色属性。格式见 §角色信息的序列化)
	}
	// ,...(可以有更多)
]

安全提示: 为防止 CC 攻击,需要为单次查询的角色数目设置最大值,该值至少为 2。

材质上传

PUT /api/user/profile/{uuid}/{textureType}
DELETE /api/user/profile/{uuid}/{textureType}

设置或清除指定角色的材质。

并非所有角色都可以上传皮肤和披风。要获取当前角色能够上传的材质类型,参见 §uploadableTextures 可上传的材质类型

请求参数:

参数
uuid 角色的 UUID(无符号)
textureType 材质类型,可以为 skin(皮肤)或 cape(披风)

请求需要带上 HTTP 头部 Authorization: Bearer {accessToken} 进行认证。若未包含 Authorization 头或 accessToken 无效,则返回 401 Unauthorized

如果操作成功,则返回 204 No Content

下面分别介绍 PUT 和 DELETE 这两个 HTTP 方法的用法:

PUT 上传材质

请求的 Content-Typemultipart/form-data,请求载荷由以下部分组成:

名称(name) 内容
model (仅用于皮肤) 皮肤的材质模型,可以为 slim(细胳膊皮肤)或空字符串(普通皮肤)。
file 材质图像,Content-Type 须为 image/png
建议客户端设置 Content-Disposition 中的 filename 参数为材质图像的文件名,这可以被验证服务器用作材质的备注。

如果操作成功,则返回 204 No Content

DELETE 清除材质

清除材质后,该类型的材质将恢复为默认。

扩展 API

以下 API 是为了方便 authlib-injector 进行自动配置而设计的。

API 元数据获取

GET /

响应格式:

{
	"meta":{
		// 服务端的元数据,内容任意
	},
	"skinDomains":[ // 材质域名白名单
		"域名匹配规则 1"
		// ,...
	],
	"signaturePublickey":"用于验证数字签名的公钥"
}

signaturePublickey 是 PEM 格式的公钥,用于验证角色属性的数字签名。其以 -----BEGIN PUBLIC KEY----- 开头,以 -----END PUBLIC KEY----- 结尾,中间允许出现换行符,但不允许出现其他空白字符(亦允许文末出现换行符)。

材质域名白名单

Minecraft 仅会从白名单中的域名下载材质。如果材质 URL 的域名不在白名单中,则会出现 Textures payload has been tampered with (non-whitelisted domain) 错误。采用此机制的原因见 MC-78491

材质白名单默认包含 .minecraft.net.mojang.com 两项规则,你可以设置 skinDomains 属性以添加额外的白名单规则。规则格式如下:

  • 如果规则以 .(dot)开头,则匹配以这一规则结尾的域名。
    • 例如 .example.com 匹配 a.example.comb.a.example.com不匹配 example.com
  • 如果规则不以 .(dot)开头,则匹配的域名须与规则完全相同
    • 例如 example.com 匹配 example.com不匹配 a.example.comeexample.com

meta 中的元数据

meta 中的内容没有强制要求,以下字段均为可选。

服务端基本信息

Key Value
serverName 服务器名称
implementationName 服务端实现的名称
implementationVersion 服务端实现的版本

服务器网址

如果您需要在启动器中展示验证服务器首页地址、注册页面地址等信息,您可以在 meta 中添加一个 links 字段。

links 字段的类型是对象,其中可以包含:

Key Value
homepage 验证服务器首页地址
register 注册页面地址

功能选项

以下带有 (advanced) 标注的字段为高级选项,通常情况下不需要设置。

Key Value
feature.non_email_login 布尔值,指示验证服务器是否支持使用邮箱之外的凭证登录(如角色名登录),默认为 false。
详情见 §使用角色名称登录
feature.legacy_skin_api (advanced) 布尔值,指示验证服务器是否支持旧式皮肤 API,即 GET /skins/MinecraftSkins/{username}.png
当未指定或值为 false 时,authlib-injector 会使用内建的 HTTP 服务器在本地处理对该 API 的请求;若值为 true,请求将由验证服务器处理。
详情见 README § 参数 中的 -Dauthlibinjector.legacySkinPolyfill 选项。
feature.no_mojang_namespace (advanced) 布尔值,是否禁用 authlib-injector 的 Mojang 命名空间(@mojang 后缀)功能,默认为 false。
详情见 README § 参数 中的 -Dauthlibinjector.mojangNamespace 选项。
feature.enable_mojang_anti_features (advanced) 布尔值,是否开启 Minecraft 的 anti-features,默认为 false。
详情见 README § 参数 中的 -Dauthlibinjector.mojangAntiFeatures 选项。
feature.enable_profile_key (advanced) 布尔值,指示验证服务器是否支持 Minecraft 的消息签名密钥对功能(即多人游戏中聊天消息的数字签名),默认为 false。
开启后,Minecraft 将通过 POST /minecraftservices/player/certificates 这一 API 获取验证服务器颁发的密钥对。当未指定或为 false 时,Minecraft 将不会在聊天消息中包含数字签名。
详情见 README § 参数 中的 -Dauthlibinjector.profileKey 选项。
feature.username_check (advanced) 布尔值,指示 authlib-injector 是否启用用户名验证功能,默认为 false。
开启后用户名中带有特殊字符的玩家将无法加入服务器,详情见 README § 参数 中的 -Dauthlibinjector.usernameCheck 选项。

响应示例

{
    "meta": {
        "implementationName": "yggdrasil-mock-server",
        "implementationVersion": "0.0.1",
        "serverName": "yushijinhun's Example Authentication Server",
        "links": {
            "homepage": "https://skin.example.com/",
            "register": "https://skin.example.com/register"
        },
        "feature.non_email_login": true
    },
    "skinDomains": [
        "example.com",
        ".example.com"
    ],
    "signaturePublickey": "-----BEGIN PUBLIC KEY-----\nMIICIj...(省略)...EAAQ==\n-----END PUBLIC KEY-----\n"
}

API 地址指示(ALI)

API 地址指示(API Location Indication,简称 ALI)是一个 HTTP 响应头字段 X-Authlib-Injector-API-Location,起到服务发现的作用。ALI 的值为相对 URL 或绝对 URL,它指向与当前页面相关联的 Yggdrasil API。

使用 ALI 后,用户只需输入一个与 Yggdrasil API 相关联的地址即可,不必输入真正的 API 地址。例如,https://skin.example.com/api/yggdrasil/ 可以被简化为 skin.example.com。支持 ALI 的启动器会请求 (https://)skin.example.com,识别响应中的 ALI 头字段,并根据它找到真正的 API 地址。

皮肤站可以在首页,或在全站启用 ALI。启用 ALI 的方法为在 HTTP 响应中添加 X-Authlib-Injector-API-Location 头字段,例如:

X-Authlib-Injector-API-Location: /api/yggdrasil/  # 使用相对 URL
X-Authlib-Injector-API-Location: https://skin.example.com/api/yggdrasil/  # 亦可使用绝对 URL,支持跨域

当一个页面的 ALI 指向其本身时,这个 ALI 会被忽略。

参见

参考实现

yggdrasil-mock 为本规范的参考实现。