Sign in with Minecraft

ให้ผู้เล่นยืนยันตัวตนด้วยบัญชี Minecraft: /authorize, Redirect, Webhook, /auth/token และการตรวจลายเซ็น

Sign in with Minecraft คืออะไร

ให้ผู้เล่นพิสูจน์ต่อแอปของคุณว่าเป็นเจ้าของบัญชี Minecraft ใด โดยแอปได้รับ uuid และชื่อที่ผ่านการยืนยันกับ Mojang แล้ว รูปแบบเดียวกับการ Login with Discord แต่ตัวตนที่พิสูจน์คือบัญชี Minecraft การพิสูจน์ทำที่ Launcher เท่านั้น (ผ่านการ join session กับ Mojang) จึงมีขั้นตอนเด้งจากเว็บไปที่ Launcher

ขั้นตอนใครทำเกิดอะไรขึ้น
1แอปของคุณส่งผู้เล่นไปหน้า /authorize ของ Dashboard พร้อมพารามิเตอร์
2ผู้เล่น (เว็บ)เห็นชื่อแอป ข้อมูลที่จะแชร์ และโดเมนปลายทาง กด ดำเนินการต่อด้วย Launcher เว็บแสดงรหัส 4 ตัว
3ผู้เล่น (Launcher)Launcher เปิดขึ้น ผู้เล่นกรอกรหัส 4 ตัวจากเว็บแล้วกดยืนยัน
4ระบบส่งผลให้แอปตามโหมด: Redirect (redirect พร้อม code) หรือ Webhook (POST ที่เซ็นกำกับ)
5server ของคุณRedirect: นำ code ไปแลกเป็นข้อมูลผู้เล่นที่ POST /auth/token / Webhook: ได้ข้อมูลมากับ POST เลย
ทำไมต้องกรอกรหัส 4 ตัวใน Launcher

ป้องกันการหลอกให้ผู้เล่นกดยืนยันแทนผู้อื่น ถ้าคนร้ายเริ่มคำขอบนเครื่องของตัวเองแล้วส่งลิงก์เปิด Launcher ให้เหยื่อ เหยื่อไม่เห็นรหัสบนหน้าเว็บของคนร้าย จึงยืนยันไม่ได้ กรอกรหัสผิดครบ 5 ครั้ง คำขอถูกยกเลิก

ตั้งค่าแอป

ในหน้าแอป ส่วน Auth เลือกโหมดและกรอกที่อยู่ (ดู Developer zone):

การตั้งค่าชนิดข้อกำหนด
callbackType"REDIRECT" | "WEBHOOK"ค่าเริ่มต้น REDIRECT
redirectUrisstring[]ใช้กับ Redirect ได้ไม่เกิน 10 ที่อยู่ แต่ละที่อยู่ไม่เกิน 500 ตัวอักษร ต้องเป็น https:// (หรือ http://localhost / http://127.0.0.1 สำหรับพัฒนา) และไม่มี #fragment ต้องมีอย่างน้อยหนึ่งที่อยู่เมื่อใช้ Redirect
webhookUrlstring | nullใช้กับ Webhook ต้องเป็น https:// ไม่เกิน 500 ตัวอักษร และต้องชี้ไปที่อยู่สาธารณะ (ดูหัวข้อ Webhook)
authScopes"identify"[]scope ที่แอปขอได้ ตอนนี้มีค่าเดียวคือ identify

1. ส่งผู้เล่นไปหน้าขอสิทธิ์ (/authorize)

เป็นหน้าเว็บของ Dashboard (โดเมนเดียวกับที่คุณเปิดเอกสารนี้ ไม่ใช่โดเมนของ API) ให้ผู้เล่นเปิดผ่านเบราว์เซอร์ด้วยการ redirect หรือปุ่มลิงก์ ไม่ต้องใช้ client_secret ในขั้นนี้

GET{Dashboard}/authorize?client_id=…&scope=identify&state=…&redirect_uri=…
url
https://<โดเมนของเว็บนี้>/authorize
  ?client_id=ckx1abcd0000
  &scope=identify
  &state=3f9c1e7a52b84d0e8a6c
  &redirect_uri=https%3A%2F%2Fexample.com%2Fauth%2Fminecraft%2Fcallback

พารามิเตอร์ (query string)

