ข้ามไปยังเนื้อหาหลัก

การย้ายผู้ใช้ (User migration)

Logto รองรับทั้งการย้ายผู้ใช้แบบกลุ่ม (bulk) และแบบ Just-in-time จากระบบข้อมูลระบุตัวตนเดิม คู่มือนี้อธิบายวิธีนำเข้าผู้ใช้แบบกลุ่มผ่าน Management API และสิ่งที่ควรพิจารณาก่อนการย้าย

เลือกกลยุทธ์การย้าย​

กลยุทธ์เลือกใช้เมื่อวิธีการทำงาน
การย้ายแบบกลุ่ม (Bulk migration)คุณสามารถส่งออกข้อมูลผู้ใช้และรหัสผ่านที่เข้ารหัสไว้ในรูปแบบที่ Logto รองรับได้นำเข้าผู้ใช้ก่อนการเปลี่ยนระบบผ่าน Management API ตามขั้นตอนในคู่มือนี้
การย้ายแบบ Just-in-time (Just-in-time migration)คุณต้องตรวจสอบรหัสผ่านกับระบบเดิม ไม่สามารถส่งออกรหัสผ่านที่เข้ารหัสในรูปแบบที่รองรับ หรืออยากย้ายผู้ใช้ที่ใช้งานอยู่ทีละน้อยกำหนดค่า Post first-factor verification Action เมื่อผู้ใช้ลงชื่อเข้าใช้ด้วยรหัสผ่านครั้งแรก Logto จะตรวจสอบข้อมูลรับรองผ่าน Action ของคุณ สร้างหรืออัปเดตผู้ใช้ และจัดเก็บรหัสผ่านใหม่ในรูปแบบ local hash

การย้ายแบบ Just-in-time จะคงบริการการยืนยันตัวตนเดิมไว้ในเส้นทางการร้องขอลงชื่อเข้าใช้จนกว่าผู้ใช้จะถูกย้าย ควรใช้ endpoint HTTPS ที่รวดเร็วและเชื่อถือได้ และเปิดใช้งานตลอดช่วงเวลาการย้าย

โครงสร้างข้อมูลผู้ใช้ (User schema)​

ก่อนเริ่มต้น มาดู โครงสร้างข้อมูลผู้ใช้ ใน Logto กันก่อน โครงสร้างข้อมูลผู้ใช้ใน Logto มี 3 ส่วนที่ควรทราบ:

  1. ข้อมูลพื้นฐาน (Basic data): คือข้อมูลพื้นฐานจากโปรไฟล์ผู้ใช้ คุณสามารถจับคู่ข้อมูลจากโปรไฟล์ผู้ใช้เดิมของคุณได้
  2. ข้อมูลกำหนดเอง (Custom data): เก็บข้อมูลเพิ่มเติมของผู้ใช้ ใช้สำหรับเก็บข้อมูลที่ไม่สามารถจับคู่กับข้อมูลพื้นฐานได้
  3. ข้อมูลโซเชียล (Social identities): เก็บข้อมูลผู้ใช้ที่ได้จากการลงชื่อเข้าใช้ผ่านโซเชียล

คุณสามารถสร้างแผนที่ (mapping) เพื่อจับคู่ข้อมูลผู้ใช้จากโปรไฟล์เดิมไปยัง ข้อมูลพื้นฐาน และ ข้อมูลกำหนดเอง สำหรับการลงชื่อเข้าใช้ผ่านโซเชียล จะต้องมีขั้นตอนเพิ่มเติมในการนำเข้าข้อมูลโซเชียล โปรดดู API ที่ เชื่อมโยงข้อมูลโซเชียลกับผู้ใช้

เก็บ ID ผู้ใช้เดิม (Keep existing user IDs)​

โดยปกติ Logto จะสร้าง ID แบบสุ่มให้กับผู้ใช้ทุกคนที่สร้างผ่าน Management API หากแอปของคุณมีการเก็บ ID ผู้ใช้จากผู้ให้บริการข้อมูลระบุตัวตนเดิมอยู่แล้ว คุณสามารถเก็บ ID เหล่านั้นได้โดยส่ง id (ไม่บังคับ) ใน request body ของ create user วิธีนี้ช่วยหลีกเลี่ยงการต้องสร้างตาราง mapping ระหว่าง ID เก่าและใหม่

