Skip to content

XLink Service (Xbox + Minecraft REST API)

LicenseGitHub StarsGitHub Issues

A standalone Node.js/Express service for Microsoft/Xbox Live auth, Xbox Profile/Presence/Captures/Stats, PlayFab client read APIs, and Minecraft (multiplayer token + entitlements) — protected by JWT and shipped with Swagger/OpenAPI docs.

Important

Project Disclaimer
"XLink Service" by SpindexGFX is an independent project. It is not affiliated with, endorsed by, sponsored by, or otherwise connected to Mojang AB, Microsoft Corporation, or any of their subsidiaries or affiliates. No partnership, approval, or official relationship with Mojang AB or Microsoft is implied.
All names, logos, brands, trademarks, service marks, and registered trademarks are the property of their respective owners and are used strictly for identification and reference purposes only. This project does not claim ownership of third-party intellectual property and does not grant any license to use it.


📚 Table of Contents


✨ Highlights

  • Microsoft/Xbox Auth Flows: Legacy and Entra Device Code plus Entra browser login → Tokens (XBL, XSTS for multiple relying parties) → PlayFab login → Minecraft multiplayer token.
  • Comprehensive Xbox Endpoints: Profile, Titles (TitleHub), Presence (including batch), People, Captures (clips/screenshots), Stats, Achievements.
  • PlayFab Client Read: Account, PlayerProfile, Catalog, TitleData, UserData/ReadOnlyData via SessionTicket/EntityToken.
  • Minecraft Services: Generate MCToken, fetch entitlements/balances, and access Marketplace wishlist + inbox messaging.
  • Redeem Flow Support: Prepare/redeem Microsoft Store codes using RedeemNow-compatible calls.
  • JWT-Secured: First-party JWTs guard the API; includes a refresh endpoint.
  • OpenAPI/Swagger: Live docs at /api-docs and GET /openapi.json.
  • Solid Express Base: Helmet, CORS, compression, Joi validation, centralized error handling.
  • Targeted Rate Limiting: Auth endpoints protected out of the box.
  • Fast & Clean Logs: Colorized request logs with badges and request IDs; Swagger/health noise muted.

🚀 Quick Start

  1. Clone & Install

    git clone https://github.com/Daniel-Ric/XLink-Service.git
    cd XLink-Service
    npm install
  2. Configure Create a .env file with the variables below (see 🔧 Configuration).

  3. Run

    node src/server.js
    # Or: NODE_ENV=production PORT=3000 node src/server.js# Dev hot reload (if you use nodemon):
    npx nodemon src/server.js
  4. Explore

    • Swagger UI: http://localhost:3000/api-docs
    • OpenAPI JSON: http://localhost:3000/openapi.json
    • Health: GET /healthz, GET /readyz

🔧 Configuration

Validated via Joi (src/config/env.js).

