เผยแพร่แพ็กเกจ npm ด้วย OIDC: เลิกเก็บโทเค็นเขียนถาวรใน CI

|ผู้เขียน: กองบรรณาธิการ QUASA|4 นาทีในการอ่าน| 1
เผยแพร่แพ็กเกจ npm ด้วย OIDC: เลิกเก็บโทเค็นเขียนถาวรใน CI

การย้ายงานเผยแพร่แพ็กเกจ npm บน GitHub Actions จาก NPM_TOKEN ไปใช้ Trusted Publishing ต้องผูกแพ็กเกจกับ repository และไฟล์ workflow บน npmjs.com แล้วให้ workflow ขอสิทธิ์ OIDC เพื่อเผยแพร่โดยไม่ต้องเก็บโทเค็นเขียนถาวรใน CI คู่มือ Trusted Publishing ของ npm กำหนดให้ใช้ npm CLI ตั้งแต่รุ่น 11.5.1 และ Node.js ตั้งแต่รุ่น 22.14.0 โดยข้อมูลรับรองสำหรับการเผยแพร่มีอายุสั้นและผูกกับ workflow ที่ได้รับอนุญาต

เก็บโทเค็นเดิมไว้ระหว่างย้ายระบบ แต่ไม่ส่งให้ขั้นตอนเผยแพร่ที่ใช้ทดสอบ OIDC เพิกถอนโทเค็นหลัง workflow ใหม่เผยแพร่เวอร์ชันหนึ่งสำเร็จและพบเวอร์ชันนั้นใน registry แล้วเท่านั้น หากชื่อ repository ไฟล์ workflow หรือ environment ไม่ตรงกับที่ผูกไว้ การเผยแพร่อาจล้มเหลวแม้แพ็กเกจมีอยู่จริง

ผูกแพ็กเกจและตรวจข้อมูลก่อนแก้ CI

ชื่อ repository และไฟล์ publish.yml ของ GitHub Actions ตรงกับค่าที่ผูกใน npm Trusted Publisher

เปิดแพ็กเกจบน npmjs.com แล้วไปที่ Settings → Trusted publishing เลือก GitHub Actions กรอกชื่อเจ้าของ repository และชื่อ repository จากนั้นกรอกชื่อไฟล์ใน .github/workflows/ เช่น publish.yml โดยใส่เฉพาะชื่อไฟล์พร้อมนามสกุล ไม่ใส่ทั้งพาธ หาก job เผยแพร่ใช้ GitHub environment ให้กรอกชื่อ environment เดียวกัน; หากไม่ได้ใช้ ให้เว้นช่องนี้ว่าง

เลือกสิทธิ์ npm publish ให้การเชื่อมต่อนี้ด้วย เพราะคำสั่งใน workflow ด้านล่างเผยแพร่โดยตรง สิทธิ์ npm stage publish เป็นอีกทางเลือกหนึ่งและไม่ได้อนุญาตให้ใช้ npm publish โดยอัตโนมัติ เมื่อบันทึกแล้ว ค่าตัวตนหลักของการเชื่อมต่อแก้ไขไม่ได้ หากกรอกผิดต้องลบรายการนั้นและสร้างใหม่ จึงควรเทียบชื่อเจ้าของ repository และไฟล์กับงานที่จะรันจริงก่อนบันทึก

ในรากแพ็กเกจ รัน npm pkg get name version repository.url private เพื่อตรวจชื่อและเวอร์ชันที่จะเผยแพร่ ค่า repository.url ต้องชี้ไปยัง GitHub repository เดียวกับที่ผูกไว้บน npm หากเป็น monorepo ให้รันคำสั่งในไดเรกทอรีของแพ็กเกจที่จะเผยแพร่ ไม่ใช่อ่าน package.json ที่รากโครงการโดยอัตโนมัติ ค่า private: true ทำให้ npm ปฏิเสธการเผยแพร่ จึงต้องแยกให้ชัดจากกรณีที่ซอร์สอยู่ใน repository ส่วนตัวแต่แพ็กเกจยังเผยแพร่ได้