ชื่อชนิดจำเป็นรายละเอียด
client_idstringใช่client_id ของแอป
redirect_uristring (URL)ใช่ ถ้าเป็น Redirect
ไม่ใช้ ถ้าเป็น Webhook
ต้องตรงทุกตัวอักษรกับหนึ่งใน redirectUris ที่ลงทะเบียน (รวมถึง / ท้ายและ query) ไม่เกิน 500 ตัวอักษร ต้อง URL-encode ในลิงก์ ถ้าไม่ตรง หน้าเว็บแจ้งข้อผิดพลาดและไม่ redirect ไปไหน
scopestringไม่ (ค่าเริ่มต้น identify)รายชื่อ scope คั่นด้วยช่องว่าง , หรือ + ต้องเป็น scope ที่แอปเปิดไว้ ถ้ามี scope ที่ไม่รู้จักตอบ 400 ถ้าเป็น scope ที่แอปไม่ได้เปิดตอบ 403
statestring (8-200 ตัวอักษร)ใช่ค่าสุ่มที่คุณสร้างและเก็บไว้ในเซสชันของผู้เล่น ระบบส่งคืนให้ตรงตัวพร้อมผลลัพธ์ เพื่อให้คุณเทียบว่าเป็นคำขอของผู้เล่นคนเดียวกัน (ป้องกัน CSRF) ถ้าไม่ส่งหรือสั้นกว่า 8 ตัว ปุ่มดำเนินการต่อจะขึ้นข้อผิดพลาด
ต้องมี state และต้องเทียบทุกครั้ง

สร้าง state ใหม่แบบสุ่ม (เช่น 16 ไบต์จาก CSPRNG) ทุกครั้งที่เริ่ม เก็บไว้ผูกกับเบราว์เซอร์ของผู้เล่น และปฏิเสธผลที่ state ไม่ตรง

2a. โหมด Redirect

เมื่อผู้เล่นตอบใน Launcher หน้าเว็บ redirect เบราว์เซอร์ไปที่ redirect_uri ของคุณ โดยเติม query ต่อท้าย (ถ้า URI มี query อยู่แล้วจะคงไว้ แล้วเติมเพิ่ม):

ผลลัพธ์query ที่เติมความหมาย
ผู้เล่นยืนยันcode=<code>&state=<state>code เป็นสตริงทึบ (opaque) ใช้ได้ครั้งเดียว อายุ 5 นาที นับจากผู้เล่นกดยืนยัน
ผู้เล่นปฏิเสธerror=access_denied&state=<state>ไม่มีข้อมูลผู้เล่นถูกส่ง
ผู้เล่นกดยกเลิกที่หน้าเว็บerror=access_denied&state=<state>เหมือนปฏิเสธ
url
https://example.com/auth/minecraft/callback?code=8Qm3vT0x2aYb7Jk9LwZp4RtN1sUe6HcD&state=3f9c1e7a52b84d0e8a6c

ถ้าผู้เล่นปิดหน้าหรือปล่อยจนหมดเวลา (คำขออายุ 10 นาทีนับจากกดดำเนินการต่อ) จะไม่มีการ redirect กลับ ให้แอปรองรับกรณีนี้ด้วยการหมดอายุของ state ฝั่งคุณเอง

แลก code เป็นข้อมูลผู้เล่น

POST/api/v1/auth/token

ต้องมี Basic auth ของแอป (อ่าน ภาพรวม) เรียกจาก server ของคุณเท่านั้น

Request body (application/json):

ฟิลด์ชนิดจำเป็นข้อกำหนด
codestringใช่ความยาว 1-200 ตัวอักษร ค่า code จาก query ของ redirect ตรงๆ
ts
interface TokenRequest {
  code: string; // 1..200 ตัวอักษร
}

interface TokenResponse {
  statusCode: 200;
  data: {
    state: string;          // state ที่คุณส่งไปตอนเริ่ม ต้องเทียบกับที่เก็บไว้
    scopes: "identify"[];   // scope ที่ผู้เล่นอนุญาต
    player: {
      uuid: string;         // 32 หลักตัวพิมพ์เล็ก ไม่มีขีด
      name: string;         // ชื่อผู้เล่น Minecraft ณ ตอนยืนยัน
    };
  };
}
http
POST /api/v1/auth/token HTTP/1.1
Authorization: Basic Y2t4MWFiY2QwMDAwOmRtc2FwcF8uLi4=
Content-Type: application/json

