Auth
Authenticate a Minecraft client without sending the Minecraft access token to the public API. The flow mirrors the vanilla join/hasJoined handshake:
- Call
/v1/auth/session/minecraft/jointo get aserverId. - The client calls Mojang
joinwith its own access token + profile. - Call
/v1/auth/session/minecraft/verifyto exchange the join for a JWT.
Endpoints
POST /v1/auth/session/minecraft/join
POST /v1/auth/session/minecraft/verify
Join request
{
"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:readtotem:readtotem:write
Join response
{
"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
{
"accessToken": "minecraft-access-token",
"selectedProfile": "minecraft-profile-uuid-no-hyphens",
"serverId": "random-server-id"
}Verify request
{
"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
{
"accessToken": "jwt-token",
"expiresIn": 2592000,
"scopes": ["profile:read", "totem:read", "totem:write"]
}Notes
joinTokenexpires after 60 seconds and can only be used once.- Returns
401if 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.
{
"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:
{
"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:
{
"service": "build-moe",
"redirect": "https://build.moe/auth/callback"
}Response:
{
"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_IDMICROSOFT_CLIENT_SECRETMICROSOFT_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)