เลือกเวอร์ชันใหม่ที่ยังไม่เคยใช้กับชื่อแพ็กเกจนั้น และรัน npm pack --dry-run เพื่อดูไฟล์ที่จะอยู่ในแพ็กเกจ โดยเฉพาะไฟล์ที่สร้างระหว่าง build การตรวจนี้ช่วยจับไฟล์ที่ขาดหรือไฟล์ที่ไม่ควรส่งขึ้น registry แต่ยังไม่ทดสอบสิทธิ์ OIDC หาก package.json มี publishConfig หรือโครงการมี .npmrc ให้ตรวจด้วยว่า registry สำหรับการเผยแพร่ยังเป็น npm registry ที่ตั้งใจใช้

ประกอบ workflow สำหรับเผยแพร่

บันทึก workflow ชื่อ .github/workflows/publish.yml รายการต่อไปนี้เรียงตามบรรทัดของไฟล์ โดยระดับรายการย่อยแทนระดับการเยื้องของ YAML ให้คัดลอกเฉพาะข้อความตัวหนาและเยื้องตามระดับรายการ แนวทางของ actions/setup-node ใช้ action รุ่น v7 กับ Node.js 24 แนะนำให้ปิดแคชในงานเผยแพร่ และเตือนว่าตัวตน workflow ที่ไม่ตรงกับการตั้งค่าบน npm อาจทำให้เกิด E404

  • name: Publish Package ตั้งชื่อที่แสดงใน GitHub Actions ซึ่งเป็นคนละค่ากับชื่อไฟล์ที่กรอกบน npm
  • on: เริ่มเงื่อนไขเรียกงาน
    • push: รับเหตุการณ์ push
      • tags: จำกัดให้ทำงานเมื่อ push แท็ก
        • - 'v*' รับแท็กที่ขึ้นต้นด้วย v
  • permissions: กำหนดสิทธิ์ของ workflow
    • contents: read ให้ checkout อ่าน repository ได้
    • id-token: write ให้ job ขอ OIDC ID token ได้
  • jobs: เริ่มรายการงาน
    • publish: ตั้งชื่องานเผยแพร่
      • runs-on: ubuntu-latest ใช้ runner ที่ GitHub โฮสต์
      • steps: เริ่มขั้นตอนตามลำดับ
        • - uses: actions/checkout@v7 ดึงซอร์สของแท็กที่เรียกงาน
        • - uses: actions/setup-node@v7 ติดตั้ง Node.js
          • with: เริ่มค่าของ setup-node
            • node-version: '24' เลือก Node.js รุ่นที่มี npm CLI ซึ่งรองรับ OIDC
            • registry-url: 'https://registry.npmjs.org' ชี้ไปยัง npm registry
            • package-manager-cache: false ปิดแคชของงานเผยแพร่
        • - run: npm --version บันทึกรุ่น npm ใน log เพื่อเทียบกับรุ่นขั้นต่ำ
        • - run: npm ci ติดตั้งตาม lockfile ที่ commit ไว้
        • - run: npm run build --if-present สร้างไฟล์ผลลัพธ์หากมีสคริปต์ build
        • - run: npm test ให้การทดสอบผ่านก่อนเผยแพร่; ปรับคำสั่งตามโครงการ
        • - run: npm publish เผยแพร่ผ่านตัวตน OIDC ของงานนี้

ตัวอย่างถือว่าแพ็กเกจอยู่ที่ราก repository และใช้ npm registry หากแพ็กเกจอยู่ในไดเรกทอรีย่อย ให้กำหนด working-directory ให้ขั้นตอน npm ที่เกี่ยวข้องทั้งหมด ไม่เช่นนั้น npm ci อาจอ่าน lockfile คนละชุดกับ npm publish การแสดง npm --version ช่วยให้เห็นรุ่นที่รันจริง แทนการอนุมานจากรุ่น Node.js เพียงอย่างเดียว

อย่ากำหนด NPM_TOKEN หรือ NODE_AUTH_TOKEN ให้ขั้นตอน npm publish และตรวจด้วยว่าไม่ได้ส่งค่าดังกล่าวเข้ามาที่ระดับ job หรือ workflow มิฉะนั้นการเผยแพร่สำเร็จอาจเกิดจากโทเค็นเดิมแทน OIDC หากต้องติดตั้ง private dependency ให้ใช้โทเค็นแบบอ่านอย่างเดียวเป็น NODE_AUTH_TOKEN เฉพาะขั้นตอน npm ci แล้วไม่ส่งต่อไปยังขั้นตอนเผยแพร่