{ "code": "8Qm3vT0x2aYb7Jk9LwZp4RtN1sUe6HcD" }
json
{
  "statusCode": 200,
  "data": {
    "state": "3f9c1e7a52b84d0e8a6c",
    "scopes": ["identify"],
    "player": {
      "uuid": "069a79f444e94726a5befca90e38aaf5",
      "name": "Notch"
    }
  }
}
สถานะmessageเมื่อไหร่
400Invalid or expired codecode ไม่มีอยู่ หมดอายุ (เกิน 5 นาที) ถูกใช้ไปแล้ว หรือเป็น code ของแอปอื่น (code ของแอปอื่นถูกทำลายเมื่อมีคนลองใช้)
400Validation failedไม่ส่ง code หรือชนิด/ความยาวผิด (มีรายการ errors)
401Invalid client credentialsclient_id หรือ secret ผิด
js
// ตัวอย่าง Express: callback ของ Redirect
app.get("/auth/minecraft/callback", async (req, res) => {
  const { code, state, error } = req.query;
  if (error) return res.redirect("/login?error=" + encodeURIComponent(String(error)));
  if (!code || state !== req.session.authState) return res.status(400).send("state ไม่ตรง");
  delete req.session.authState; // ใช้ครั้งเดียว

  const r = await fetch(API + "/api/v1/auth/token", {
    method: "POST",
    headers: {
      Authorization: "Basic " + Buffer.from(CLIENT_ID + ":" + CLIENT_SECRET).toString("base64"),
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ code }),
  });
  if (!r.ok) return res.status(400).send("ยืนยันตัวตนไม่สำเร็จ");
  const { data } = await r.json();
  req.session.player = data.player; // { uuid, name }
  res.redirect("/");
});

2b. โหมด Webhook

server ของเรายิง POST ไปที่ webhookUrl ของคุณเมื่อผู้เล่นยืนยัน ผู้เล่นไม่ถูกส่งต่อไปที่เว็บของคุณ (หน้าเว็บของเราแจ้งว่าสำเร็จ) ไม่ต้องเรียก /auth/token เพราะข้อมูลผู้เล่นมากับ POST ตรงๆ ไม่ต้องส่ง redirect_uriตัวแอปจับคู่ผลกับผู้ใช้ของตัวเองด้วย state (เช่นตอนสร้างลิงก์ ให้เก็บ state ผูกกับผู้ใช้ไว้ในฐานข้อมูลของคุณ) กรณีที่ผู้เล่นปฏิเสธหรือไม่ตอบ จะไม่มีการส่ง webhook

คำขอที่คุณได้รับ

ส่วนค่า
Method / URLPOST ไปที่ webhookUrl ของแอป
Content-Typeapplication/json
User-AgentDimensionLauncherManager-Webhook/1.0
X-DMS-Timestampเวลาที่ส่ง เป็น Unix timestamp หน่วยวินาที (สตริงตัวเลข) เช่น 1790000000
X-DMS-Signaturesha256=<hex> ลายเซ็น HMAC ดูหัวข้อถัดไป
ts
interface AuthCompletedWebhook {
  event: "auth.completed";   // ตอนนี้มีเหตุการณ์เดียว
  state: string;             // state ที่แอปส่งตอนเริ่ม
  scopes: "identify"[];      // scope ที่ผู้เล่นอนุญาต
  player: {
    uuid: string;            // 32 หลักตัวพิมพ์เล็ก ไม่มีขีด
    name: string;            // ชื่อผู้เล่น Minecraft ณ ตอนยืนยัน
  };
}
json
{
  "event": "auth.completed",
  "state": "3f9c1e7a52b84d0e8a6c",
  "scopes": ["identify"],
  "player": { "uuid": "069a79f444e94726a5befca90e38aaf5", "name": "Notch" }
}

ตรวจลายเซ็น

  1. อ่าน body ดิบ (ก่อน parse JSON) เป็นสตริง และ header X-DMS-Timestamp, X-DMS-Signature
  2. คำนวณกุญแจ: key = SHA-256(client_secret) เป็นสตริง hex ตัวพิมพ์เล็ก (ระบบเก็บเฉพาะ hash ของ secret จึงใช้ hash เป็นกุญแจ แอปของคุณหาค่าเดียวกันได้จาก secret)
  3. คำนวณ expected = "sha256=" + hex(HMAC-SHA256(key, timestamp + "." + rawBody))
  4. เทียบแบบ constant-time กับ X-DMS-Signature
  5. ปฏิเสธถ้า timestamp ต่างจากเวลาปัจจุบันเกินไม่กี่นาที (แนะนำ 5 นาที) กันการส่งซ้ำ และกันการประมวลผล state เดิมซ้ำ
js
// Node.js (Express): ต้องใช้ body ดิบ
import crypto from "node:crypto";

const KEY = crypto.createHash("sha256").update(process.env.CLIENT_SECRET).digest("hex");

app.post("/hooks/minecraft", express.raw({ type: "application/json" }), (req, res) => {
  const timestamp = req.get("X-DMS-Timestamp") ?? "";
  const signature = req.get("X-DMS-Signature") ?? "";
  const raw = req.body.toString("utf8");

  const expected =
    "sha256=" + crypto.createHmac("sha256", KEY).update(timestamp + "." + raw).digest("hex");
  const ok =
    signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300;
  if (!ok || !fresh) return res.sendStatus(401);

  const event = JSON.parse(raw); // { event, state, scopes, player }
  // จับคู่ event.state กับผู้ใช้ของคุณ แล้วผูก event.player.uuid
  res.sendStatus(204);
});
python
# Python (Flask)
import hashlib, hmac, time
from flask import request, abort

