# คู่มือสำหรับ AI: ปรับหรือสร้างโปรแกรมให้เชื่อม Tapee One เวอร์ชันเอกสาร 2026-10-21 · อ้างอิง implementation ปัจจุบันของ `one.tapee.ac.th` คู่มือนี้ใช้กับ AI coding agent และผู้พัฒนาที่จะปรับโปรแกรมเดิมหรือสร้างโปรแกรมใหม่ของมหาวิทยาลัยตาปี ให้เข้าสู่ระบบและตรวจสิทธิ์ผ่าน Tapee One ใช้อ่านร่วมกับ source ของโปรแกรมปลายทางก่อนเริ่มแก้ไข ไม่ต้องส่งข้อมูลรับรองจริงให้ AI อ่านเอกสารนี้ ## 1. เริ่มต้นจากลิงก์เหล่านี้ - หน้าอ่านสำหรับผู้พัฒนา: https://one.tapee.ac.th/developers - คู่มือฉบับ Markdown: https://one.tapee.ac.th/developers/ai-guide.md - จุดค้นพบสำหรับ AI: https://one.tapee.ac.th/llms.txt - คู่มือฉบับเต็มสำหรับ AI: https://one.tapee.ac.th/llms-full.txt - สัญญาเชื่อมต่อแบบ JSON: https://one.tapee.ac.th/developers/contract.json - ตัวอย่าง backend PHP: https://one.tapee.ac.th/developers/sso-client.php.txt เอกสารเหล่านี้เป็นข้อมูลสาธารณะ ไม่มี Client Secret, Bot Token, ข้อมูลบุคลากร หรือค่าตั้งค่า production ปุ่ม Login และ API ที่ป้องกันยังต้องใช้การยืนยันตัวตนตามปกติ ## 2. Prompt ที่นำไปใช้กับ AI ในโปรเจกต์อื่นได้ คัดลอกข้อความด้านล่าง แล้วเติมชื่อโปรแกรมและเส้นทางโปรเจกต์: ```text ให้ปรับโปรแกรม [ชื่อโปรแกรม] ใน [เส้นทาง source] ให้ใช้ Tapee One SSO อ่าน https://one.tapee.ac.th/developers/ai-guide.md และ https://one.tapee.ac.th/developers/contract.json ก่อนเริ่ม ตรวจ stack, route, middleware, user model, role และการ deploy ของโปรเจกต์จริง เก็บ Client ID/Secret ฝั่ง server เท่านั้น ห้ามใส่ค่าลับใน prompt, Git หรือ frontend รองรับ callback start=1, browser-bound state, one-time code exchange และ introspection ก่อนทุก protected request โดย fail closed หากตรวจสิทธิ์ไม่ได้ ผูกบัญชีเดิมอัตโนมัติด้วย email ที่ยืนยันแล้วจาก introspection เมื่ออีเมลตรงเพียงบัญชีเดียว บันทึก issuer+sub จับคู่บัญชีถาวร รักษาข้อมูลและสิทธิ์เดิม ห้ามตั้งทุกคนเป็น Admin คง Login ตรงด้วยอีเมล/รหัสผ่านหรือ Google ของโปรแกรมเดิมไว้ควบคู่ One ตรวจ introspection เฉพาะเซสชันที่เข้าโดย One ไม่ fallback จาก SSO ที่ล้มเหลวเป็น local session เพิ่ม tests สำหรับการเข้าตรง การระงับ การหมดอายุ state ผิด และระบบกลางล่ม ส่งรายการไฟล์ที่แก้ วิธีตั้งค่า วิธีทดสอบ และ rollback plan ก่อน deploy ต้องสำรอง source ไป Git และตรวจ remote สำเร็จก่อน อย่าระบุว่าเชื่อมจริงสำเร็จจนทดสอบกับบัญชีและโปรแกรมที่ได้รับอนุญาตแล้ว ``` สำหรับโปรแกรมใหม่ ให้เพิ่มข้อกำหนด: สร้าง backend/BFF สำหรับ SSO, ใช้ session ของแอป, กำหนดสิทธิ์ภายในแอป, สร้างหน้าไม่มีสิทธิ์และระบบกลางไม่พร้อม พร้อมคู่มือติดตั้ง ค่าเริ่มต้นต้องไม่ให้สิทธิ์พิเศษ ## 3. สิ่งที่ระบบรองรับจริง Tapee One เป็นศูนย์กลางบุคลากร Google Workspace `@tapee.ac.th` และสิทธิ์เปิดใช้โปรแกรม ใช้ SSO แบบ confidential backend เฉพาะแพลตฟอร์ม: authorization code + token exchange + introspection - ยังไม่ใช่ OAuth 2.0 / OpenID Connect / SAML provider มาตรฐานครบชุด - ไม่มี discovery endpoint, JWKS, ID token, JWT signature verification, refresh token หรือ scopes แบบมาตรฐาน ให้ใช้ endpoint และ payload ในคู่มือนี้เท่านั้น - Token เป็น opaque string ไม่ใช่ JWT ห้าม decode แล้วเชื่อข้อมูลโดยไม่ introspect - โปรแกรมที่มีเฉพาะ static frontend / SPA ต้องเพิ่ม backend หรือ BFF ที่เก็บ Secret และ session ได้ก่อน ห้ามใส่ Secret ลง JavaScript หรือเรียก token/introspect จากเบราว์เซอร์ - ไม่ต้องคัดลอกรหัสผ่าน Google ไม่ต้องแชร์ Google OAuth Secret หรือ Telegram Bot Token ให้แต่ละโปรแกรม และไม่ต้องแชร์ session cookie ข้าม subdomain - การเพิ่มรายการโปรแกรมใน One อย่างเดียวไม่ทำให้โปรแกรมเดิมรองรับ SSO ต้องปรับ backend และทดสอบจริงด้วย - One เป็นช่องทางเพิ่มเติม ต้องคง Login ตรงด้วยอีเมล/รหัสผ่านและ Google เดิมตามที่โปรแกรมรองรับไว้ แม้บัญชีผูกกับ One แล้ว ไม่บังคับให้ทุกเซสชันผ่าน One ## 4. ข้อมูลที่ต้องเตรียมและลงทะเบียน ผู้พัฒนาตรวจ repository ของโปรแกรมปลายทาง แล้วกำหนดชื่อโปรแกรม stack backend URL production และ callback route เช่น `https://your-app.tapee.ac.th/auth/tapee-one/callback` ซึ่งเป็นตัวอย่าง ไม่ใช่ URL โปรแกรมที่ลงทะเบียนแล้ว Admin ของ One เปิดเมนู **จัดการโปรแกรม** ที่ `https://one.tapee.ac.th/admin/programs` เพิ่มโปรแกรม ระบุ HTTPS callback ของ backend เปิดใช้งาน และเลือกเปิดรับคำขอตามนโยบาย ระบบสร้าง Client ID และ Client Secret ซึ่งแสดงครั้งเดียว เก็บผ่านช่องทางลับของ deployment เท่านั้น ตัวแปรของโปรแกรมปลายทาง (ตัวอย่าง ไม่มีค่ารับรองจริง): ```dotenv ONE_ISSUER=https://one.tapee.ac.th ONE_CLIENT_ID= ONE_CLIENT_SECRET= ``` - ตั้ง issuer จาก server config ที่เชื่อถือได้ ห้ามรับ host/endpoint จาก query ของผู้ใช้ - callback ใช้ URL ที่ Admin ลงทะเบียนไว้ ระบบกลางไม่รับ `redirect_uri` จาก request เพื่อเปลี่ยนปลายทาง ผู้ใช้ต้องไม่ควบคุม callback URL - callback route ควรไม่มี query string เดิม โดยเฉพาะชื่อ `start`, `code`, `state` เพื่อไม่ให้ชนกับพารามิเตอร์ SSO - กำหนดสิทธิ์โปรแกรมให้บัญชีทดสอบหรือกลุ่มที่ได้รับอนุญาตก่อนทดสอบ - Secret ของแต่ละโปรแกรมแยกกัน ไม่ใช้ Client ของอีกโปรแกรมแทน ไม่เชื่อมฐานข้อมูลของ One โดยตรง ## 5. ลำดับ Login และการตรวจสิทธิ์ 1. ผู้ใช้ Login ที่ `https://one.tapee.ac.th` ผ่าน Google แล้วเลือกโปรแกรมที่มีสิทธิ์ One ตรวจสิทธิ์ที่ `POST /programs/{id}/launch` แล้วตอบหน้า HTML ภายใน One (HTTP 200) เพื่อจบการส่งฟอร์ม โดยคง CSP `form-action self` ไว้ (route นี้ใช้ session/CSRF ของ One ไม่ใช่ API สำหรับปลายทาง) 2. หน้าเปิดโปรแกรมใช้ JavaScript ที่โฮสต์ใน One นำเบราว์เซอร์ไป callback ที่ลงทะเบียนด้วย top-level GET พร้อม `start=1` มีลิงก์ให้กดต่อได้หาก JavaScript ปิดอยู่ โปรแกรมปลายทางสร้าง cryptographically random state อย่างน้อย 32 ตัวอักษร เก็บพร้อมเวลาหมดอายุใน server session ของเบราว์เซอร์นั้น 3. ปลายทาง redirect ไป `GET https://one.tapee.ac.th/sso/authorize?client_id=...&state=...` 4. One ตรวจบัญชีและสิทธิ์ปัจจุบัน แล้ว redirect กลับ callback เดิมพร้อม `code` และ `state` 5. ปลายทางตรวจ state แบบ timing-safe ให้ตรงกับ session และยังไม่หมดอายุ แล้วลบทิ้ง ใช้ code จาก callback แลก token ด้วย backend POST เพียงครั้งเดียว 6. หลังแลกสำเร็จ ให้ introspect token เพื่อตรวจตัวตน/สิทธิ์ก่อนสร้าง local authenticated session จากนั้น regenerate session ID เก็บ token ฝั่ง server แล้ว redirect ไปหน้า URL สะอาด ไม่มี code/state 7. ทุกคำขอที่ป้องกันของเซสชันที่ Login ผ่าน One รวมหน้าเว็บ API export download งานเปลี่ยนข้อมูล และการเข้าจากลิงก์ตรง ต้องผ่าน server middleware ที่ introspect token และยอมรับเฉพาะ boolean `active === true` เซสชันที่ Login ตรงใช้การตรวจบัญชี บทบาท และสิทธิ์เดิมของโปรแกรม 8. ถ้า active=false ให้ล้าง local SSO session และแสดงว่าไม่มีสิทธิ์หรือหมดอายุ เมื่อระบบกลางหรือการตรวจ SSO ไม่พร้อมให้หยุด protected action และตอบ 503 ห้ามใช้สถานะอนุญาตเก่าต่อ สถานะ Login อยู่ใน session ของแต่ละระบบ ปัจจุบันถ้ายังไม่ Login ที่ One การเข้าหน้า authorize จะถูกส่งไปหน้า Login และ Google Login กลับหน้าหลัก One จึงควร Login ที่ One แล้วกดเลือกโปรแกรมใหม่ อย่าสัญญาว่าการเริ่มจากลิงก์ปลายทางที่ยังไม่มี One session จะกลับเข้าโปรแกรมอัตโนมัติครบทุกกรณี State ต้องไม่คงที่ ไม่เก็บแค่ใน cookie ที่แก้เองได้ หรือรับจากผู้ใช้โดยไม่ผูก session ตัวอย่าง PHP รองรับหนึ่ง login ที่รอดำเนินการต่อ session หากต้องรองรับหลายแท็บพร้อมกัน ให้เก็บ state หลายรายการแบบจำกัดจำนวน/อายุและ consume เฉพาะรายการนั้น ## 6. สัญญา API ที่ต้องใช้ตรงตามนี้ ### 6.1 Authorize ผ่านเบราว์เซอร์ `GET https://one.tapee.ac.th/sso/authorize` | พารามิเตอร์ | กติกา | | --- | --- | | client_id | string ไม่เกิน 100 ตัวอักษร จากการลงทะเบียน | | state | 32–128 ตัวอักษร อนุญาต A–Z, a–z, 0–9, underscore, hyphen | ต้องมี session ของ One ที่ active และสิทธิ์ใช้โปรแกรม ถ้าสำเร็จตอบ redirect ไป callback ที่ลงทะเบียนพร้อม `code` (64 ตัวอักษร อายุ 60 วินาที) และ `state` เดิม ไม่มีการส่ง token ใน authorize response ### 6.2 แลก Code ด้วย backend `POST https://one.tapee.ac.th/api/sso/token` ส่ง `Accept: application/json` และ body แบบ `application/x-www-form-urlencoded` หรือ JSON โดยมี `client_id`, `client_secret`, `code` ห้ามส่ง Secret ใน URL ตัวอย่าง body แบบ JSON ที่ใช้ placeholder: ```json { "client_id": "", "client_secret": "", "code": "" } ``` สำเร็จ HTTP 200: ```json { "access_token": "", "token_type": "Bearer", "expires_in": 3600 } ``` Token อายุ 1 ชั่วโมงนับจากเวลาออก Token ส่วน code แลกได้ครั้งเดียวและเฉพาะ Client ที่ออกให้ หากคำขอแลก code timeout และไม่ทราบผล ให้เริ่ม Login ด้วย state/code ใหม่ ไม่ retry code เดิมวนซ้ำ ตัว API นี้ไม่มี `grant_type`, `code_verifier`, refresh token หรือ standard OAuth error schema ### 6.3 ตรวจ Token และสิทธิ์ด้วย backend `POST https://one.tapee.ac.th/api/sso/introspect` ส่ง `client_id`, `client_secret`, `token` ใน body พร้อม `Accept: application/json` ใช้ค่า token จาก server session ไม่รับ token จาก query ของผู้ใช้ เส้นทางนี้ใช้ body parameters ไม่ได้ยืนยัน Client ด้วยเพียง Authorization: Bearer สำเร็จและยังมีสิทธิ์ HTTP 200: ```json { "active": true, "sub": "123", "name": "Example Staff", "email": "example@tapee.ac.th", "email_verified": true, "program_id": 7, "exp": 1800000000 } ``` ค่าด้านบนเป็นตัวอย่าง `sub` เป็น string รหัสบุคลากรภายใน One, `program_id` เป็นรหัสโปรแกรมของ Client นี้, `exp` เป็น Unix timestamp จริงของ token ห้าม hardcode ค่าตัวอย่างในแอป Token ไม่พบ หมดอายุ ถูกยกเลิก ถูกใช้กับ Client อื่น หรือไม่มีสิทธิ์ปัจจุบัน ตอบ HTTP 200: ```json {"active": false} ``` ### 6.4 ข้อผิดพลาดและขีดจำกัด | กรณี | ผลปัจจุบัน | การจัดการของปลายทาง | | --- | --- | --- | | Client ผิด/Secret ผิด/โปรแกรมปิด | 401 ใน token/introspect | ปฏิเสธ ตรวจ config ฝั่ง server ไม่ให้ user กรอก Secret | | Code ผิด หมดอายุ ใช้ซ้ำ หรือสิทธิ์ไม่อนุญาต | 403 ตอน token exchange | เริ่ม Login ใหม่หลังตรวจสิทธิ์ ห้าม retry code เดิม | | authorize ไม่อนุญาต | 403 | แสดงทางกลับ One เพื่อขอสิทธิ์ | | Payload ไม่ถูกต้อง | 422 | แก้ implementation ไม่แสดง response ที่อาจมีรายละเอียดภายในต่อผู้ใช้ | | เกิน throttle | 429 | หยุด action แสดงให้ลองใหม่ เคารพ Retry-After ถ้ามี | | TLS/timeout/5xx/JSON ไม่ถูกต้อง/active ไม่ใช่ boolean true | ไม่สามารถยืนยัน | fail closed; 503 สำหรับ dependency failure, ไม่ใช้สิทธิ์เก่า | Route throttle ปัจจุบัน: authorize 60/min, token 120/min, introspect 600/min ตาม key ของ Laravel middleware ไม่ได้เป็นโควตาที่รับรองแยกให้แต่ละผู้ใช้หรือ Client โปรแกรมหลายตัวหลัง IP เดียวกันอาจใช้ key ร่วมกัน ให้ประเมินโหลดก่อนเปิดใช้จำนวนมาก ห้ามแก้ด้วยการ cache ผลอนุญาตข้าม protected requests จนสิทธิ์ระงับไม่มีผล ## 7. จับคู่บัญชีและสิทธิ์ภายในโปรแกรม ใช้คู่ `(issuer, sub)` เป็น external identity ที่มี unique constraint เช่น `external_identities(provider, subject, user_id)` โดย provider เป็น issuer ที่กำหนดไว้ ไม่ใช้ชื่อหรือ email เป็น primary key ของผู้ใช้จาก One และไม่ถือว่า sub คือ Google subject สำหรับโปรแกรมเดิม: 1. สำรวจ users เดิม ระบบ Login บทบาท tenant/คณะ เจ้าของข้อมูล background jobs และ protected routes ก่อนแก้ 2. เพิ่มตาราง identity mapping ด้วย migration ที่รักษาข้อมูลเดิม ไม่ล้าง users และไม่เปลี่ยน primary key เดิม 3. ผูกบัญชีเก่าอัตโนมัติในการเข้าโปรแกรมครั้งแรก เมื่อ `email_verified === true` จาก introspection ที่ backend ตรวจแล้ว และ email ที่ trim/แปลงเป็นตัวพิมพ์เล็กตรงกับบัญชี active เดิมเพียงบัญชีเดียว ห้ามเชื่อ email ที่ browser ส่งมา หาก email ซ้ำ กำกวม หรือ local account ผูกกับ sub อื่นอยู่แล้ว ให้ปฏิเสธและส่งให้ Admin ตรวจ ห้าม merge หรือย้าย mapping อัตโนมัติ 4. บัญชีใหม่จาก One เริ่มด้วยสิทธิ์พื้นฐานหรือรออนุมัติตามนโยบายแอป ไม่เลื่อนเป็น Admin/เจ้าของข้อมูลจาก email domain หรือจากการ Login สำเร็จ 5. ตรวจข้อมูลใน introspection: active เป็น true จริง, sub เป็น string ไม่ว่าง, email/name มีรูปแบบตาม schema ที่แอปรองรับ, exp ยังไม่หมด และถ้าตั้ง expected program_id ไว้ต้องตรง ห้ามเชื่อ identity จาก browser/query 6. ยังคงตรวจ role, ownership, tenant boundary และ CSRF ภายในโปรแกรมทุกครั้งหลังผ่านสิทธิ์ One ### ขั้นตอนผูกอัตโนมัติที่ทุกโปรแกรมต้องทำ 1. ตรวจ state แลก code ที่ backend และ introspect token ต้อง `active === true` และ sub/email/exp/program_id ถูกต้องตามสัญญา สำหรับการผูกครั้งแรกต้อง `email_verified === true` 2. ค้นหา `(issuer, sub)` ที่เคยผูกก่อนเสมอ หากพบให้ใช้ user_id เดิมและตรวจบัญชียัง active แม้ email เปลี่ยน ไม่ค้นหา email ใหม่เพื่อย้ายบัญชี 3. หากยังไม่ผูก เปิด transaction ค้นหาและ lock บัญชี local ด้วย email ที่ trim/lowercase ต้องพบ active account เพียงบัญชีเดียว ไม่ใช้ชื่อหรือ domain เพียงอย่างเดียว 4. ตรวจ mapping ทั้งด้าน subject และ local user อีกครั้งภายใต้ lock พร้อม unique constraints ที่ `(issuer, subject)` และ `(issuer, user_id)` หากขัดแย้งให้ปฏิเสธ ไม่ทับข้อมูลเดิม 5. สร้าง mapping ไป user_id เดิม บันทึก audit การผูกจาก email match โดยไม่บันทึก code/token/secret ไม่เปลี่ยนรหัสผ่าน local role กลุ่ม เจ้าของข้อมูล หรือสิทธิ์เดิม 6. regenerate session แล้วเข้าสู่โปรแกรมด้วยบัญชีเดิม กรณี email ตรงชัดเจนไม่ต้องให้ Admin ผูกด้วยมือ 7. ไม่พบ email ให้แสดงว่าบัญชียังไม่พร้อมเชื่อมและให้ Admin ตรวจ ไม่สร้างบัญชีหรือกำหนด Admin อัตโนมัติ แต่ละโปรแกรมอาจมีขั้นตอนสร้างบัญชีพื้นฐานแยกตามนโยบายของตน One ส่ง `email_verified` เป็น boolean ตามสถานะยืนยันอีเมล เมื่อ Admin เปลี่ยนอีเมลจะล้างสถานะยืนยันและยกเลิก code/token เดิม บุคลากรต้องยืนยันผ่าน Google Login อีกครั้งก่อนใช้ email นั้นผูกบัญชีใหม่ การผูกเป็นงานของ backend โปรแกรมปลายทาง One ไม่สามารถเขียนตาราง users ของโปรแกรมอื่นได้เพียงเพิ่มรายการโปรแกรม ### คง Login ตรงด้วยอีเมลควบคู่ One - แสดงทั้งปุ่ม One และช่องอีเมล/รหัสผ่านเดิม รวมถึง Google โดยตรงถ้าโปรแกรมตั้งค่าไว้ บุคลากรที่ผูก One แล้วต้องยังใช้บัญชีเดิม Login ตรงได้ - บันทึกวิธีการยืนยันตัวตนใน server-side session หลังตรวจข้อมูลรับรองสำเร็จเท่านั้น ไม่รับ auth source หรือ user_id จาก browser เพื่อข้าม One - เซสชัน One ต้อง introspect ทุก protected request และตรวจสิทธิ์ภายในโปรแกรมตามเดิม เมื่อ One ระงับ/หมดอายุ/ติดต่อไม่ได้ ให้ปฏิเสธเซสชันนั้น ห้ามเปลี่ยนเป็น local session อัตโนมัติ - เซสชัน Login ตรงต้องตรวจ password หรือ Google identity จริง พร้อม local active/role/scope/auth_version ตามระบบเดิม ไม่ต้องมี token ของ One การผูก mapping ไม่ใช่เหตุให้ปิดช่องทางนี้ - ผู้ใช้ต้อง Login ตรงใหม่อย่างชัดเจนเมื่อต้องการใช้ช่องทางตรง การเริ่ม One ต้องล้างเครื่องหมาย local authentication เดิม และการ Login ตรงสำเร็จต้องล้าง One token เดิม พร้อม regenerate session และ CSRF token ตามระบบ - การเพิกถอนสิทธิ์ใน One มีผลกับเซสชัน One เท่านั้น หากต้องปิดการเข้าใช้ทั้งสองช่องทาง ให้ระงับบัญชีที่โปรแกรมปลายทางด้วย การเชื่อม One ไม่เปลี่ยนสิทธิ์ local เดิมโดยอัตโนมัติ ```text identity = introspect(server_session_token) require identity.active === true mapping = find(configured_issuer, identity.sub) if mapping exists: return require_active_local_user(mapping.user_id) require identity.email_verified === true transaction: user = exactly_one_active_local_user_for_update(lowercase(trim(identity.email))) recheck both mapping unique keys; reject conflicts insert mapping(configured_issuer, identity.sub, user.id) audit auto_link_by_email without tokens return user // existing roles and ownership unchanged ``` สิทธิ์ One ตัดสินว่าเปิดใช้โปรแกรมได้หรือไม่ ไม่ได้ให้สิทธิ์แก้ข้อมูลทุกประเภทในโปรแกรม introspection ปัจจุบันไม่ส่ง is_admin หรือรายชื่อคณะ/กลุ่ม ห้ามอนุมานว่าผู้ดูแล One เป็นผู้ดูแลปลายทาง และห้ามอ่านตารางฐานข้อมูลกลางเพื่อหลีกเลี่ยงข้อจำกัดนี้ ## 8. แนวทางแยกตาม Stack | Stack ปลายทาง | ส่วนที่ต้องเพิ่ม | | --- | --- | | PHP / Laravel | callback route, server HTTP client, OneAccess middleware, mapping user, server session และ tests | | Node.js / Express / NestJS | backend callback, server session store, HTTP client, guard ก่อน handlers และ CSRF ของ local mutations | | Next.js / Nuxt | server routes/BFF สำหรับ callback และ introspection, session ฝั่ง server; ตรวจสิทธิ์ใน server actions/API ด้วย ไม่พึ่ง client component | | Python / Django / FastAPI | callback view, session/auth adapter, dependency/middleware ตรวจ One และ local roles | | .NET / Java / Go | server controller/handler, session store, authorization filter และ external identity mapping | | SPA / static site | เพิ่ม backend/BFF ก่อน ไม่มีวิธีเก็บ Client Secret อย่างปลอดภัยใน frontend-only app ของสัญญารุ่นนี้ | ใช้ HTTP client ของ stack นั้นโดยเปิด TLS certificate/hostname verification จำกัด timeout และไม่ follow redirect ของ server-to-server requests ห้ามติดตั้ง generic OIDC adapter โดยชี้มาที่ One แล้วคาดว่าจะทำงานตามมาตรฐานที่ยังไม่มี โครงสร้าง pseudocode ที่ต้องได้: ```text GET registered_callback?start=1: create random state + expires_at bound to local browser session redirect to fixed One authorize endpoint GET registered_callback?code=...&state=...: validate and consume browser session state exchange code via backend using this app's client credentials introspect returned token; require active === true and valid identity map issuer+sub to local user without expanding local permissions regenerate session id; store token only on server redirect to a fixed clean local URL EVERY protected request: if no local One token: deny or offer restart from One introspect token via backend if unavailable: stop action with 503 if inactive: clear SSO login and deny validate local role + object/tenant ownership + CSRF where relevant only then execute the request ``` ## 9. ตัวอย่าง PHP และการติดตั้ง Route ดาวน์โหลด https://one.tapee.ac.th/developers/sso-client.php.txt เก็บเป็นไฟล์ PHP ฝั่ง backend ของแอป ไม่วางเป็นไฟล์ดาวน์โหลดที่มีค่าจริง ตัวอย่างต้องใช้ PHP 8.1+ และ cURL สำหรับฟังก์ชันในไฟล์นี้ สำหรับ Laravel ให้ปรับเป็น service/middleware และใช้ session ของ framework ไม่เรียก session_start ซ้ำ Route ตัวอย่างที่ลงทะเบียนเป็น callback: ```php