เผยแพร่จริงและไล่หาสาเหตุเมื่อ E404

การเผยแพร่ไปยัง npm พบ E404 เมื่อชื่อไฟล์ workflow ไม่ตรงกับการตั้งค่า Trusted Publisher

สร้างแท็กที่ตรงกับ v* จาก commit ซึ่งมีเวอร์ชันใหม่ แล้ว push แท็กเพื่อเรียก workflow ตรวจว่า npm --version แสดงรุ่นที่รองรับ ขั้นตอนทดสอบผ่าน และ npm publish สำเร็จ จากนั้นตรวจเวอร์ชันใหม่บนหน้าแพ็กเกจหรือใน npm registry ด้วย การผ่าน npm pack --dry-run หรือการที่ workflow เริ่มทำงานได้ ยังไม่ยืนยันว่า npm ยอมรับตัวตน OIDC

ถ้า npm publish ตอบ E404 ให้เทียบชื่อเจ้าของ repository ชื่อ repository และชื่อไฟล์ publish.yml กับรายการ Trusted Publisher ทีละค่า รวมตัวพิมพ์และนามสกุลไฟล์ ตรวจ environment ของ job และ repository.url ใน package.json ด้วย หากใช้ workflow_call เพื่อเรียก workflow อื่น ให้ตรวจชื่อ workflow ฝั่งที่เรียกและสิทธิ์ id-token: write ของทั้ง workflow ผู้เรียกและ workflow ที่ถูกเรียก การแก้เพียงชื่อไฟล์ซึ่งมีคำสั่ง npm publish อาจยังไม่แก้การจับคู่

สำหรับการเชื่อมต่อใหม่ ประกาศของ GitHub ระบุว่าต้องเผยแพร่สำเร็จครั้งแรกภายใน 48 ชั่วโมงหลังสร้างการเชื่อมต่อ มิฉะนั้นรายการที่ยังไม่ผ่านการยืนยันจะหมดอายุ หากพ้นช่วงนี้แล้ว ให้ลบรายการที่หมดอายุ สร้างการเชื่อมต่อใหม่ และเผยแพร่ภายในช่วงยืนยันใหม่ การเปลี่ยนตัวตนของ repository ก็ต้องสร้างความสัมพันธ์ Trusted Publisher ใหม่

หากพบ ENEEDAUTH ให้ตรวจสิทธิ์ id-token: write รุ่น npm และชนิด runner ก่อน แล้วดูว่ามีโทเค็นเดิมหรือการตั้งค่า .npmrc ที่แทรกเข้ามาหรือไม่ อย่าใช้ npm whoami เป็นตัวตัดสินว่า Trusted Publishing พร้อมใช้งาน เพราะคำสั่งนั้นไม่ได้ทดสอบสิทธิ์เผยแพร่ของ OIDC ตรวจสถานะเวอร์ชันใน registry ก่อนลองงานซ้ำเสมอ: หากเวอร์ชันเผยแพร่สำเร็จไปแล้ว ต้องใช้เวอร์ชันใหม่ในการเผยแพร่ครั้งถัดไป

ระหว่างแก้การจับคู่ ให้คงโทเค็นเขียนเดิมไว้ใน secret แต่แยกจากขั้นตอน OIDC หากจำเป็นต้องออกรีลีสก่อนแก้เสร็จ สามารถย้อน workflow ไปใช้ secret เดิมชั่วคราวภายใต้สิทธิ์เดิมที่ยังใช้ได้ แล้วกลับมาตรวจ Trusted Publisher กับเวอร์ชันใหม่ภายหลัง อย่าใส่ค่าโทเค็นลงในไฟล์ workflow หรือ log

ข้อจำกัดของ runner และแพ็กเกจ

