ต่อระบบ
สิทธิ์ระดับรายการ (resource)
เจ้าของ ทีม การแชร์ต่อรายการ — Pi Auth เก็บและตรวจ แอปไม่ต้องทำหน้าทีม/แชร์เอง
สิทธิ์ระดับรายการ (เช่น โปรเจกต์ เอกสาร) — ใครเป็นเจ้าของ ทีมไหนเข้าได้ แชร์ให้ใคร — ให้ Pi Auth เป็นคนเก็บและตรวจ แอปไม่ต้องสร้างระบบทีม คำเชิญ และหน้าแชร์เอง
Pi Auth เก็บแค่ (องค์กร, แอป, ชนิด, externalId) เจ้าของ ทีม สมาชิก คำเชิญ และการแชร์ ไม่เก็บไฟล์หรือข้อมูลธุรกิจของแอป ต้องใช้ @suphark/auth-client ≥ 0.4.0
ประกาศ resource
ประกาศ resources ใน definePermissions() ครั้งเดียว (ดู Permission manifest) ระบุ action และสิ่งที่ owner, ทีม, การแชร์ และ permission ของแอปได้รับ
ใช้ในโค้ด
const projects = piAuth.resource(P.resources.project);
// สร้าง: ลงทะเบียน PENDING → สร้างแถวในฐานแอป → ACTIVE (fn ล้มเหลว SDK ยกเลิก PENDING ให้)
const project = await projects.create(id, { displayName: title }, () =>
prisma.project.create({ data: { id, tenantId, title } }),
);
await projects.require(projectId, "edit"); // 404 ถ้าไม่มีสิทธิ์
// รายการ: แอป query/sort เองเป็นช่วง แล้วกรองแบบชุด (คงลำดับ) จนได้ครบหน้า
const candidates = await prisma.project.findMany({
where: { tenantId, deletedAt: null }, orderBy: { updatedAt: "desc" }, take: 200,
});
const allowed = await projects.filterAllowed(candidates.map((p) => p.id), "read");
const actions = await projects.allowedActions(allowed, ["edit", "manage"]);
await projects.trash(projectId); // ย้ายลงถัง (สิทธิ์ purge) — restore() คืน
const manageUrl = await projects.manageUrl(projectId, `/projects/${projectId}`);- SDK ไม่ผูกกับ ORM: แอปเป็นเจ้าของ query/sort/page แล้วส่ง ID ไปตรวจครั้งละ 100 รายการ (พร้อมกันไม่เกิน 4 คำขอ) — ดึง candidate ทีละช่วงและหยุดเมื่อได้ครบหน้า ไม่ต้องตรวจทั้งองค์กร
- ติดต่อ Pi Auth ไม่ได้ → SDK ส่ง
AUTH_UNAVAILABLE(503) - คำสั่งเปลี่ยนสถานะ (create/activate/delete/trash/restore/pending) ต้องมี client secret ของแอปฝั่ง server เพิ่มจาก token ผู้ใช้
ทีม (หน้าที่ Pi Auth ทำให้)
เมนู "ทีม" ในแอปให้ redirect ไป await piAuth.resources.teamsUrl(returnPath) ลิงก์ไปทีมเดียวใช้ teamUrl(teamId, returnPath) — ไม่มี sidebar มีปุ่มกลับแอป สมาชิกที่เข้าแอปได้สร้างทีมได้ อีเมลของสมาชิก active เข้าทีมทันที อีเมลอื่นได้คำเชิญ 7 วัน การผูกทีมกับรายการทำในหน้าจัดการของรายการ (manageUrl) ป้ายทีมในรายการใช้ resource.teamsOf(ids) (คืน null เมื่อไม่มีทีมหรืออ่านรายการไม่ได้)
เปิดเป็น dialog ในแอป
ส่ง { embed: true } ให้ teamsUrl/teamUrl/manageUrl แล้วเปิดใน iframe หน้าจะมีปุ่ม "ปิด" ที่ส่ง postMessage({ type: "pi-auth:close" }) แอปรับเฉพาะ message จาก origin ของ issuer แล้วปิด dialog และ refresh ข้อมูล
| เงื่อนไข | รายละเอียด |
|---|---|
| origin ของแอป | ต้องอยู่ในรายการ embed origin ของ Pi Auth (ตอนนี้ https://*.suphark.com) |
| same-site | แอปต้อง same-site กับ Pi Auth เพราะ cookie เป็น SameSite=Lax |
| CSP | CSP ของแอปต้องมี frame-src <issuer> |
ควรมีลิงก์ "เปิดในแท็บใหม่" (URL เดียวกันไม่มี embed) เผื่อ iframe ใช้ไม่ได้
เลือกองค์กรใน dialog: piAuth.resources.selectTenantUrl({ embed: true, next }) เปิด /select-tenant ใน iframe — เลือกแล้วหน้าไม่ redirect ในเฟรม แต่ส่ง postMessage({ type: "pi-auth:select-tenant", tenant: slug }) ไปที่ origin ของ redirect URI ที่ลงทะเบียนเท่านั้น จากนั้นแอปเข้า /auth/login?tenant=<slug> ที่ระดับบนเอง
เจ้าของรายการ
Pi Auth เป็นแหล่งจริงของเจ้าของ (โอนได้จากหน้าจัดการของรายการ หรือผู้ดูแลองค์กรโอนจากเมนู "ทีมและเจ้าของ" ของแอปในคอนโซล) ค่าเจ้าของในฐานของแอปเป็นแค่สำเนา:
resource.describe(ids)→{ ownerId, team }ของแต่ละรายการresource.ownedIds()→ ID ทั้งหมดที่ผู้ใช้ปัจจุบันเป็นเจ้าของ (ใช้คิดโควตา)
วงจรชีวิตและการคืนสภาพ
PENDINGไม่ผ่านการตรวจสิทธิ์ ถ้าเปิดใช้ล้มเหลวหลังฐานแอปสร้างแถวแล้ว ให้เรียกprojects.reconcilePending(async (ids) => idsที่ยังมีในฐานแอป)— SDK เปิดใช้เฉพาะ ID ที่คืนมา- ถังขยะ:
trash()/restore()ด้วย token ผู้ใช้ — Pi Auth เปลี่ยนรายการที่อยู่ในถังเกิน 180 วันเป็นDELETEDเอง ระยะถังของแอปจึงต้องสั้นกว่า 180 วัน (cron ล้างถาวรของแอปไม่ต้องเรียก Pi Auth) - ลบถาวรทันทีใช้
remove(id, fn) - นำเข้าข้อมูลเดิม:
piAuth.resources.importExisting([{ type, externalId, ownerId, displayName?, shares? }])ครั้งละ ≤ 100 รายการ ใช้ token ผู้ดูแลองค์กรและ client secret เรียกซ้ำได้ไม่สร้างซ้ำ — owner ต้องเคยเป็นสมาชิก และผู้รับ share ต้องเป็นสมาชิก active