Skip to content

Auth

Authenticate a Minecraft client without sending the Minecraft access token to the public API. The flow mirrors the vanilla join/hasJoined handshake:

  1. Call /v1/auth/session/minecraft/join to get a serverId.
  2. The client calls Mojang join with its own access token + profile.
  3. Call /v1/auth/session/minecraft/verify to exchange the join for a JWT.

Endpoints

POST /v1/auth/session/minecraft/join

POST /v1/auth/session/minecraft/verify

Join request

json
{
  "userId": "00000000-0000-0000-0000-000000000000",
  "name": "PlayerName",
  "scopes": ["profile:read", "totem:read", "totem:write"]
}

userId and name are optional but recommended (the verify step will still validate the Mojang session). If scopes is omitted, the default scopes are:

  • profile:read
  • totem:read
  • totem:write

Join response

json
{
  "serverId": "random-server-id",
  "joinToken": "short-lived-join-jwt",
  "expiresIn": 60
}

Mojang join call (client-side)

The client must call Mojang directly (no public API token involved):

POST https://sessionserver.mojang.com/session/minecraft/join

json
{
  "accessToken": "minecraft-access-token",
  "selectedProfile": "minecraft-profile-uuid-no-hyphens",
  "serverId": "random-server-id"
}

Verify request

json
{
  "serverId": "random-server-id",
  "name": "PlayerName",
  "joinToken": "short-lived-join-jwt",
  "userId": "00000000-0000-0000-0000-000000000000"
}

joinToken is required and expires after 60 seconds. If userId is provided, it must match the Mojang profile returned by hasJoined.

Verify response

json
{
  "accessToken": "jwt-token",
  "expiresIn": 2592000,
  "scopes": ["profile:read", "totem:read", "totem:write"]
}

Notes

  • joinToken expires after 60 seconds and can only be used once.
  • Returns 401 if the Mojang join is missing, expired, or already used.
  • A user record is created on first successful verify if it does not exist yet.

Auth session history

GET /v1/profile/auth/sessions?limit=20

Returns recent join-based login sessions for the authenticated user.

json
{
  "items": [
    {
      "id": "join-token-id",
      "createdAt": "2026-01-18T12:34:56Z",
      "ip": "203.0.113.5",
      "userAgent": "TotemMod/1.0",
      "profileName": "PlayerName",
      "serverId": "random-server-id",
      "scopes": ["profile:read"]
    }
  ]
}

Microsoft sign-in

Use the Microsoft OAuth flow to obtain a long-lived API JWT (30 days).

Start login

GET /v1/auth/microsoft/login?platform=dash

Optional query:

  • redirect - absolute URL to send the browser back to after login. Allowed hosts: localhost, 127.0.0.1, and *.uebliche.net (http/https).

Redirects to Microsoft and returns to the callback endpoint. When platform=dash is used, the callback redirects back to the dashboard (or redirect) with:

#authToken=<jwt>&expiresAt=<iso timestamp>

If the login fails, the dashboard is redirected with ?loginError=<message>.

Session exchange (SSO)

GET /v1/auth/session/exchange?platform=build-moe

Reads the uebliche_token cookie (fallback: auth_token) and issues a fresh API JWT without touching Microsoft. If the cookie is missing or invalid, the endpoint falls back to the Microsoft login flow.

platform values:

  • dash - redirects to the dash URL with #authToken=<jwt>&expiresAt=<iso>.
  • build-moe - redirects to the build.moe sign-in URL with the same hash.
  • any other value returns JSON or uses the default web flow.

If the login fails, the redirect receives ?loginError=<message>.

Token verification (build.moe)

GET /auth/verify (also available as /v1/auth/verify)

Validates an API JWT and returns minimal claims for compatibility with build.moe. The token can be supplied via Authorization: Bearer <token> or the uebliche_token / auth_token cookies.

Response:

json
{
  "claims": {
    "sub": "00000000-0000-0000-0000-000000000000",
    "exp": 1700000000,
    "iat": 1699990000
  }
}

Service auth (OAuth-style)

Use services created in Dash to request a scoped JWT that includes service + user context. The flow is browser-based and uses Dash consent screens.

Start service exchange

GET /v1/auth/service/exchange?service=build-moe&redirect=https://build.moe/auth/callback

The API validates the redirect against the service redirect URLs and then redirects to Dash consent. If the user is not signed in, Dash will ask them to sign in with Microsoft first.

If the service is missing, unknown, or the redirect is invalid, the endpoint returns an HTML error page (not JSON) with a short explanation and status code.

After consent is approved, Dash redirects back to the redirect URL with:

#authToken=<jwt>&expiresAt=<iso timestamp>&service=<slug>

Issue a service token (Dash)

POST /v1/auth/service/token

Requires an API JWT (Bearer Authorization). Dash calls this after consent to issue the service token.

Request body:

json
{
  "service": "build-moe",
  "redirect": "https://build.moe/auth/callback"
}

Response:

json
{
  "accessToken": "jwt-token",
  "expiresIn": 2592000,
  "expiresAt": "2026-01-26T12:00:00Z",
  "permissions": ["profile:read"],
  "service": {
    "id": "service-id",
    "slug": "build-moe",
    "name": "Build.moe",
    "verificationLevel": "original",
    "verified": true
  },
  "user": {
    "id": "user-uuid"
  }
}

Callback

GET /v1/auth/microsoft/callback

This endpoint is called by Microsoft after login. It issues an API JWT and returns an HTML response that redirects to the dashboard (or shows the token if no dashboard platform is selected). If the user does not exist yet, the API creates the account during this step.

Configuration

The public API uses the following environment variables:

  • MICROSOFT_CLIENT_ID
  • MICROSOFT_CLIENT_SECRET
  • MICROSOFT_REDIRECT_URI (default: https://api.uebliche.net/v1/auth/microsoft/callback)
  • MICROSOFT_DASH_REDIRECT (default: https://dash.uebliche.net)
  • MICROSOFT_BUILD_MOE_REDIRECT (default: https://build.moe/signin)
  • MICROSOFT_SCOPES (default: XboxLive.signin offline_access)