KeyDefaultDescription
PORT3000Service port
NODE_ENVdevelopmentdevelopment | production | test
CORS_ORIGIN*CORS origin(s), comma-separated (e.g., http://localhost:5173)
JWT_SECRETrequiredAt least 16 chars, used to sign API JWTs
JWT_EXPIRES_IN1hJWT expiry (e.g., 1h, 30m, 2d)
JWT_ISSUERxlink-serviceRequired issuer claim for API JWTs
JWT_AUDIENCExlink-apiRequired audience claim for API JWTs
CLIENT_IDrequiredMicrosoft/Xbox OAuth client ID. Its format selects the legacy or Entra device flow. See Microsoft/Xbox client IDs.
MICROSOFT_AUTH_MODEautoOAuth flow selection: auto, legacy, or modern
MICROSOFT_OAUTH_REDIRECT_URIExact HTTPS Entra Web redirect URI for /auth/browser/callback
MICROSOFT_OAUTH_FRONTEND_REDIRECT_URIOptional fixed HTTPS frontend target that receives only a short-lived result code
MICROSOFT_OAUTH_CLIENT_SECRETEntra Web application secret used by the server-side browser code exchange and modern refresh grant
MICROSOFT_OAUTH_TTL_MS300000Lifetime of one-time OAuth states and browser result codes (60-900 seconds)
HTTP_TIMEOUT_MS15000Timeout for outgoing HTTP calls (ms)
LOG_LEVELinfoGeneral log level
LOG_PRETTYtrue (dev)Pretty logs (true/false), defaults to false in production
MC_GAME_VERSION1.21.62Minecraft game version for token generation
MC_PLATFORMWindows10Platform identifier for Minecraft
PLAYFAB_TITLE_ID20ca2PlayFab Title ID
ACCEPT_LANGUAGEen-USDefault Accept-Language for TitleHub
REDEEM_FLIGHTS_JSONOptional JSON array overriding Microsoft Redeem flight flags
REDEEM_USER_AGENTOptional browser User-Agent override for Redeem calls
REDEEM_SEC_CH_UAOptional sec-ch-ua override for Redeem calls
REDEEM_CV_BASEOptional MS-CV base override for Redeem calls
REDEEM_CLIENT_TYPEMinecraftNetRedeem client type sent to Microsoft
REDEEM_DEVICE_FAMILYWebRedeem device family sent to Microsoft
SWAGGER_ENABLEDtrueEnable Swagger UI and OpenAPI endpoints
SWAGGER_SERVER_URLOverride OpenAPI server URL (defaults to http://localhost:${PORT})
TRUST_PROXYloopbackExpress trust proxy setting (false, loopback, subnet, or hop count like 1)

CORS: In production, set CORS_ORIGIN to explicit origins (no *).

Microsoft/Xbox Client IDs

CLIENT_ID controls both the Microsoft OAuth protocol and the title identity used for Xbox Live/XSTS token exchange:

  • GUID client IDs such as 3a6cd51c-9323-4ee2-be08-1f0d96aba816 use the Microsoft identity platform v2 endpoints under login.microsoftonline.com/consumers with XboxLive.signin XboxLive.offline_access.
  • All other IDs keep the legacy login.live.com endpoints and service::user.auth.xboxlive.com::MBI_SSL.
  • 0000000048183522 therefore remains on the original legacy device-code flow without any configuration change.
  • MICROSOFT_AUTH_MODE=legacy or MICROSOFT_AUTH_MODE=modern overrides format detection for client IDs that do not follow the usual format. Browser login still requires an Entra GUID in modern mode.
  • When CLIENT_ID is changed to an Entra GUID, set XAL_CLIENT_ID=0000000048183522 explicitly if the existing XAL/SISU title identity must remain unchanged.

For this service, the most practical default is the Minecraft: Bedrock Android client ID because it is commonly used with the Xbox Live SISU/device-code flow and is also used by go-xsapi's MinecraftAndroid example configuration.

Target clientClient IDNotes
Minecraft: Java / Win3200000000402b5328Java Edition title client ID.
Minecraft: Bedrock Windows / Win320000000040159362Bedrock Windows title client ID.
Minecraft: Bedrock Android0000000048183522Recommended default for this service. Used by the current .env and by go-xsapi's Minecraft Android SISU example.
Minecraft: Bedrock iOS000000004c17c01aBedrock iOS title client ID.
Minecraft: Bedrock Nintendo00000000441cc96bBedrock Nintendo title client ID.
Minecraft: Bedrock PlayStation000000004827c78eBedrock PlayStation title client ID.
Minecraft Educationb36b1432-1a1c-4c82-9b76-24de1cab42f2Minecraft Education client ID.

Example:

CLIENT_ID=0000000048183522

Changing CLIENT_ID invalidates assumptions made by previously issued Microsoft refresh tokens. After changing it, start a new /auth/device login instead of reusing old msRefreshToken values.

The source project does not own or verify these Microsoft/Mojang/Xbox identifiers. They are listed as interoperability references for users who already understand the target title and platform they want to authenticate as.

Device Code vs. browser login

GET /auth/device is available for both client-ID formats. Legacy clients receive the existing Live device-code request including response_type=device_code; Entra GUID clients use the v2 device endpoint without that parameter. POST /auth/callback and POST /auth/refresh keep their existing request and response contracts and automatically use the matching token endpoint and scope.

GET /auth/browser is an additional interactive authorization-code flow. It redirects directly to Microsoft sign-in and consent, so no device code is entered. Browser login is supported only with an Entra app registration that you own and whose application ID is configured as the GUID CLIENT_ID. A normal legacy Minecraft/Xbox client ID does not give this service permission to register or use its own redirect URI.

Configure the Entra application as follows:

  1. Under Supported account types, select an option that includes personal Microsoft accounts. For Xbox-only sign-in, Personal Microsoft accounts only is the narrowest choice.
  2. Under Authentication, add MICROSOFT_OAUTH_REDIRECT_URI as an exact redirect URI on the Web platform. Production callback URLs must use HTTPS and must match path and case exactly.
  3. Create a client secret under Certificates & secrets and store only its value in the server environment as MICROSOFT_OAUTH_CLIENT_SECRET. The server-side Web code exchange and its later refresh grants require this secret. It is never sent to the browser. PKCE S256 is used in addition to the secret.
  4. Optionally set MICROSOFT_OAUTH_FRONTEND_REDIRECT_URI to one fixed HTTPS frontend callback. The service redirects there with only a short-lived one-time code; the frontend redeems it once at POST /auth/browser/token. No Microsoft, Xbox, XSTS, PlayFab, Minecraft or refresh token is placed in a URL.

The OAuth state, PKCE verifier and optional frontend result are held in process memory. They are single-use, capacity-bounded and expire after MICROSOFT_OAUTH_TTL_MS; deployments with multiple service instances need sticky routing or a shared transient store before enabling browser login.

Authentication responses include microsoftAuthFlow (browser or device). Clients must persist it and return it with msRefreshToken to POST /auth/refresh, so confidential browser refreshes use the client secret while device-code refreshes remain public-client requests. For backwards compatibility, a missing value is treated as browser.

Client handoff sessions created by POST /auth/browser/session use a private, one-time polling token. First-time clients may create a session without a JWT. During re-authentication, clients should include their valid XLink bearer JWT; the Microsoft account completing the browser flow must then have the same XUID, and mismatched logins are discarded.

API JWTs contain non-reversible fingerprints of Xbox, redeem and PlayFab credentials issued with them. Protected routes reject those supplied upstream credentials when they are not bound to the current API JWT. Minecraft tokens are refreshed independently, so their issuing endpoint is protected through the bound PlayFab session ticket instead of pinning a short-lived Minecraft token into the JWT. Existing JWTs issued before this contract change must be replaced by completing or refreshing the Microsoft authentication flow.

Microsoft references used for this implementation:


📂 Project Structure

XLink-Service/
├── src/
│ ├── app.js # Express app, Swagger setup, route mounting, colorful request logger
│ ├── server.js # Bootstrap (port, startup logs, graceful shutdown)
│ ├── config/
│ │ └── env.js # .env validation + export (Joi)
│ ├── middleware/
│ │ ├── error.js # 404 + centralized error handler
│ │ └── rateLimit.js # Auth-specific rate limiter
│ ├── routes/ # Feature routes (Swagger via JSDoc)
│ │ ├── auth.routes.js
│ │ ├── profile.routes.js
│ │ ├── titles.routes.js
│ │ ├── presence.routes.js
│ │ ├── people.routes.js
│ │ ├── captures.routes.js
│ │ ├── achievements.routes.js
│ │ ├── stats.routes.js
│ │ ├── inventory.routes.js
│ │ ├── playfab.routes.js
│ │ ├── minecraft.routes.js
│ │ ├── lookup.routes.js
│ │ ├── redeem.routes.js
│ │ ├── wishlist.routes.js
│ │ ├── messaging.routes.js
│ │ ├── health.routes.js
│ │ └── debug.routes.js # only mounted in non-production
│ ├── services/ # Integrations (Microsoft, Xbox, PlayFab, Minecraft)
│ │ ├── microsoft.service.js
│ │ ├── xbox.service.js # LRU cache for hot endpoints
│ │ ├── playfab.service.js
│ │ ├── minecraft.service.js
│ │ └── redeem.service.js
│ ├── utils/
│ │ ├── async.js # asyncHandler
│ │ ├── http.js # Axios instance with keep-alive agents
│ │ ├── cache.js # LRU cache helper
│ │ ├── httpError.js # HttpError + helpers
│ │ ├── jwt.js # sign/verify + middleware
│ │ ├── logger.js # tiny console logger (optional)
│ │ └── swagger.js # OpenAPI definition (3.0.3)
│ └── ...
└── (LICENSE, README.md, package.json, etc.)

💻 Usage Examples

Most endpoints require Authorization: Bearer <JWT> (from /auth/callback). Many Xbox calls also require x-xbl-token: XBL3.0 x={uhs};{xstsToken}. Minecraft inventory endpoints use x-mc-token.

1) Request device code → /auth/device

curl -X GET http://localhost:3000/auth/device

2) Redeem device code → receive tokens/JWT → /auth/callback