ตัวตน OIDC แก้เฉพาะการยืนยันสิทธิ์ตอนเผยแพร่ เงื่อนไขของ runner และการติดตั้ง dependency ยังมีผลแยกกัน สำหรับงานที่ย้ายจาก GitHub Actions ให้ตรวจข้อจำกัดเหล่านี้ก่อนตัดโทเค็นเขียน

  • GitHub-hosted runner: รองรับ Trusted Publishing ตัวอย่างใช้ ubuntu-latest เพื่อให้การเผยแพร่รันบน runner ประเภทนี้
  • Self-hosted runner: ยังไม่รองรับการเผยแพร่ด้วยวิธีนี้ หากงาน build จำเป็นต้องใช้ runner ของตนเอง ให้ย้ายเฉพาะงานเผยแพร่ไป GitHub-hosted runner พร้อมส่งต่อผลลัพธ์ที่ตรวจสอบแล้วตามกระบวนการของโครงการ
  • Repository ส่วนตัว: ยังใช้ Trusted Publishing ได้ แต่ npm จะไม่สร้าง provenance จากซอร์สใน repository ส่วนตัว แม้แพ็กเกจที่เผยแพร่จะเป็นสาธารณะ
  • Private dependency: OIDC สำหรับ npm publish ไม่ได้ให้สิทธิ์ npm ci อ่านแพ็กเกจส่วนตัว ขั้นตอนติดตั้งยังต้องใช้ข้อมูลรับรองแบบอ่านอย่างเดียวที่จำกัดขอบเขต

เมื่อเผยแพร่จาก repository สาธารณะด้วย Trusted Publishing npm จะสร้าง provenance ให้โดยอัตโนมัติ จึงไม่ต้องเพิ่ม --provenance เพื่อเริ่มใช้วิธีนี้ การไม่เห็น provenance จาก repository ส่วนตัวไม่ได้แปลว่าการเผยแพร่ผ่าน OIDC ล้มเหลว ให้ตัดสินจากผลของ npm publish และเวอร์ชันที่ปรากฏใน registry

ตรวจให้ครบก่อนเพิกถอนโทเค็นเขียน

การเผยแพร่สำเร็จครั้งเดียวเป็นหลักฐานของเส้นทางที่รันครั้งนั้น ก่อนเพิกถอนโทเค็นเดิม ให้ตรวจงานอื่นที่อาจยังใช้โทเค็นเดียวกันด้วย โดยเฉพาะโครงการที่เผยแพร่หลายแพ็กเกจหรือมี workflow แยกสำหรับรีลีสตามปกติและรีลีสฉุกเฉิน

  • workflow ที่ผูกบน npm เป็นงานที่เผยแพร่จริง และ log แสดง npm CLI รุ่นที่รองรับ
  • ขั้นตอน npm publish สำเร็จโดยไม่มี NPM_TOKEN หรือ NODE_AUTH_TOKEN ที่มีสิทธิ์เขียนในสภาพแวดล้อมของขั้นตอนนั้น
  • ชื่อและเวอร์ชันใหม่ของแพ็กเกจปรากฏใน npm registry และไฟล์ที่เผยแพร่ตรงกับผลที่ตั้งใจส่ง
  • งานติดตั้ง private dependency ยังมีสิทธิ์อ่านที่จำเป็น โดยไม่ต้องใช้โทเค็นเขียนเดิม
  • workflow และแพ็กเกจอื่นที่เคยอ้างถึง secret เดียวกันมีเส้นทางเผยแพร่ใหม่หรือโทเค็นที่แยกกันแล้ว

เมื่อผ่านรายการนี้ ให้ตั้ง Publishing access ของแพ็กเกจเป็น Require two-factor authentication and disallow tokens หากรูปแบบการเผยแพร่ของแพ็กเกจรองรับนโยบายดังกล่าว จากนั้นเพิกถอน automation token ที่เลิกใช้จากบัญชี npm ลบ NPM_TOKEN จาก GitHub Actions secrets และลบการอ้างถึง secret นี้ใน workflow โทเค็นอ่านอย่างเดียวสำหรับ private dependency เก็บไว้ได้เมื่อยังจำเป็น แต่ต้องไม่มีสิทธิ์เผยแพร่แพ็กเกจ

แชร์:

สมัครรับจดหมายข่าวของเรา

รับข่าวสารล่าสุดเกี่ยวกับ Web3, AI และคริปโตส่งตรงถึงกล่องจดหมายของคุณ

0