Authenticate through Mojang's session server; add version manifest #4

Merged
selimaj-dev merged 1 commits from mojang-session-auth into master 2026-09-25 13:24:34 +00:00
Owner

Server half of saturnclientmc/saturnclient#7. The client half is saturnclientmc/common (mojang-session-auth).

Summary

Authentication without the access token

auth used to receive the player's Minecraft access token and call the Mojang profile API with it. It's replaced with the flow vanilla online-mode servers use:

  1. auth_challenge(username) -> serverId
    • The username is validated: 1–16 characters of [A-Za-z0-9_].
    • The server returns a random 160-bit hex server id and stores {username, serverId} as this connection's pending challenge.
  2. The client calls POST sessionserver.mojang.com/session/minecraft/join with its token and the server id. The token only goes to Mojang.
  3. auth_verify() -> User
    • The server takes the pending challenge, which is single-use, and calls GET sessionserver.mojang.com/session/minecraft/hasJoined?username=…&serverId=….
    • Mojang returns the verified profile, and the player is logged in as before (session map, get_put).
    • A 204 from Mojang means the join didn't happen, and gives "Session not verified by Mojang".

The old auth is removed, not kept alongside, so there's a single implementation. Clients on 0.1.0-beta2 get "Unknown method: auth" and fail to sign in (see Rollout).

Version manifest

GET /versions returns:

{"supported": ["0.1.0-beta3"], "deprecated": []}
  • The lists come from the comma-separated SUPPORTED_VERSIONS and DEPRECATED_VERSIONS env vars, read at startup. The defaults are 0.1.0-beta3 supported and nothing deprecated.
  • Both variables are exposed in compose.yaml, so they can be edited in Coolify.
  • Clients on a deprecated version are warned but still connect. Clients on neither list are warned and don't connect.

Other

  • reqwest gains the query feature, which 0.13 moved behind a feature flag.
  • rand 0.9 is added for the server id.

Testing

Local server, driven over a raw WebSocket and HTTP:

  • /versions returns the configured lists.
  • The old auth gets "Unknown method: auth".
  • An invalid username gets "Invalid username".
  • auth_verify without a challenge gets "No pending challenge".
  • auth_challenge then auth_verify without a Mojang join gets "Session not verified by Mojang". That was a real hasJoined call.
  • Retrying the verify gets "No pending challenge" (single use).

The Saturn client (1.21.11, real account via DevAuth) against this server:

  • supported: the full flow. The client joined at Mojang, and the server logged Authenticated Kr4ight (…).
  • deprecated: warned, then connected.
  • unsupported: warned, and made no WebSocket connection.

Rollout

Deploying this breaks 0.1.0-beta2 clients immediately, and they don't know about the manifest. Deploy it together with the 0.1.0-beta3 client release. The pasted Coolify compose has no environment: block, but the built-in default (0.1.0-beta3 supported) covers it. Add SUPPORTED_VERSIONS / DEPRECATED_VERSIONS in Coolify to change the lists later.

🤖 Generated with Claude Code

Server half of saturnclientmc/saturnclient#7. The client half is saturnclientmc/common (mojang-session-auth). ## Summary ### Authentication without the access token `auth` used to receive the player's **Minecraft access token** and call the Mojang profile API with it. It's replaced with the flow vanilla online-mode servers use: 1. **`auth_challenge(username) -> serverId`** - The username is validated: 1–16 characters of `[A-Za-z0-9_]`. - The server returns a random 160-bit hex server id and stores `{username, serverId}` as this connection's pending challenge. 2. **The client** calls `POST sessionserver.mojang.com/session/minecraft/join` with its token and the server id. The token only goes to Mojang. 3. **`auth_verify() -> User`** - The server takes the pending challenge, which is single-use, and calls `GET sessionserver.mojang.com/session/minecraft/hasJoined?username=…&serverId=…`. - Mojang returns the verified profile, and the player is logged in as before (session map, `get_put`). - A `204` from Mojang means the join didn't happen, and gives "Session not verified by Mojang". The old `auth` is **removed**, not kept alongside, so there's a single implementation. Clients on `0.1.0-beta2` get "Unknown method: auth" and fail to sign in (see Rollout). ### Version manifest `GET /versions` returns: ```json {"supported": ["0.1.0-beta3"], "deprecated": []} ``` - The lists come from the comma-separated `SUPPORTED_VERSIONS` and `DEPRECATED_VERSIONS` env vars, read at startup. The defaults are `0.1.0-beta3` supported and nothing deprecated. - Both variables are exposed in `compose.yaml`, so they can be edited in Coolify. - Clients on a deprecated version are warned but still connect. Clients on neither list are warned and don't connect. ### Other - `reqwest` gains the `query` feature, which 0.13 moved behind a feature flag. - `rand` 0.9 is added for the server id. ## Testing Local server, driven over a raw WebSocket and HTTP: - `/versions` returns the configured lists. - The old `auth` gets "Unknown method: auth". - An invalid username gets "Invalid username". - `auth_verify` without a challenge gets "No pending challenge". - `auth_challenge` then `auth_verify` **without a Mojang join** gets "Session not verified by Mojang". That was a real `hasJoined` call. - Retrying the verify gets "No pending challenge" (single use). The Saturn client (1.21.11, real account via DevAuth) against this server: - **supported:** the full flow. The client joined at Mojang, and the server logged `Authenticated Kr4ight (…)`. - **deprecated:** warned, then connected. - **unsupported:** warned, and made no WebSocket connection. ## Rollout Deploying this breaks `0.1.0-beta2` clients immediately, and they don't know about the manifest. Deploy it together with the `0.1.0-beta3` client release. The pasted Coolify compose has no `environment:` block, but the built-in default (`0.1.0-beta3` supported) covers it. Add `SUPPORTED_VERSIONS` / `DEPRECATED_VERSIONS` in Coolify to change the lists later. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
selimaj-dev added 1 commit 2026-09-25 13:22:19 +00:00
Replace `auth` (which received the player's Minecraft access token) with
the vanilla online-mode flow, so the token never reaches this server:

- auth_challenge: validate the username and return a random one-time
  server id
- the client calls Mojang's session `join` with its token and that id
- auth_verify: confirm the join with Mojang's `hasJoined` and log the
  player in

Add GET /versions returning {"supported": [...], "deprecated": [...]}
from the SUPPORTED_VERSIONS / DEPRECATED_VERSIONS env vars (defaults:
0.1.0-beta3 supported), exposed in compose.yaml.

Refs saturnclientmc/saturnclient#7

Co-Authored-By: Claude Opus 5.5 <[email protected]>
selimaj-dev merged commit c4ff921a7c into master 2026-09-25 13:24:34 +00:00
selimaj-dev deleted branch mojang-session-auth 2026-09-25 13:24:38 +00:00
Sign in to join this conversation.