curl -X POST http://localhost:3000/auth/callback -H "Content-Type: application/json" -d '{"device_code":"<DEVICE_CODE_FROM_STEP_1>"}'

3) Start browser login → /auth/browser

Open https://api.example.com/auth/browser in a browser.

For the website flow, open /auth/browser?source=website and redeem the one-time frontend code with the matching source:

curl -X POST https://api.example.com/auth/browser/token -H "Content-Type: application/json" -d '{"code":"<ONE_TIME_RESULT_CODE>","source":"website"}'

Non-browser clients first create a handoff with POST /auth/browser/session, open the returned session through /auth/browser?source=client&session=..., and poll POST /auth/browser/session/token with the private pollToken. The polling token is never placed in the browser URL. A client may include an optional same-origin path such as {"successPath":"/auth/client/success"} when creating the session. xLink redirects there on completion using the configured frontend origin; without it, xLink returns its neutral completion page.

4) Who am I? → /auth/whoami

curl -H "Authorization: Bearer <JWT>" http://localhost:3000/auth/whoami

5) Xbox profile (settings) → /profile/me

curl "http://localhost:3000/profile/me?settings=GameDisplayPicRaw,Gamerscore,Gamertag" -H "Authorization: Bearer <JWT>" -H "x-xbl-token: XBL3.0 x=<uhs>;<xstsToken>"

