The latest version of authlib-injector can be downloaded directly from authlib-injector.yushi.moe.
The authlib-injector project provides an API set for downloading authlib-injector artifacts. The API root is https://authlib-injector.yushi.moe/.
GET /artifacts.json
Response format:
{
"latest_build_number": latest build number,
"artifacts": [
{
"build_number": build number,
"version": "version"
}
]
}GET /artifact/{build_number}.json
{build_number} in the URL is the build number.
Response format:
{
"build_number": build number,
"version": "version",
"download_url": "download URL of authlib-injector",
"checksums": {
"sha256": "SHA-256 checksum"
}
}GET /artifact/latest.json
Response format is the same as above. If you only need the latest version, this endpoint is enough.
When using BMCLAPI, please follow the BMCLAPI terms.
BMCLAPI provides a mirror for this download API. Its root is https://bmclapi2.bangbang93.com/mirrors/authlib-injector/.
Project goals:
This project documents all APIs in detail and also defines some APIs that are not part of vanilla Yggdrasil. This is intended to minimize the setup required for a designated Yggdrasil service: in most cases, you only need to provide the service URL.
If you are a Yggdrasil server developer, launcher developer, or interested in this project, please watch this repository to track the latest spec updates.
Developer chat groups: QQ group
926979364, Telegram group @authlib_injector. Launcher and skin-site developers are welcome.
BMCLAPI provides the download mirror for authlib-injector. If you want to support authlib-injector development, you can donate to BMCLAPI.
This document provides technical guidance for implementing authlib-injector support in launchers. Because this feature relies on Yggdrasil APIs, read Yggdrasil Server Technical Specification first.
In launchers, this login method can be named "External Login (authlib-injector)" or "authlib-injector Login". The former is recommended for clarity.
The authentication server (Yggdrasil server) is the core of the whole authentication system; all authentication-related requests are sent to it.
To identify one server, a launcher stores its API root URL (for example, https://example.com/api/yggdrasil/).
A launcher may support one or multiple authentication servers. Supporting multiple servers means multiple accounts can coexist and can belong to different servers.
Server configuration is usually done by the player, but may also be preset by server owners or distributed with launcher/game packages.
The launcher can store the API URL directly in its config file and let users edit it. This is simple and works well for dedicated server launchers.
Users input a URL in the launcher. It may be a full API URL (for example https://example.com/api/yggdrasil/) or a short URL (for example example.com).
If protocol is missing, prepend HTTPS automatically.
That is, example.com/api/yggdrasil/ should be interpreted as https://example.com/api/yggdrasil/.
For security reasons, launchers must not downgrade to plain HTTP even when HTTPS fails.
authlib-injector also defines API Location Indication (ALI), a discovery mechanism that maps a short/incomplete URL to the real API root.
To resolve user input to the actual API root:
X-Authlib-Injector-API-Location, that value is the API URL.Pseudo-code:
function resolve_api_url(url)
response = http_get(url) // follow redirects
if response.headers["x-authlib-injector-api-location"] exists
new_url = to_absolute_url(response.headers["x-authlib-injector-api-location"])
if new_url != url
return new_url
// if you are going to fetch metadata next, response can be reused
return urlThis method allows users to configure authentication servers via drag-and-drop (DnD).
The DnD source can be a browser or app. The target is the launcher. The source shows text/image/content prompting users to drag into launcher to add a server. After drop, launcher should confirm with user before adding.
MIME type: text/plain.
Content is a URI in this format:
authlib-injector:yggdrasil-server:{api_root}{api_root} is URI component and must be URL-encoded.
DnD effect: copy.
Add draggable="true" and handle dragstart on the draggable element:
<span id="dndLabel" draggable="true" ondragstart="dndLabel_dragstart(event);">example.yggdrasil.yushi.moe</span>function dndLabel_dragstart(event) {
let yggdrasilApiRoot = "https://example.yggdrasil.yushi.moe/";
let uri = "authlib-injector:yggdrasil-server:" + encodeURIComponent(yggdrasilApiRoot);
event.dataTransfer.setData("text/plain", uri);
event.dataTransfer.dropEffect = "copy";
}Launcher can send GET to API root and retrieve metadata (response format), such as server name, to improve UX.
Server name is provided in meta.serverName. Use it when presenting a server to users.
Server names may collide. The launcher should provide a way to view API root, for example in a tooltip when hovering the server name.
If user attempts to configure an HTTP (plaintext) server, launcher should show a clear warning that credentials may be transmitted in plaintext.
An account maps to a player used to start the game.
Relationship of Account / User / Profile: in launcher terminology, "account" is not the same as Yggdrasil "user". Launcher account corresponds to Yggdrasil profile. A Yggdrasil user owns one or more profiles.
Launcher should identify an account by three immutable fields:
feature.non_email_login is true, non-email identifiers are supported. In that case launchers should not assume email format and should use neutral labels like "Account" instead of "Email". See Yggdrasil spec § Login with profile name.Two accounts are considered identical only when all three fields match.
Additional mutable fields:
accessToken and clientToken)Security warning:
- "Remember me" stores tokens, not plaintext password.
Launcher must update mutable fields after each login/refresh.
In all login and refresh requests below, set requestUser=true so user ID/properties can be refreshed immediately.
To add an account, launcher asks for authentication server, account identifier, and password. Then:
selectedProfile exists, login succeeded; update account and stop.availableProfiles is empty, throw error (no profile).availableProfiles.Before using credentials (for example, before launch), launcher should validate them.
selectedProfile and UUID matches stored profile UUID, update and stop; otherwise error.availableProfiles; if none, error.When displaying an account, show both profile name and authentication server (see server-name section) to avoid ambiguity for same-name profiles on different servers.
If launcher displays skins, it can call Query Profile Properties to read skin info from profile properties.
Before launch, launcher should:
Steps 1/2/3 can be run in parallel.
Launcher may bundle authlib-injector.jar, or download/cache it before launch. This project provides a download API.
If most users are in mainland China, BMCLAPI mirror is recommended.
Before launch, send GET to API root and fetch metadata. Pass this metadata to the game at startup so authlib-injector does not need an extra runtime request. This improves startup speed and reduces launch failure risk caused by transient network issues.
Add these JVM args (before main class args):
-javaagent:{path/to/authlib-injector.jar}={api_root}-Dauthlibinjector.yggdrasil.prefetched={Base64-encoded API metadata}Example:
/home/user/.launcher/authlib-injector.jarhttps://example.yggdrasil.yushi.moe/-javaagent:/home/user/.launcher/authlib-injector.jar=https://example.yggdrasil.yushi.moe/
-Dauthlibinjector.yggdrasil.prefetched=eyJza2luRG9tYWluc... (omitted)In game version JSON (versions/<version>/<version>.json), replace auth-related templates as follows:
| Template | Replacement |
|---|---|
| ${auth_access_token} | account accessToken |
| ${auth_session} | account accessToken |
| ${auth_player_name} | profile name |
| ${auth_uuid} | unsigned profile UUID |
| ${user_type} | mojang |
| ${user_properties} | user properties JSON (or {} if unsupported) |
This page describes the key pair used for digital signatures. OpenSSL is used in the examples.
The authentication server should digitally sign profile properties in responses of:
unsigned=false)The server publishes the public key via API metadata.
Note: the key should remain stable. If you run multiple server instances behind load balancing, they should share the same key.
All OpenSSL commands below read from stdin and write to stdout.
If you need files, use -in <file> and -out <file>.
The key algorithm is RSA. 4096 bits is recommended.
openssl genrsa 4096openssl rsa -puboutThe private key is read from stdin and the public key is written to stdout.
First, download the latest authlib-injector from here.
Since authlib-injector v1.2.0, you need to set
enforce-secure-profile=true(different from previous versions).If you encounter chat-signing issues on MC 1.19+, see: authlib-injector v1.2.0 upgrade FAQ #174
Set online-mode=true in server.properties. For 1.19+ servers, also set enforce-secure-profile=true.
Then add this JVM argument to the server startup command (before -jar):
-javaagent:{path/to/authlib-injector.jar}={https://your-yggdrasil-api-root.com}{path/to/authlib-injector.jar} is where the JAR downloaded in the previous step is located.{https://your-yggdrasil-api-root.com} is the authentication server URL.Example, original command:
java -jar minecraft_server.1.12.2.jar noguiAssume:
authlib-injector.jar.minecraft_server.1.12.2.jar.https://example.yggdrasil.yushi.moe.Then the startup command becomes:
java -javaagent:authlib-injector.jar=https://example.yggdrasil.yushi.moe -jar minecraft_server.1.12.2.jar noguiIf using BungeeCord or Velocity, load authlib-injector on all backend servers and BungeeCord/Velocity (same method as above), and enable enforce-secure-chat.
Only BungeeCord/Velocity should have online-mode enabled; backend MC servers should disable online-mode.
After loading authlib-injector, skins are fetched from your configured authentication server by default. For example:
/give @p minecraft:skull 1 3 {SkullOwner:"notch"}/npc skin notchThese commands fetch the skin of profile notch from your custom authentication server.
To use Mojang skins instead, append @mojang:
/give @p minecraft:skull 1 3 {SkullOwner:"notch@mojang"}/npc skin notch@mojangSee -Dauthlibinjector.mojangNamespace in README § Parameters.
The Mojang-skin feature requires backend servers to reach Mojang APIs. If your server must use a proxy, add this JVM option:
-Dauthlibinjector.mojangProxy=socks://<host>:<port>Notes:
This document provides a non-official technical specification for implementing a Yggdrasil authentication server.
The behavior described here is not guaranteed to be identical to Mojang's server implementation. Mojang's implementation is closed-source, so exact behavior is not always observable. In practice, if clients can correctly understand and handle responses, implementation details do not have to be identical.
UTF-8 is used throughout this document.
Unless otherwise stated, requests and responses use JSON (when a body exists), and Content-Type should be application/json; charset=utf-8.
All APIs should use HTTPS.
{
"error": "brief machine-readable description",
"errorMessage": "human-readable message",
"cause": "optional cause"
}When an error case is explicitly described in this document, the server should return the required error format.
Common error cases:
| Error Case | HTTP Status | Error | Error Message |
|---|---|---|---|
| General HTTP error (non-business, e.g. Not Found / Method Not Allowed) | undefined | HTTP reason phrase | undefined |
| Invalid token | 403 | ForbiddenOperationException | Invalid token. |
| Wrong password, or temporarily blocked after many failed logins | 403 | ForbiddenOperationException | Invalid credentials. Invalid username or password. |
| Trying to assign a profile to a token that already has one | 400 | IllegalArgumentException | Access token already has a profile assigned. |
| Trying to bind a token to a profile that doesn't belong to the user (non-standard) | 403 | ForbiddenOperationException | undefined |
| Trying to join a server with the wrong profile | 403 | ForbiddenOperationException | Invalid token. |
Defined terms:
- removed.A system may contain multiple users. A user has:
User ID is an unsigned UUID. Email can be changed but must be unique.
{
"id":"user id",
"properties":[
{
"name":"property name",
"value":"property value"
}
]
}Known user property keys:
| Name | Value |
|---|---|
| preferredLanguage | optional language preference, e.g. en, zh_CN |
Mojang currently does not support multiple profiles. Multi-profile behavior here is based on available observations.
A profile belongs to exactly one user. A profile has:
default or slimdefault: normal 4px armslim: 3px armSKIN or CAPEUUID and name are globally unique (name may change). Do not use name as persistent identity.
Without compatibility constraints, profile UUID can be random (UUID v4).
However, Minecraft uses UUID as profile identity. If a server migrates from another auth system and UUID changes, user data may be lost. To avoid this, keep profile UUID stable across migrations whenever possible.
If a server used offline mode before, profile UUID is a function of profile name. Using the same algorithm keeps compatibility and avoids data loss.
Java:
UUID.nameUUIDFromBytes(("OfflinePlayer:" + characterName).getBytes(StandardCharsets.UTF_8))Other language example:
{
"id":"unsigned profile uuid",
"name":"profile name",
"properties":[
{
"name":"property name",
"value":"property value",
"signature":"optional base64 signature"
}
]
}properties and signature are omitted unless required by endpoint behavior.
signature is Base64-encoded SHA1withRSA signature over property value. See PKCS #1 and Signature-Key-Pair.
Profile properties may include:
| Name | Value |
|---|---|
| textures | optional Base64 JSON string, see textures property |
| uploadableTextures | optional authlib-injector extension, see uploadableTextures property |
textures PropertyThe value is a Base64-encoded JSON object:
{
"timestamp": unix milliseconds,
"profileId": "unsigned uuid",
"profileName": "profile name",
"textures": {
"SKIN": {
"url": "texture URL",
"metadata": {
"model": "default or slim"
}
},
"CAPE": {
"url": "texture URL"
}
}
}uploadableTextures PropertyThis is defined by authlib-injector docs. Mojang profile properties do not include this field.
uploadableTextures indicates which texture types can be uploaded for a profile.
The value is a comma-separated list containing skin and/or cape.
Examples:
skin: skin upload allowed, cape upload not allowed.skin,cape: both allowed.See Texture Upload.
Minecraft uses texture hash as identifier. After a client downloads a texture, it caches by hash.
The hash is derived from the texture URL filename (substring after the last /). Therefore, servers should place the precomputed hash in the URL filename.
Example URL (hash is the filename):
https://yggdrasil.example.com/textures/e051c27e803ba15de78a1d1e83491411dffb6d7fd2886da0a6c34a2161f7ca99Security:
- Texture responses must set
Content-Type: image/pngto reduce MIME-sniffing risks.
Hash algorithm is server-defined; SHA-256 (or stronger) is recommended.
Security:
- Improper handling may allow remote code execution.
- Not checking image size before decode can cause denial-of-service.
PNG can contain extra chunks beyond pixel data. A server must sanitize uploaded textures before serving them to clients.
Required handling:
64x32 or 64x64.64x32 or 22x17.22x17-based cape should be padded with transparent pixels to 64x32 multiple.Java tip: use ImageReader.getWidth()) / getHeight() before full decode.
A token belongs to one user and is time-limited. Token fields:
accessTokenclientTokenaccessToken and clientToken are arbitrary strings (unsigned UUID, JWT, etc.). accessToken is server-generated random value; clientToken is client-provided.
accessToken can be used as primary key. clientToken is not unique.
A user may hold multiple tokens. Server should cap token count (for example 10). When exceeding limit, revoke oldest tokens first.
Tokens have three states:
A temporarily invalid token should act as invalid in API behavior.
Temporary invalid state helps with profile-rename compatibility.
Example:
A.B.A fail to join server.By marking old token temporarily invalid after rename, launcher will perform refresh/re-auth and obtain updated profile name before launch.
POST /authserver/authenticate
Request:
{
"agent": {
"name": "Minecraft",
"version": 1
},
"username": "user identifier",
"password": "password",
"clientToken": "optional client token",
"requestUser": true
}Response:
{
"accessToken": "access token",
"clientToken": "client token",
"availableProfiles": [
{"id":"unsigned uuid","name":"profile name"}
],
"selectedProfile": {"id":"unsigned uuid","name":"profile name"},
"user": {"id":"user id","properties":[]}
}If only one profile exists, server may set selectedProfile directly. If multiple profiles exist, launcher may need a refresh call with explicit selected profile.
By default, username is email-like identifier. authlib-injector allows optional non-email login.
API metadata feature flag:
feature.non_email_login = true: non-email identifiers supported.Recommended server-side validations when this feature is enabled:
A-Za-z0-9_@.-POST /authserver/refresh
Request:
{
"accessToken":"access token",
"clientToken":"client token",
"selectedProfile":{"id":"unsigned uuid","name":"profile name"},
"requestUser": true
}Notes:
selectedProfile is provided, bind it.selectedProfile must be omitted or match the bound one.requestUser=true, include user in response.Response format is similar to authenticate response.
POST /authserver/validate
Request:
{
"accessToken":"access token",
"clientToken":"client token"
}Success response body can be empty (204 No Content is acceptable).
POST /authserver/invalidate
Request:
{
"accessToken":"access token",
"clientToken":"client token"
}Revokes the token.
POST /authserver/signout
Request:
{
"username":"user identifier",
"password":"password"
}Revokes all tokens for that user.
POST /sessionserver/session/minecraft/join
Request:
{
"accessToken":"access token",
"selectedProfile":"unsigned uuid",
"serverId":"server hash"
}GET /sessionserver/session/minecraft/hasJoined?username=<name>&serverId=<serverId>&ip=<optional ip>
If verification succeeds, return serialized profile. If failed, return 204 No Content.
Query parameter ip is optional and may be ignored if server does not enforce IP checks.
GET /sessionserver/session/minecraft/profile/<unsigned_uuid>?unsigned=<true|false>
When unsigned=false, return signature for textures property.
If profile does not exist, return 204 or 404 (implementation-defined, but should be consistent).
POST /api/profiles/minecraft
Request body is JSON array of profile names:
["Notch", "Steve"]Response is an array of serialized profiles (without full properties unless your implementation chooses to include them).
PUT /api/user/profile/<unsigned_uuid>/<texture_type>
<texture_type> is skin or cape.
Auth:
Authorization: Bearer <accessToken>.Request is typically multipart/form-data with image payload.
Expected behavior:
uploadableTextures).DELETE /api/user/profile/<unsigned_uuid>/<texture_type>
Auth:
Authorization: Bearer <accessToken>.Removes profile texture of specified type.
GET /
Response:
{
"skinDomains":["example.com"],
"signaturePublickey":"base64-encoded DER RSA public key",
"meta":{
"serverName":"Example Auth Server",
"implementationName":"example-impl",
"implementationVersion":"1.0.0",
"links":{
"homepage":"https://example.com",
"register":"https://example.com/register"
},
"feature":{
"non_email_login":false,
"legacy_skin_api":false,
"no_mojang_namespace":false,
"enable_profile_key":false,
"enable_mojang_anti_features":false,
"username_check":true
}
}
}skinDomains lists allowed texture domains. authlib-injector uses it for client-side domain checks.
metaRecommended fields:
serverNameimplementationNameimplementationVersionRecommended meta.links fields:
homepageregistermeta.feature indicates optional capabilities. Known keys include:
non_email_loginlegacy_skin_apino_mojang_namespaceenable_profile_keyenable_mojang_anti_featuresusername_checkSee the JSON example above; additional fields are allowed for forward compatibility.
ALI allows converting short/incomplete URLs into real API root URLs.
Server behavior:
X-Authlib-Injector-API-Location to relevant URLs.Launcher behavior should follow the flow defined in Launcher-Technical-Specification.
Hosted by Fentanyl Solutions