Get-authlib-injector.md

Manual Download

The latest version of authlib-injector can be downloaded directly from authlib-injector.yushi.moe.

Download API

The authlib-injector project provides an API set for downloading authlib-injector artifacts. The API root is https://authlib-injector.yushi.moe/.

Get Version List

GET /artifacts.json

Response format:

{
    "latest_build_number": latest build number,
    "artifacts": [
        {
            "build_number": build number,
            "version": "version"
        }
    ]
}

Get a Specific 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 the Latest Version

GET /artifact/latest.json

Response format is the same as above. If you only need the latest version, this endpoint is enough.

BMCLAPI Mirror

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/.

Home.md

Introduction

Project goals:

  • Provide tools to modify Minecraft so it can use custom Yggdrasil authentication services.
  • Provide technical specifications for custom Yggdrasil servers and launchers that use custom Yggdrasil services.
  • Provide players with a unified non-Mojang login experience outside the game.
    • Players can use any launcher implementing this specification to log in to any Yggdrasil service implementing this specification.

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.

  • yggdrasil-mock
    • Reference implementation of the Yggdrasil server specification and test cases for Yggdrasil APIs.
    • Demo site based on it: auth-demo.yushi.moe
  • BMCLAPI
    • BMCLAPI provides a mirror for downloading authlib-injector.
  • Yggdrasil API for Blessing Skin
    • Yggdrasil plugin for Blessing Skin.
  • HMCL
    • HMCL v3.x supports authlib-injector.
  • BakaXL
    • BakaXL 3.0 supports authlib-injector.
  • LaunchHelper
    • If you want to use authlib-injector on a Multicraft panel server, try this project.

Donation

BMCLAPI provides the download mirror for authlib-injector. If you want to support authlib-injector development, you can donate to BMCLAPI.

Launcher-Technical-Specification.md

Overview

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.

Authentication Servers

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.

Configuring Authentication Servers

Server configuration is usually done by the player, but may also be preset by server owners or distributed with launcher/game packages.

Set in Configuration File

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.

Enter URL in Launcher

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.

Process API Location Indication (ALI)

To resolve user input to the actual API root:

  1. If protocol is missing, add HTTPS.
  2. Send GET request to the URL (follow redirects).
  3. If response contains X-Authlib-Injector-API-Location, that value is the API URL.
    • It can be absolute or relative.
    • If it points to itself, current URL is the API URL.
  4. If the header is absent, treat current URL as 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 url

Configure via Drag and Drop

This 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.

Drag Data

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.

HTML Example

Demo page

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";
}

Display Authentication Server Info

Launcher can send GET to API root and retrieve metadata (response format), such as server name, to improve UX.

Display Server Name

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.

Warn on Non-HTTPS Authentication Servers

If user attempts to configure an HTTP (plaintext) server, launcher should show a clear warning that credentials may be transmitted in plaintext.

Accounts

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.

Storing Account Identity

Launcher should identify an account by three immutable fields:

  • Authentication server
  • Account identifier (for example, email)
    • Usually email. But when server metadata 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.
  • Profile UUID

Two accounts are considered identical only when all three fields match.

Additional mutable fields:

  • Tokens (accessToken and clientToken)
  • Profile name
  • User ID
  • User properties

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.

Adding an Account

To add an account, launcher asks for authentication server, account identifier, and password. Then:

  1. Call Authenticate with user input.
  2. If selectedProfile exists, login succeeded; update account and stop.
  3. If availableProfiles is empty, throw error (no profile).
  4. Ask user to pick one profile from availableProfiles.
  5. Call Refresh with token from authenticate and selected profile.
  6. Login succeeded; update account and stop.

Checking Credential Validity

Before using credentials (for example, before launch), launcher should validate them.

  1. Call Validate with stored access/client tokens.
  2. If success, credentials are valid.
  3. Otherwise call Refresh.
  4. If success, update account and stop.
  5. Otherwise ask user to re-enter password.
  6. Call Authenticate.
  7. If response has selectedProfile and UUID matches stored profile UUID, update and stop; otherwise error.
  8. Else find matching profile UUID in availableProfiles; if none, error.
  9. Call Refresh with selected matching profile.
  10. Update account and stop.