5) Recently played titles → /titles/recent

curl "http://localhost:3000/titles/recent?limit=20" -H "Authorization: Bearer <JWT>" -H "x-xbl-token: XBL3.0 x=<uhs>;<xstsToken>" -H "Accept-Language: en-US,en;q=0.9"

6) Presence batch → /presence/batch

curl -X POST http://localhost:3000/presence/batch -H "Authorization: Bearer <JWT>" -H "x-xbl-token: XBL3.0 x=<uhs>;<xstsToken>" -H "Content-Type: application/json" -d '{"xuids":["2533274...","2814650..."]}'

7) Minecraft entitlements → /inventory/minecraft

curl "http://localhost:3000/inventory/minecraft?includeReceipt=false" -H "Authorization: Bearer <JWT>" -H "x-mc-token: MCToken eyJ..."

8) PlayFab inventory → /inventory/playfab

curl -X POST http://localhost:3000/inventory/playfab -H "Authorization: Bearer <JWT>" -H "Content-Type: application/json" -d '{"sessionTicket":"<PLAYFAB_SESSION_TICKET>","count":50}'

9) PlayFab inventory test (title id e9d1) → /inventory/playfab/test

curl -X POST http://localhost:3000/inventory/playfab/test -H "Authorization: Bearer <JWT>" -H "Content-Type: application/json" -d '{"playfabToken":"XBL3.0 x=<uhs>;<xstsToken>","entityType":"title_player_account","count":50}'
  • playfabToken comes from POST /auth/callback in this API (response field playfabToken).
  • Use entityType=master_player_account to target the master entity. If you want a specific entity id, pass entityId. Otherwise the service uses the PlayFabId returned by LoginWithXbox.

10) Captures (screenshots) → /captures/screenshots

curl "http://localhost:3000/captures/screenshots?max=24" -H "Authorization: Bearer <JWT>" -H "x-xbl-token: XBL3.0 x=<uhs>;<xstsToken>"