KEY = hashlib.sha256(CLIENT_SECRET.encode()).hexdigest()

@app.post("/hooks/minecraft")
def hook():
    ts = request.headers.get("X-DMS-Timestamp", "")
    sig = request.headers.get("X-DMS-Signature", "")
    raw = request.get_data(as_text=True)
    expected = "sha256=" + hmac.new(KEY.encode(), f"{ts}.{raw}".encode(), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(sig, expected) or abs(time.time() - int(ts)) > 300:
        abort(401)
    event = request.get_json()
    return "", 204

การส่งและการลองใหม่

หัวข้อรายละเอียด
ที่อยู่ปลายทางต้องเป็น https:// และชื่อโฮสต์ต้องชี้ไปที่อยู่ สาธารณะ ที่อยู่ภายใน (loopback, 10.x, 172.16-31.x, 192.168.x, 169.254.x, 100.64-127.x, IPv6 ภายใน) ถูกปฏิเสธ ทุกครั้งที่ส่ง
การ redirectไม่ตาม redirect (ตอบ 3xx ถือว่าไม่สำเร็จ)
เวลารอ5 วินาทีต่อครั้ง
เกณฑ์สำเร็จตอบ 2xx ถึงจะถือว่าได้รับแล้ว
ลองใหม่ส่งไม่สำเร็จ (ไม่ใช่ 2xx, หมดเวลา หรือเชื่อมต่อไม่ได้) ระบบลองซ้ำอีก 2 ครั้ง หลังจาก 1 วินาทีและ 4 วินาที รวมสูงสุด 3 ครั้ง แล้วเลิก ไม่มีคิวเก็บ
การไม่รอการส่งเกิดเบื้องหลัง ผู้เล่นไม่ต้องรอ webhook ของคุณ
การซ้ำอาจได้รับ webhook เดียวกันซ้ำได้ (เช่น ตอบช้าจนระบบลองใหม่) ให้ประมวลผลแบบ idempotent โดยใช้ state

ข้อผิดพลาดที่ผู้เล่นอาจเห็นที่หน้า /authorize

หน้านี้ตรวจคำขอก่อนแสดงผล ถ้าไม่ถูกต้องจะแจ้งข้อผิดพลาดและไม่ redirect ผู้เล่นไปที่ไหน:

ข้อความ (message จาก server)สาเหตุวิธีแก้
Unknown applicationไม่มีแอปที่ client_id นี้ตรวจ client_id
redirect_uri is not registered for this applicationRedirect แต่ไม่ส่ง redirect_uri หรือไม่ตรงกับที่ลงทะเบียนลงทะเบียนที่อยู่ในหน้าแอป และส่งให้ตรงตัวอักษร
Unknown scope: xขอ scope ที่ระบบไม่มีใช้ identify
This application may not ask for: xscope มีในระบบแต่แอปไม่ได้เปิดเปิด scope ในหน้าแอป
This application has no webhook setโหมด Webhook แต่ยังไม่ได้ใส่ webhookUrlใส่ Webhook URL
Validation failedstate ไม่ได้ส่งหรือสั้นกว่า 8 ตัวส่ง state แบบสุ่มยาวอย่างน้อย 8 ตัว

เวลาและขีดจำกัดทั้งหมด

รายการค่า
คำขอ (จากกดดำเนินการต่อ จนผู้เล่นตอบใน Launcher)10 นาที
code ของ Redirect หลังผู้เล่นยืนยัน5 นาที ใช้ได้ครั้งเดียว
รหัส 4 ตัวที่ผู้เล่นกรอกใน Launcherผิดได้ไม่เกิน 5 ครั้ง ครบแล้วคำขอถูกยกเลิก
ตัวอักษรของรหัส 4 ตัวA-Z และ 2-9 ตัดตัวที่สับสนออก (0 O 1 I L) ไม่สนตัวพิมพ์เล็กใหญ่
การส่ง webhook5 วินาทีต่อครั้ง สูงสุด 3 ครั้ง

ดูชนิดข้อมูลทั้งหมดที่ อ้างอิงชนิดข้อมูลและข้อผิดพลาด และ Instance API ที่ Instance API: ผู้เล่น

เอกสารต่างๆ

Term of servicePrivacy Policy

Dimension Studio

ไดแมนชั่น สตูดิโอ

ดิสคอร์ด :

Dimension Studio

เว็บไซต์ :

Dimension Studio

ยูทูป :

Dimension Studio

Copyright © 2024-2026 บริษัท เน็กชันนารี จำกัด (NEXIONARY Company Limited). All rights reserved.

ทะเบียนเลขที่ 50105568159679

Dimension Launcher ไม่มีส่วนเกี่ยวข้องกับ Mojang AB หรือ Microsoft Corporation