Displaying Accounts

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.

Launch Game

Before launch, launcher should:

  1. (If needed) Download authlib-injector
  2. Validate credentials
  3. Prefetch configuration
  4. Append launch arguments

Steps 1/2/3 can be run in parallel.

Download authlib-injector

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.

Prefetch Configuration

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 Launch Arguments

Configure authlib-injector

Add these JVM args (before main class args):

  1. Java agent:
    -javaagent:{path/to/authlib-injector.jar}={api_root}
  2. Prefetched metadata:
    -Dauthlibinjector.yggdrasil.prefetched={Base64-encoded API metadata}

Example:

  • JAR path: /home/user/.launcher/authlib-injector.jar
  • API root: https://example.yggdrasil.yushi.moe/
  • GET metadata from API root, then Base64 encode the full response.
  • JVM args become:
    -javaagent:/home/user/.launcher/authlib-injector.jar=https://example.yggdrasil.yushi.moe/
    -Dauthlibinjector.yggdrasil.prefetched=eyJza2luRG9tYWluc... (omitted)

Replace Argument Templates

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)

Signature-Key-Pair.md

Overview

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:

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.

Generate and Process the Key Pair

All OpenSSL commands below read from stdin and write to stdout. If you need files, use -in <file> and -out <file>.

Generate Private Key

The key algorithm is RSA. 4096 bits is recommended.

openssl genrsa 4096

Generate Public Key from Private Key

openssl rsa -pubout

The private key is read from stdin and the public key is written to stdout.

Using-authlib-injector-on-Minecraft-Server.md

Get authlib-injector

First, download the latest authlib-injector from here.

Vanilla Server / Spigot / Paper / ...

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 nogui

Assume:

  • The downloaded JAR is authlib-injector.jar.
  • It is in the same directory as minecraft_server.1.12.2.jar.
  • The API root is 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 nogui

BungeeCord / Velocity

If 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.

Use Mojang Skins

After loading authlib-injector, skins are fetched from your configured authentication server by default. For example:

  • /give @p minecraft:skull 1 3 {SkullOwner:"notch"}
  • (Citizens2) /npc skin notch

These 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@mojang

See -Dauthlibinjector.mojangNamespace in README § Parameters.

Access Mojang via Proxy

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:

  • The proxy is only used when querying profile info from Mojang.
  • Texture image download does not use this proxy, even for Mojang textures.
  • Only SOCKS5 is currently supported.

Yggdrasil-Server-Specification.md

Overview

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.

Conventions

Character Encoding

UTF-8 is used throughout this document.

Request/Response Format

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 Format