11) Debug: decode token → /debug/decode-token (non-production)

curl -X POST http://localhost:3000/debug/decode-token -H "Authorization: Bearer <JWT>" -H "Content-Type: application/json" -d '{"token":"XBL3.0 x=<uhs>;<xstsToken>","type":"xsts"}'

More examples & schemas: See Swagger UI at http://localhost:3000/api-docs.


📖 API Reference

Auth

MethodEndpointDescription
GET/auth/deviceRequest Microsoft device code
POST/auth/callbackRedeem device code → JWT, XBL/XSTS, PlayFab, MC
GET/auth/browserStart Entra browser authorization-code login
POST/auth/browser/sessionCreate a source-bound client browser handoff
GET/auth/browser/callbackEntra OAuth callback; JSON result or one-time frontend redirect
POST/auth/browser/tokenRedeem a short-lived browser result code
POST/auth/browser/session/tokenPoll and consume a client browser handoff
POST/auth/refreshRefresh tokens via Microsoft refresh_token
GET/auth/whoamiDecoded JWT user info
POST/auth/jwt/refreshRefresh your API JWT

Lookup

MethodEndpointDescriptionHeaders
GET/lookup/xuidResolve XUID from Gamertagx-xbl-token
GET/lookup/gamertagResolve Gamertag from XUIDx-xbl-token

Profile & Titles

MethodEndpointDescriptionHeaders
GET/profile/meProfile settings (selectable fields)x-xbl-token
GET/profile/titlesUser's TitleHub listx-xbl-token
POST/profile/overviewCombined profile, stats, optional inventoryx-xbl-token
GET/titles/recentRecently played (sorted)x-xbl-token

Presence & People

MethodEndpointDescriptionHeaders
GET/presence/mePresence for the authenticated userx-xbl-token
POST/presence/batchPresence for multiple XUIDsx-xbl-token
GET/people/friendsMutual friendsx-xbl-token
GET/people/followersFollowersx-xbl-token
GET/people/friends/presencePresence for first N friendsx-xbl-token

Captures

MethodEndpointDescriptionHeaders
GET/captures/clipsUser's game clipsx-xbl-token
GET/captures/screenshotsUser's screenshotsx-xbl-token

Achievements & Stats

MethodEndpointDescriptionHeaders
GET/achievements/meAchievements (optional titleId)x-xbl-token
GET/achievements/summaryAggregated earned/total for a titlex-xbl-token
GET/stats/xbox/meXbox stats (Minecraft SCIDs) + aggregatesx-xbl-token

Inventory & Minecraft

MethodEndpointDescriptionHeaders
POST/inventory/playfabPlayFab inventory via SessionTicket/EntityToken
POST/inventory/playfab/testPlayFab inventory test via XSTS (title id e9d1)
GET/inventory/minecraftMinecraft entitlements (optional includeReceipt)x-mc-token
GET/inventory/minecraft/balancesMinecraft Marketplace currency balancesx-mc-token
POST/inventory/minecraft/capesMinecraft Dressing Room capes layout page (body optional)x-mc-token
GET/inventory/minecraft/creators/topTop creators from entitlements (by item count)x-mc-token
GET/inventory/minecraft/searchSearch entitlements (productId, q, limit)x-mc-token
POST/wishlist/listMarketplace wishlist pagex-mc-token
POST/wishlist/itemAdd/remove Marketplace wishlist itemx-mc-token
POST/messaging/inbox/startMarketplace inbox session (start/resume)x-mc-token
POST/messaging/session/startAlias of inbox startx-mc-token
POST/messaging/inbox/eventMark seen/delete message eventsx-mc-token
POST/minecraft/tokenCreate Minecraft multiplayer token from SessionTicket
POST/minecraft/token/refreshRotate SessionTicket + Minecraft token + bound API JWT

PlayFab