ID ต้องมีความยาว 1 ถึง 128 ตัวอักษร และสามารถประกอบด้วยตัวอักษร ตัวเลข และ _ - . @ : + = | ซึ่งครอบคลุมรูปแบบทั่วไป เช่น auth0|5f7c8ec7c33c6c004bbafe82, google-oauth2|103547991597142817347, UUID, user_01H8MZ2QK3V4X5Y6Z7 และอีเมล ตัวอักษรที่ไม่ถูกต้องใน URL path (ช่องว่าง, /, ?, #, %, \) จะถูกปฏิเสธด้วยรหัส 400 เนื่องจาก ID นี้ใช้ใน /api/users/{userId} หาก ID ถูกใช้ไปแล้ว คำขอจะล้มเหลวด้วย 422 user.id_already_in_use

{
"id": "auth0|5f7c8ec7c33c6c004bbafe82",
"primaryEmail": "john.doe@example.com",
"passwordAlgorithm": "Bcrypt",
"passwordDigest": "$2b$10$..."
}
บันทึก:

Custom user IDs รองรับเฉพาะใน Logto ที่ติดตั้งเอง (self-hosted) เท่านั้น Logto Cloud จะปฏิเสธคำขอที่มี id ด้วย 501 request.feature_not_supported เนื่องจาก ID ผู้ใช้ถูกแชร์ข้าม tenant

การแฮชรหัสผ่าน (Password hashing)​

Logto ใช้ Argon2 ในการแฮชรหัสผ่านของผู้ใช้ และยังรองรับอัลกอริทึมอื่น เช่น MD5, SHA1, SHA256 และ Bcrypt เพื่อความสะดวกในการย้าย อัลกอริทึมเหล่านี้ถือว่าไม่ปลอดภัย รหัสผ่านที่แฮชด้วยอัลกอริทึมเหล่านี้จะถูกย้ายไปใช้ Argon2 เมื่อผู้ใช้ลงชื่อเข้าใช้สำเร็จครั้งแรก

หากคุณใช้อัลกอริทึมหรือ salt อื่น ๆ สามารถตั้งค่า passwordAlgorithm เป็น Legacy เพื่อใช้แฮชอัลกอริทึมใด ๆ ที่ Node.js รองรับ ดูรายการอัลกอริทึมที่รองรับได้ใน Node.js crypto documentation ในกรณีนี้ passwordDigest จะเป็น JSON string ที่มีชื่ออัลกอริทึมและพารามิเตอร์เฉพาะของอัลกอริทึมนั้น

รูปแบบ Legacy ทั่วไป (General Legacy format)​

รูปแบบของ JSON string คือ:

["hash_algorithm", ["argument1", "argument2", ...], "expected_hashed_value"]

และสามารถใช้ @ เป็นตัวแทนรหัสผ่านจริงใน argument ได้

ตัวอย่างเช่น หากใช้ SHA256 กับ salt สามารถเก็บรหัสผ่านในรูปแบบนี้:

["sha256", ["salt123", "@"], "c465f66c6ac481a7a17e9ed5b4e2e7e7288d892f12bf1c95c140901e9a70436e"]

ซึ่งเทียบเท่ากับโค้ดนี้:

const hash = crypto.createHash('sha256');
hash.update('salt123' + 'password123');
const expectedHashedValue = hash.digest('hex');
// expectedHashedValue = "c465f66c6ac481a7a17e9ed5b4e2e7e7288d892f12bf1c95c140901e9a70436e"

รองรับ PBKDF2 (PBKDF2 support)​

Logto รองรับ PBKDF2 โดยเฉพาะ

หากต้องการย้ายรหัสผ่านที่แฮชด้วย PBKDF2 ให้ตั้งค่า passwordAlgorithm เป็น Legacy และจัดรูปแบบ passwordDigest ดังนี้:

["pbkdf2", ["salt", "1000", "20", "sha512", "@"], "expected_hashed_value"]

พารามิเตอร์คือ:

  • salt: ค่า salt ที่ใช้ในการแฮชเดิม
  • iterations: จำนวนรอบ (เช่น "1000")
  • keylen: ความยาวของคีย์ที่ได้ (เช่น "20")
  • digest: ฟังก์ชันแฮชที่ใช้ (เช่น "sha512", "sha256", "sha1")
  • @: ตัวแทนรหัสผ่านจริง
  • expected_hashed_value: ผลลัพธ์ hash ที่คาดหวังในรูปแบบ hexadecimal

ตัวอย่าง payload สำหรับย้าย:

{
"username": "john_doe",
"primaryEmail": "john.doe@example.com",
"passwordAlgorithm": "Legacy",
"passwordDigest": "[\"pbkdf2\", [\"mySalt123\", \"1000\", \"20\", \"sha512\", \"@\"], \"c465f66c6ac481a7a17e9ed5b4e2e7e7288d892f12bf1c95c140901e9a70436e\"]"
}

ขั้นตอนการย้าย (Steps to migrate)​

  1. เตรียมข้อมูลผู้ใช้ ควรส่งออกข้อมูลผู้ใช้จากแพลตฟอร์มเดิมของคุณก่อน แล้ว map ข้อมูลผู้ใช้ไปยังโครงสร้างข้อมูลผู้ใช้ของ Logto แนะนำให้เตรียมข้อมูลที่ map แล้วในรูปแบบ JSON ตัวอย่างข้อมูลผู้ใช้:

    [
    {
    "username": "user1",
    "passwordDigest": "password-encrypted",
    "passwordAlgorithm": "SHA256"
    },
    {
    "username": "user2",
    "passwordDigest": "password-encrypted",
    "passwordAlgorithm": "SHA256"
    }
    ]
  2. สร้าง tenant ใน Logto คุณต้องตั้งค่า tenant ใน Logto สามารถใช้ Logto Cloud หรือ Logto OSS ก็ได้ หากยังไม่ได้ตั้งค่า โปรดดูคู่มือ Set up Logto cloud

  3. ตั้งค่าการเชื่อมต่อ Management API เราจะใช้ Management API ในการนำเข้าข้อมูลผู้ใช้ ดูวิธีตั้งค่าการเชื่อมต่อในสภาพแวดล้อมของคุณได้ที่ Management API

  4. นำเข้าข้อมูลผู้ใช้ แนะนำให้เตรียมสคริปต์สำหรับนำเข้าข้อมูลผู้ใช้ทีละคน โดยจะเรียก API create user เพื่อ import ข้อมูลผู้ใช้ ตัวอย่างสคริปต์:

    const users = require('./users.json');

    const importUsers = async () => {
    for (const user of users) {
    try {
    await fetch('https://[tenant_id].logto.app/api/users', {
    method: 'POST',
    headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer [your-access-token]',
    },
    body: JSON.stringify(user),
    });
    // หน่วงเวลาเล็กน้อยเพื่อหลีกเลี่ยง rate limit
    await new Promise((resolve) => setTimeout(resolve, 200));
    } catch (error) {
    console.error(`Failed to import user ${user.username}: ${error.message}`);
    }
    }
    };

    importUsers();

โปรดทราบว่า endpoint ของ API มีการจำกัดอัตรา (rate limit) ควรเพิ่มการหน่วงเวลาระหว่างแต่ละคำขอเพื่อหลีกเลี่ยง rate limit โปรดตรวจสอบหน้า rate limits ของเราเพื่อดูรายละเอียด

หากคุณมีข้อมูลผู้ใช้จำนวนมาก (100,000+ users) สามารถ ติดต่อเรา เพื่อขอเพิ่ม rate limit ได้

แนวทางทั่วไปสำหรับการย้ายฐานข้อมูลผู้ใช้เดิมของคุณไปยัง Logto