High level overview

Note: The following document proposes a roadmap. Most features here have not yet been implemented, and are still open to feedback from the community!

If you have suggestions or concerns, please raise them in our Discord server, bridged Matrix server, or GitLab repo issue tracker.

Client initialization

When a client launches for the first time, it should create a new secure random Ed25519 identity keypair. Users should have the option of saving the keypair on disk, or entering it as-needed from a password manager. There should be some UI for importing/exporting and managing multiple identities from the Minecraft title screen. This menu can also include management of the user's profile name and skin texture for each identity.

Saved servers should display a status indicator for whether or not they advertise support for Decentralized Auth (see Server initialization for more details).

Server initialization

When a server launches for the first time, it should create a new secure random Ed25519 identity keypair. The keypair should be saved to disk.

Servers should broadcast that they accept Decentralized Auth in their server metadata by adding an additional numeric JSON field: decentralizedAuth: 1. The number presented in the JSON field is intended to be incremented in the case of an incompatible protocol upgrade. Clients should explicitly check that the number is 1 and avoid trying to login to a server which presents a higher number. A state-of-the-art cryptographic signature algorithm was chosen for identity proofs, so protocol upgrades are not expected to be common, but in the far future it may be necessary to switch to a more secure algorithm. There is no expectation of compatibility or built-in migratability between clients and servers using different Decentralized Auth protocols.

Connection process

In order to maintain compatibility with Microsoft account logins, the Decentralized Auth process is implemented using a completely new login handler. Mixing in to the existing login handler was considered, but too many changes are necessary than it's actually worth.

First, the client sends a handshake with a unique "next state" value to the server. Official server implementations read this value from a packet as a VarInt, so we have plenty of values to choose from. It should be safest to select a relatively small value that still fits into a single byte, while still giving some distance from the current largest value (i.e., 2) in the case of future updates to the official protocol. According to the above criteria, a value of 69 should be sufficient. The existing handshakes require a followup packet from the client, even if there's no new information to send, so the client will send an empty Decentralized Auth Start packet.

When the server receives a handshake with a next state of 69, it will expect a corresponding Decentralized Auth start packet. Then it will generate a new ephemeral X25519 keypair for transport encryption and send the public key in an Encryption Request packet.

The client will respond by generating its own ephemeral X25519 keypair and sending the public key in an Encryption Response packet.

At this point, both the client and server complete the X25519 key exchange and agree on a shared secret. They will use the shared secret to derive 16-byte AES CFB8 stream ciphers using HKDF with a Blake2b digest function. The client and server enable encryption on all future packets in the session using these ciphers, as in the vanilla online login protocol.

Next, the server will send an Auth Challenge packet. The packet includes the server's proof of identity in the form of its own Ed25519 identity public key as well as a signature of the shared secret from the encryption steps, signed using the identity private key.

When the client receives the server's identity public key and signature, it should verify that the signature corresponds to the correct public key. If not, it should end the connection immediately with a protocol error. If the client has connected to the same server before, it should also verify that the identity public key is the same as it was in any previous connections. If not, some warning about MitM interference can be shown to the user so they can reconsider if they want to join. If the joining process continues, the client will send an Auth Proof back to the server, which similarly contains its own Ed25519 identity public key and signed shared secret.

The server should similarly verify that the client's signature matches its advertised public key. At this point, the server can derive the client's UUIDv5 using the client Ed25519 identity public key. It should check whether or not to reject the player based on any configured whitelists, banlists, etc. Then, it will send a Profile Request packet. Vanilla servers keep a cache of user's profiles that is updated at each login; if the server has a cached profile for the player it should send it in the profile request. The cached profile includes the player's last known username and SHA256 hashes of their last used skin and cape textures.

The client should respond with a Profile Response packet. This packet sends the client's desired profile, including its preferred username (which may be different from the last known username) and optionally its preferred skin/cape textures if the server's cached versions do not match the desired ones.

At this point the server will run a login hook based on the client's requested profile. The login hook is a modular component that allows the server to enforce additional custom policies for users based on their requested usernames and public key identities. If desired, the login hook may re-assign the player's username or deny the login request.

If the server has received skin data in the profile request, it should validate that they obey vanilla criteria for length and size, respectively. It should cache the latest skin texture for any Decentralized Auth UUID that connects to the server. Caching textures ensures that the server can save bandwidth on future connections, and also allows them to be served to other players who log in later. The server should finally send the Login Success packet with the profile information selected by the login hook, creating the player's in-game representation and beginning the gameplay session.

Auth config

Servers should be able to configure whether or not to accept vanilla connections. By default, vanilla connections are accepted.

Stretch goals

E2E encrypted messaging between Decentralized Auth clients

This feature would be enabled on the server by default. However, the server can refuse to forward E2E encrypted message packets if sideband messaging might interfere with in-game mechanics.

Non-goals

Account migration / mirroring tools

Players may wish to have server progress from their official Minecraft account moved and/or mirrored to a Decentralized Auth account. This is understandable but would be best served as an alternate, general-purpose account migration tool that can transfer all data from any player's UUID to another.

Broader web3 integration, ICO, NFT skins, etc.

This is just an authentication tool, for logging in to a Minecraft server using a unique private key. If you want, nobody's stopping you from building an ENS-like blockchain registry for Minecraft account names and content-addressed skins on top of it! Keeping Decentralized Auth composable allows different servers to support different use-cases.