MethodEndpointDescription
POST/playfab/accountPlayFab account info via SessionTicket
POST/playfab/profilePlayer profile via SessionTicket/PlayFabId
POST/playfab/catalogCatalog items (optional catalog version)
POST/playfab/titledataTitleData values (optional keys)
POST/playfab/userdataUserData (optional keys or PlayFabId)
POST/playfab/userdata/readonlyUserReadOnlyData (optional keys or PlayFabId)

Redeem

MethodEndpointDescriptionHeaders
POST/redeem/lookupPrepareRedeem lookup for a codex-redeem-token
POST/redeem/redeemPrepareRedeem + RedeemToken flowx-redeem-token

Debug & Health

MethodEndpointDescription
POST/debug/decode-tokenDecode JWT, XSTS (XBL3.0), MCToken, and PlayFab sessionTicket token (no verify)
POST/debug/decode-callbackExtract + decode tokens from /auth/callback payload
GET/healthzLiveness
GET/readyzReadiness

Security & Headers: Global BearerAuth (JWT) via Swagger; individual endpoints may require Xbox or MC headers as apiKey schemes.


⛔ Rate Limiting

  • Global limiter: 600 requests/minute/IP (all routes).
  • /auth/*: 30 requests/minute/IP (see src/middleware/rateLimit.js).

📊 Logging & Docs

  • Request logging: Custom colorful logger (badges: OK/WARN/ERR), request duration, status code, method, URL, request ID. Swagger assets and health probes are muted to avoid log spam.
  • Errors: Consistent JSON format; stack traces only in non-production.
  • Swagger/OpenAPI: Generated from route JSDoc in src/utils/swagger.js.
    • UI: GET /api-docs
    • JSON: GET /openapi.json

🛡 Middleware Stack

  1. helmet (security headers)
  2. cors (configurable via CORS_ORIGIN)
  3. express.json({ limit: "1mb" })
  4. compression (gzip)
  5. Global rate limiter (600 req/min)
  6. Custom logger (color badges, muted noise)
  7. Route-specific:
    • jwtMiddleware (JWT validation)
    • authLimiter (only for /auth/*)
  8. notFoundHandlererrorHandler (uniform JSON errors)

🐳 Docker Support

Example Dockerfile to containerize quickly:

# Dockerfile (example)FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "src/server.js"]

Build & Run

docker build -t xlink-service .
docker run -d --name xlink -p 3000:3000 -e PORT=3000 -e JWT_SECRET="change-me-please-change-me" -e CLIENT_ID="<MS_APP_CLIENT_ID>" -e PLAYFAB_TITLE_ID="20ca2" xlink-service

🧪 Scripts

Add or adapt in your package.json:

{
"scripts": {
"start": "node src/server.js",
"dev": "nodemon src/server.js",
"lint": "eslint .",
"test": "jest"
}
}

🤝 Contributing

  1. Fork the repo 🔀
  2. Create a branch: git checkout -b feat/your-feature
  3. Commit: git commit -m "feat: description"
  4. Push: git push origin feat/your-feature
  5. Open a Pull Request 📝

Please follow the code style and provide clear descriptions to ease reviews.


❓ FAQ

  • Why am I getting 401/403?
    Ensure Authorization: Bearer <JWT> is present and, for Xbox calls, also x-xbl-token.

  • How do I obtain x-xbl-token?
    From /auth/callback — it returns JWT, XBL/XSTS, PlayFab, and MC tokens.

  • Missing x-mc-token?
    Either get it from /auth/callback or create one via /minecraft/token using a PlayFab SessionTicket.

  • Title localization & images?
    Send Accept-Language (e.g., en-US,en;q=0.9) with TitleHub endpoints.

  • Upstream timeouts
    Tune HTTP_TIMEOUT_MS (default 15000 ms).

  • Rate limits
    Global limit is 600 req/min/IP; /auth/* is limited to 30 req/min/IP.


📄 License

This project uses the license in LICENSE. See LICENSE for details.

About

XLink-Service is a minimal Node/Express REST API that finalizes Microsoft device flow, returns a complete Xbox + Minecraft token bundle, and exposes read-only endpoints for profile/ titles/ presence/ captures/ achievements/ stats/ inventory—secured with CORS, Helmet, rate limits, logging, and optional Swagger.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

7 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages