Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

49 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Yggdrasil Connect for Blessing Skin

本插件实现了 Yggdrasil 服务端技术规范,可与 authlib-injector 及支持的启动器配合实现 Minecraft 外置登录身份验证,并内置了基于 Yggdrasil Connect 协议 的 OpenID Connect 服务端,无需额外部署独立服务即可实现 OAuth 身份验证。要了解更多关于 Yggdrasil Connect 的信息,请阅读下面的 关于 Yggdrasil Connect 部分。

本插件由原版 Yggdrasil API 插件重构而来,并整合了原 Janus 项目的 OpenID Connect 功能。本插件使用 Laravel Passport 的个人访问令牌(Personal Access Token)作为访问令牌,通过在 JWT Payload 中添加角色 UUID 并重新签名实现访问令牌与角色的绑定。首次启用本插件时,请务必按照下方的 插件使用方法 部分中的说明执行操作,否则本插件可能无法正常工作。

本插件不需要也不能与原版 Yggdrasil API 插件同时启用,但插件数据可与原版 Yggdrasil Connect 插件通用。如需从原版 Yggdrasil API 插件迁移至本插件,请务必按照下方的 插件使用方法 部分的第三步处理 uuid 表,否则本插件无法正常工作。

本插件在一定程度上修复了原版 Yggdrasil API 插件的多个 Bug。要了解具体细节,请阅读下面的 关于 Bug 修复 部分。

插件使用方法

  1. 如果你已经安装了基于原版 Yggdrasil API 插件修改的旧版插件,请务必在下载新版插件前禁用旧版插件,否则可能出现 Invalid version string 错误。
  2. 复制下载链接 https://mc.sjtu.cn/union,粘贴至 插件管理 - 从远程下载 - URL,点击提交按钮安装并启用本插件。
  3. 进入终端,在 Blessing Skin Server 根目录下执行 php artisan yggc:create-personal-access-client 命令,创建个人访问客户端(Personal Access Client)。
    • 创建完成后,请在 .env 中新建一条配置 PASSPORT_PERSONAL_ACCESS_CLIENT_ID,并将其值设为命令返回的个人访问客户端的 Client ID。
  4. 如果你是从原版 Yggdrasil API 插件迁移而来,请在终端中执行 php artisan yggc:fix-uuid-table 命令,以清除原版 Yggdrasil API 插件的 UUID 表中可能存在的异常数据,并修改数据表结构。
    • 该指令会直接删除 uuid 表中的部分记录,因此在执行该指令前,请务必备份原先的 uuid 表!!!
      • 要了解该指令对你的 uuid 表都做了什么,请阅读下面的 关于 Bug 修复 部分。
    • 如果你没有安装过原版 Yggdrasil API 而直接安装了本插件,则无需执行这条命令。
  5. 如需启用 Yggdrasil Connect(OpenID Connect 服务端),请确保站点的 site_url 已正确配置为 HTTPS 地址。Yggdrasil Connect 功能在 site_url 配置完成后自动启用,无需额外部署。
    • Yggdrasil Connect 的 OpenID Discovery 端点位于 /.well-known/openid-configuration
    • 所有 Yggdrasil Connect 的 API 端点均位于 /yggc/ 路径下,具体端点信息可通过 Discovery 端点获取。
    • 可在插件配置页面的「Yggdrasil Connect」选项卡中配置设备码过期时间、授权过期时间、共享客户端 ID 等参数。
    • 如需禁用传统 Auth Server(即通过用户名密码登录的方式),可在配置页面中勾选「禁用 Auth Server」。
  6. 如果你之前独立部署了 Janus 项目,可以将其停用。本插件已内置 Janus 的全部功能,无需再独立运行。

基于 CNB 流水线的插件更新机制(可选)

默认情况下,联盟服务器下发「更新插件」请求时,本插件会直接下载 zip 覆盖插件目录(UpdateController::update)。如果你在本地对插件做过改动,这种覆盖方式会丢失你的改动。

本插件提供一种可选的更新模式:不再下载 zip,而是触发 CNB 流水线,把上游更新合并进你的 CNB 仓库,再让本地插件强制同步。这样你的本地改动会在合并时保留,而不是被覆盖。

工作流程