{
    "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.

Data Formats

Defined terms:

  • Unsigned UUID: UUID string with all - removed.

Models

User

A system may contain multiple users. A user has:

  • ID
  • Email
  • Password

User ID is an unsigned UUID. Email can be changed but must be unique.

Serialized User

{
    "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

Profile

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:

  • UUID
  • Name
  • Texture model: default or slim
    • default: normal 4px arm
    • slim: 3px arm
  • Textures
    • map where key is SKIN or CAPE
    • value is URL

UUID and name are globally unique (name may change). Do not use name as persistent identity.

Profile UUID Generation

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.

Offline-Mode Compatibility

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:

Serialized Profile

{
    "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 Property

The 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 Property

This 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.
  • property missing: no upload allowed.

See Texture Upload.

Texture URL Rules

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/e051c27e803ba15de78a1d1e83491411dffb6d7fd2886da0a6c34a2161f7ca99

Security:

  • Texture responses must set Content-Type: image/png to reduce MIME-sniffing risks.

Hash algorithm is server-defined; SHA-256 (or stronger) is recommended.

Security of User-Uploaded Textures

Security:

  • Improper handling may allow remote code execution.
  • Not checking image size before decode can cause denial-of-service.

See: Unchecked user-uploaded texture may lead to RCE #10

PNG can contain extra chunks beyond pixel data. A server must sanitize uploaded textures before serving them to clients.

Required handling:

  1. Read image dimensions first; reject oversized images.
    • Even tiny files can decompress into huge images (PNG bomb). Do not fully decode before size checks.
  2. Validate image as legal skin/cape.
    • Skin size must be multiples of 64x32 or 64x64.
    • Cape size must be multiples of 64x32 or 22x17.
    • Non-standard 22x17-based cape should be padded with transparent pixels to 64x32 multiple.
  3. Re-encode image to strip unrelated metadata.

Java tip: use ImageReader.getWidth()) / getHeight() before full decode.

Token

A token belongs to one user and is time-limited. Token fields:

  • accessToken
  • clientToken
  • bound profile (optional)
  • issuance time

accessToken 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.

Token States

Tokens have three states:

  • Valid: usable.
  • Invalid: unusable.
  • Temporarily invalid: currently unusable, can return to valid.

A temporarily invalid token should act as invalid in API behavior.

About Temporarily Invalid

Temporary invalid state helps with profile-rename compatibility.

Example:

  1. User logs in and token is bound to profile name A.
  2. Profile is renamed to B.
  3. Launchers with stale profile name 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.

Yggdrasil API

User APIs

Authenticate

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.

Login with Profile Name

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:

  • max length 48
  • characters limited to A-Za-z0-9_@.-

Refresh

POST /authserver/refresh

Request:

{
    "accessToken":"access token",
    "clientToken":"client token",
    "selectedProfile":{"id":"unsigned uuid","name":"profile name"},
    "requestUser": true
}

Notes:

  • If token has no bound profile and selectedProfile is provided, bind it.
  • If token already has a bound profile, selectedProfile must be omitted or match the bound one.
  • If requestUser=true, include user in response.

Response format is similar to authenticate response.

Validate

POST /authserver/validate

Request:

{
    "accessToken":"access token",
    "clientToken":"client token"
}

Success response body can be empty (204 No Content is acceptable).

Invalidate

POST /authserver/invalidate

Request:

{
    "accessToken":"access token",
    "clientToken":"client token"
}

Revokes the token.

Signout

POST /authserver/signout

Request:

{
    "username":"user identifier",
    "password":"password"
}

Revokes all tokens for that user.

Session APIs

Join Minecraft Server

POST /sessionserver/session/minecraft/join

Request:

{
    "accessToken":"access token",
    "selectedProfile":"unsigned uuid",
    "serverId":"server hash"
}

Has Joined Minecraft Server

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.

Profile APIs

Query Profile Properties

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).

Query Profiles by Names

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).

Texture Upload

PUT Upload Texture

PUT /api/user/profile/<unsigned_uuid>/<texture_type>

<texture_type> is skin or cape.

Auth:

  • Use Authorization: Bearer <accessToken>.

Request is typically multipart/form-data with image payload.

Expected behavior:

  • Verify token validity and ownership.
  • Verify profile exists and upload permission (uploadableTextures).
  • Validate and sanitize image.
  • Store texture and update profile properties.

DELETE Remove Texture

DELETE /api/user/profile/<unsigned_uuid>/<texture_type>

Auth:

  • Use Authorization: Bearer <accessToken>.

Removes profile texture of specified type.

Extended APIs

API Metadata

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
        }
    }
}

Skin Domain Whitelist

skinDomains lists allowed texture domains. authlib-injector uses it for client-side domain checks.

Metadata in meta

Basic Server Information

Recommended fields:

  • serverName
  • implementationName
  • implementationVersion

Recommended meta.links fields:

  • homepage
  • register

Feature Flags

meta.feature indicates optional capabilities. Known keys include:

  • non_email_login
  • legacy_skin_api
  • no_mojang_namespace
  • enable_profile_key
  • enable_mojang_anti_features
  • username_check

Example Response

See the JSON example above; additional fields are allowed for forward compatibility.

API Location Indication (ALI)

ALI allows converting short/incomplete URLs into real API root URLs.

Server behavior:

  • Add response header X-Authlib-Injector-API-Location to relevant URLs.
  • Header may be absolute or relative URL.
  • If header points to itself, current URL is already API root.

Launcher behavior should follow the flow defined in Launcher-Technical-Specification.

See Also

Reference Implementation

Hosted by Fentanyl Solutions