联盟下发「更新插件」请求
  → UpdateController 立即返回 200(受理成功),联盟不再等待
  → 后台启动 yggc:cnb-sync 命令(独立 PHP 进程)
      → 调用 CNB OpenAPI 触发 api_trigger_upstream_sync 流水线
      → 流水线在 master 分支 fetch 上游 → git merge upstream
          ├─ 无冲突:push 回 CNB 仓库
          └─ 有冲突:不 push,通过 CNB OpenAPI 创建 Issue 通知,构建失败
      → 合并成功后流水线回调插件服务器 /api/cnb/sync-callback(带回调令牌)
      → 插件收到回调后强制同步(fetch + reset --hard)完成更新

启用步骤

  1. 将本地插件目录改为 git 仓库,remote 指向你的 CNB 仓库(https://cnb.cool/<组织>/<仓库>.git),分支设为 master
  2. 在 CNB 访问令牌页面创建一个最小权限令牌(至少需要 repo-cnb-trigger),填入插件配置页「CNB 流水线更新」选项卡的 CNB 访问令牌
  3. 生成一个随机回调令牌(如 openssl rand -hex 32),分别填入:
    • 插件配置页「CNB 流水线更新」的 CNB 回调令牌
    • CNB 密钥仓库 ecustmc/keyenv.yml 中,新增 CNB_CALLBACK_SECRET 变量(值相同)。
  4. 在插件配置页「CNB 流水线更新」选项卡中填写:
    • 启用基于 CNB 流水线的更新:勾选
    • CNB 仓库:你的 CNB 仓库路径,如 ecustmc/yggdrasil-connect
    • 更新分支master
    • 上游仓库 URL:上游 git 地址,如 https://github.com/MUAlliance/yggdrasil-connect.git
    • 上游分支main
    • PHP CLI 路径(可选):留空默认 php;若后台更新进程未启动,请填 PHP CLI 绝对路径(如 /usr/bin/php
  5. 确保 CNB 仓库的 .cnb.yml 已包含 api_trigger_upstream_sync 流水线(本插件自带,见仓库根目录 .cnb.yml)。

注意

  • 流水线内 push 回 CNB 仓库、创建 Issue 使用的是 CNB 流水线运行期临时令牌 CNB_TOKEN,权限由 CNB 平台按可信事件自动分配,无需额外配置。令牌 cnb_token 仅用于触发流水线,请妥善保管。
  • 联盟请求会立即收到 200 响应(异步受理);流水线合并成功后通过回调通知插件强制同步,不再轮询。日志写入 storage/logs/ygg-cnb-sync.logyggdrasil.log

部署注意事项

  1. 插件目录属主必须是 PHP-FPM 运行用户(通常是 www)。若目录由 root 克隆、PHP-FPM 以 www 运行:
    • 只读校验(rev-parseremote get-url)会因 git 的 dubious ownership 检查失败(已由代码内 -c safe.directory=$dir 兜底);
    • git reset --hard 因写权限不足必然失败,回调返回 500。必须执行:
      chown -R www:www /path/to/site/plugins/yggdrasil-connect
  2. 更新采用强制同步(git fetch + git reset --hard origin/<branch>:回调更新时会丢弃服务器插件目录上已跟踪文件的全部本地改动,无条件对齐远程。插件目录应视为部署镜像,请勿在其中直接修改文件;本地改动请提交到 CNB 仓库后由流水线合并推送。(未跟踪文件不受 reset 影响,如需彻底清理请手动 git clean。)
  3. CALLBACK_URL 自动生成:回调地址由插件以 site_url 为基准自动生成({site_url}/api/cnb/sync-callback),无需手动配置。请确保 site_url 是公网可达的 HTTPS 地址,且该路径未被防火墙/安全组拦截。
  4. PHP CLI 路径:后台更新进程通过 proc_open/exec 启动 php artisan yggc:cnb-sync。若服务器上 PHP-FPM 的 PATH 中找不到 php,在插件配置「PHP CLI 路径」填入绝对路径(which php 查询,一般为 /usr/bin/php)。
  5. 回调令牌一致性cnb_callback_secret(插件配置)必须与 CNB 密钥仓库 env.yml 中的 CNB_CALLBACK_SECRET 完全一致,否则回调被拒绝(403)。
  6. 流水线配置需推送.cnb.ymlapi_trigger_upstream_sync 流水线定义在 CNB 仓库 master 分支,改动后需 git push origin master 才会生效。

已知问题

  • 在部分情况下,用户在通过传统 Auth Server 登录时,可能会遇到 HTTP 500 错误;或在请求 OAuth 授权时,在请求了正确的 scope 的情况下,仍遇到 invalid_scopes 错误。

关于 Yggdrasil Connect

Yggdrasil Connect 是基于 OAuth 2.0 和 OpenID Connect 协议的 Minecraft 外置登录身份验证协议,其核心目标是取代 Yggdrasil API 中的 Auth Server 部分,从而改善 authlib-injector 外置登录方案的安全性和用户体验。

在 Yggdrasil API 的 Auth Server 部分中,用户需要将自己的用户名和密码直接暴露给第三方应用,才能通过第三方应用访问 Yggdrasil API,这增加了用户账户关键信息泄露的风险;且该部分的 API 在设计上几乎没有考虑到二步验证,无法良好地保障用户账户的安全。并且,由于各个第三方应用在该部分的实现的差异,用户在不同应用之间的登录体验存在较大割裂,时常出现应用实现不完整导致用户无法正常登录的问题。

Yggdrasil Connect 的出现正是为了解决这些问题。通过 OAuth 2.0 和 OpenID Connect 协议,Yggdrasil Connect 可以实现用户在不同应用间的登录体验的统一,同时方便各验证服务器自行设计用户身份认证方式,以保障用户账户的安全。

对于 Blessing Skin Server 来说,Yggdrasil Connect 还解决了「通过社交网站 OAuth 注册的用户默认没有密码,无法在启动器中登录」的问题:通过 Yggdrasil Connect,用户可以通过社交网站账户登录皮肤站并授予启动器权限,而无需输入密码。

要了解更多关于 Yggdrasil Connect 协议的信息,请阅读 Yggdrasil Connect 协议规范

为 Blessing Skin Server 启用 Yggdrasil Connect

本插件内置了完整的 Yggdrasil Connect(OpenID Connect)服务端,无需额外部署独立服务。

要启用 Yggdrasil Connect,只需确保 Blessing Skin Server 的 site_url 配置项已正确设置为 HTTPS 地址即可。Yggdrasil Connect 功能在 site_url 配置完成后自动启用。

启用后,以下端点将可用:

端点 路径 说明
OpenID Discovery /.well-known/openid-configuration OpenID Connect 服务发现(必须在根路径,遵循 OIDC 规范)
JWKS /yggc/jwks JWT 签名公钥
授权端点 /yggc/auth OAuth 2.0 授权端点
令牌端点 /yggc/token OAuth 2.0 令牌端点
用户信息端点 /yggc/userinfo OpenID Connect 用户信息端点
吊销端点 /yggc/revoke OAuth 2.0 令牌吊销端点
设备授权端点 /yggc/device/auth OAuth 2.0 设备授权端点
设备验证页面 /yggc/device 设备码验证页面

可在插件配置页面的「Yggdrasil Connect」选项卡中配置以下参数:

  • 设备码过期时间:设备授权流程中设备码的有效时间
  • 授权过期时间:用户授权的有效时间,过期后需要重新授权
  • 共享客户端 ID:用于设备授权流程的共享 OAuth 客户端 ID,启动器会从 OIDC Discovery 文档中读取此 ID 并自动使用。留空时自动使用 PASSPORT_PERSONAL_ACCESS_CLIENT_ID 环境变量的值,通常无需手动填写
  • 禁用 Auth Server:禁用后,用户将无法通过在启动器中输入用户名和密码的传统方式登录,必须通过 Yggdrasil Connect 登录
  • 启用在校状态查询:启用后,客户端可请求 campus_status 权限范围,在用户信息和 ID 令牌中获取用户的在校状态(campus_status: true/false)。需配合 campus-status 插件 使用,默认关闭

注意:如果你之前使用了独立部署的 Janus 项目,本插件已内置其全部功能,可以将其停用。

关于 Bug 修复

本插件在一定程度上修复了原版 Yggdrasil API 插件的多个影响用户体验乃至导致用户无法正常登录 Minecraft 游戏服务器的 Bug:

  • 使用角色名登录时,并不会自动选择角色#123):
    • 本插件在用户使用角色名登录时,会自动将 Access Token 绑定至角色名对应的角色,同时在 API 响应中添加 selectedProfile 字段。
    • 同时,对于签发 Access Token 时能确定 Access Token 绑定到的角色的场景(使用角色名登录、令牌刷新),API 响应中的 availableProfiles 字段中将仅包含 Access Token 绑定到的角色的信息,以避免客户端出现异常行为。
  • uuid 表中数据不一致#151):
    • 本插件重新设计了 uuid 表,将 UUID 记录与角色模型的关联字段从角色名(name)改为了 PID(pid)。同时,本插件为 uuid 表中 pidnameuuid 字段添加了 UNIQUE 约束,确保不会出现两条拥有相同的 PID、角色名或 UUID 的记录。因此,在从原版 Yggdrasil API 插件迁移至本插件时,必须在终端中执行 php artisan yggc:fix-uuid-table 命令,以清除 uuid 表中异常的数据,并为 uuid 表添加 pid 字段及相关约束。
      • 但这项改动也会导致「正版验证」(mojang-verification)插件的「更新 UUID」功能无法正常工作,具体表现为该功能可能会在 uuid 表中插入一条 pidnull 的无效记录,该记录可能导致后续插入相同角色名的正确的 UUID 记录时失败并报错。考虑到该功能即使是在配合原版 Yggdrasil API 插件使用的情况下也可能会导致更大程度的数据错乱,建议直接将该功能禁用。
    • 为保证与原版 Yggdrasil API 插件的兼容性,UUID 表中的角色名字段(name)仍被保留,并监听了 player.renamed 事件,使得 UUID 表中记录的角色名可以在角色更名时被一同更新。
  • 角色改名(新旧名字仅大小写不同)会导致丢失 uuid#152):
    • 当 UUID 算法为 v3 时,Union 版插件修改了默认行为,确保角色改名(包括仅修改大小写)后 UUID 不变,不会出现该问题。
    • 当 UUID 算法为 v4 时,本插件使用 PID 而非角色名作为 UUID 记录与角色模型的关联字段,PID 全局唯一且不可更改,不会出现该问题。
  • 删除角色时不会删除 uuid 表中的对应的记录#202):
    • 本插件在 uuid 表中将 pid 字段定义为了外键,关联至 players 表中的 pid 字段,并添加了级联删除规则,确保用户删除角色时 uuid 表中的 UUID 记录会被一起删除。
      • 即使出现意外情况,导致 uuid 表中的记录未在用户删除角色时删除,考虑到本插件使用角色 PID 作为关联字段,而 PID 是全局唯一的,这些孤立的记录并不会与其他角色发生错误关联,从而避免了该 Bug 带来的数据错乱的问题。
  • 通过刷新获得的 accessToken 在用户邮箱存在大写字母的情况下无法通过校验#212):
    • 本插件使用 Laravel Passport 的个人访问密钥(Personal Access Token)作为 Access Token,其使用用户的 UID 作为 Access Token 所有者的身份标识符,不会出现类似的问题。
      • 但这项改动也要求站点管理员在终端中执行 php artisan yggc:create-personal-access-client 命令创建个人访问客户端(Personal Access Client),并在 .env 中配置 PERSONAL_ACCESS_CLIENT_ID 的值为个人访问客户端的 Client ID 后,传统 Auth Server 才可正常运行。
  • 角色改名后 Access Token 仍然有效#231):
    • 本插件监听了 player.renamed 事件,在角色更名后,本插件会在缓存中记录角色更名的时间。
    • 在验证 Access Token 时,本插件会检查 Access Token 的签发时间,如早于缓存中记载的角色更名时间,则视为 Access Token 暂时失效,返回错误。
      • 为确保 UserInfo Endpoint 与进服请求中的令牌验证结果一致(即,避免 UserInfo Endpoint 验证令牌有效,但进服请求验证令牌无效的情况出现),本插件对于 UserInfo Endpoint 也会执行该检查。这可能导致通过 Yggdrasil Connect 签发的 Access Token 被刷新的频率增加(因为 UserInfo Endpoint 中已包含有最新的角色信息,启动器本可直接请求 UserInfo Endpoint 获取最新角色信息,而无需通过刷新令牌的方式更新角色信息,但由于 Access Token 被认为是过期的,UserInfo Endpoint 会返回 invalid_token 错误),但只要启动器正确按照正常的令牌失效刷新流程处理这种情况,就不会产生影响用户体验的问题。
  • 查询角色属性时返回的角色名大小写和实际角色名不符#232
    • 本插件在返回角色信息时会返回角色在数据库中记录的角色名,而非请求中的角色名。

版权

Copyright (c) 2025-present LittleSkin. All rights reserved. Open source under the MIT license.

Disclaimer:你站产品经理自己写代码的原则就是代码和人有一个能跑就行,自然有些代码很粗糙很难看很低效。如果你看着哪里的代码不爽,欢迎直接重构并 PR。

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages