วิธีผสานรวม OAuth 2 เข้ากับแบ็คเอนด์ Django/DRF ของคุณโดยไม่ทำให้เสียสติ

เผยแพร่แล้ว: 2022-03-11

เราทุกคนเคยไปที่นั่น คุณกำลังทำงานกับส่วนหลังของ API และคุณพอใจกับสิ่งที่เกิดขึ้น คุณเพิ่งเสร็จสิ้นผลิตภัณฑ์ที่มีศักยภาพน้อยที่สุด (MVP) การทดสอบทั้งหมดผ่านไปแล้ว และคุณตั้งตารอที่จะนำคุณลักษณะใหม่มาใช้

จากนั้นเจ้านายก็ส่งอีเมลถึงคุณ: “ยังไงก็ตาม เราต้องให้ผู้คนเข้าสู่ระบบผ่าน Facebook และ Google; พวกเขาไม่ควรต้องสร้างบัญชีสำหรับไซต์เล็กๆ อย่างเรา”

ยอดเยี่ยม. ขอบเขตการคืบคลานโจมตีอีกครั้ง

ข่าวดีก็คือ OAuth 2 ได้กลายเป็นมาตรฐานอุตสาหกรรมสำหรับการตรวจสอบสิทธิ์ทางสังคมและบุคคลที่สาม (ใช้โดยบริการเช่น Facebook, Google ฯลฯ ) ดังนั้นคุณจึงสามารถมุ่งเน้นไปที่การทำความเข้าใจและนำมาตรฐานนั้นไปใช้เพื่อรองรับสังคมที่หลากหลาย ผู้ให้บริการรับรองความถูกต้อง

เป็นไปได้ว่าคุณไม่คุ้นเคยกับ OAuth 2; ฉันไม่ได้ เมื่อสิ่งนี้เกิดขึ้นกับฉัน

ผสานรวม OAuth 2 เข้ากับแบ็คเอนด์ Django/DRF ของคุณ

ในฐานะนักพัฒนา Python สัญชาตญาณของคุณอาจนำคุณไปสู่ ​​pip ซึ่งเป็นเครื่องมือแนะนำ Python Package Index (PyPA) สำหรับการติดตั้งแพ็คเกจ Python ข่าวร้ายคือ pip รู้เกี่ยวกับ 278 แพ็คเกจที่เกี่ยวข้องกับ OAuth - 53 รายการกล่าวถึง Django โดยเฉพาะ แค่ค้นคว้าตัวเลือกต่าง ๆ ก็คุ้มแล้วสำหรับการทำงานหนึ่งสัปดาห์ ไม่ต้องสนใจเลยที่จะเริ่มต้นเขียนโค้ด

ในบทช่วยสอนนี้ คุณจะได้เรียนรู้วิธีผสานรวม OAuth 2 เข้ากับ Django หรือ Django Rest Framework โดยใช้ Python Social Auth แม้ว่าบทความนี้จะเน้นที่ Django REST Framework แต่คุณสามารถใช้ข้อมูลที่ให้ไว้ที่นี่เพื่อนำไปใช้ในกรอบงานส่วนหลังทั่วไปอื่นๆ ที่หลากหลาย

ภาพรวมโดยย่อของโฟลว์ OAuth 2

OAuth 2 ได้รับการออกแบบมาตั้งแต่ต้นเป็นโปรโตคอลการตรวจสอบสิทธิ์เว็บ ซึ่งไม่เหมือนกับว่าได้รับการออกแบบให้เป็นโปรโตคอลการตรวจสอบสิทธิ์สุทธิ จะถือว่าเครื่องมือต่างๆ เช่น การเรนเดอร์ HTML และการเปลี่ยนเส้นทางของเบราว์เซอร์นั้นพร้อมให้คุณใช้งาน

เห็นได้ชัดว่านี่เป็นอุปสรรคสำหรับ API ที่ใช้ JSON แต่คุณสามารถแก้ไขปัญหานี้ได้

คุณจะผ่านกระบวนการนี้เสมือนว่าคุณกำลังเขียนเว็บไซต์ฝั่งเซิร์ฟเวอร์แบบดั้งเดิม

OAuth 2 Flow ฝั่งเซิร์ฟเวอร์

ขั้นตอนแรกเกิดขึ้นนอกโฟลว์แอปพลิเคชันทั้งหมด เจ้าของโปรเจ็กต์ต้องลงทะเบียนแอปพลิเคชันของคุณกับผู้ให้บริการ OAuth 2 แต่ละรายที่คุณต้องการเข้าสู่ระบบ

ในระหว่างการลงทะเบียนนี้ ผู้ให้บริการ OAuth 2 จะได้รับ URI การโทรกลับ ซึ่งแอปพลิเคชันของคุณจะพร้อมรับคำขอ เพื่อแลกเปลี่ยน พวกเขาจะได้รับ รหัสลูกค้า และ ข้อมูลลับลูกค้า โทเค็นเหล่านี้มีการแลกเปลี่ยนระหว่างกระบวนการตรวจสอบความถูกต้องเพื่อตรวจสอบคำขอเข้าสู่ระบบ

โทเค็นอ้างถึงรหัสเซิร์ฟเวอร์ของคุณในฐานะไคลเอนต์ โฮสต์คือผู้ให้บริการ OAuth 2 ไม่ได้มีไว้สำหรับไคลเอ็นต์ API ของคุณ

ขั้นตอนเริ่มต้นเมื่อแอปพลิเคชันของคุณสร้างเพจที่มีปุ่ม เช่น “เข้าสู่ระบบด้วย Facebook” หรือ “ลงชื่อเข้าใช้ด้วย Google+” โดยพื้นฐานแล้ว ลิงก์เหล่านี้ไม่ใช่ลิงก์ธรรมดา ซึ่งแต่ละลิงก์จะชี้ไปยัง URL ดังต่อไปนี้:

 https://oauth2provider.com/auth? response_type=code& client_id=CLIENT_KEY& redirect_uri=CALLBACK_URI& scope=profile& scope=email

(หมายเหตุ: แทรกตัวแบ่งบรรทัดใน URI ด้านบนเพื่อให้สามารถอ่านได้)

คุณได้ระบุรหัสลูกค้าและเปลี่ยนเส้นทาง URI แต่ไม่มีข้อมูลลับ ในการแลกเปลี่ยน คุณได้บอกเซิร์ฟเวอร์ว่าคุณต้องการรหัสการตรวจสอบสิทธิ์ในการตอบกลับและเข้าถึงขอบเขตทั้ง 'โปรไฟล์' และ 'อีเมล' ขอบเขตเหล่านี้กำหนดสิทธิ์ที่คุณร้องขอจากผู้ใช้ และจำกัดการให้สิทธิ์ของโทเค็นการเข้าถึงที่คุณได้รับ

เมื่อได้รับแล้ว เบราว์เซอร์ของผู้ใช้จะถูกส่งไปยังหน้าไดนามิกที่ผู้ให้บริการ OAuth 2 ควบคุม ผู้ให้บริการ OAuth 2 จะตรวจสอบว่า URI การโทรกลับและคีย์ไคลเอ็นต์ตรงกันก่อนที่จะดำเนินการต่อ หากเป็นเช่นนั้น โฟลว์จะแตกต่างกันชั่วครู่ขึ้นอยู่กับโทเค็นเซสชันของผู้ใช้

หากผู้ใช้ไม่ได้ลงชื่อเข้าใช้บริการนั้นอยู่ ระบบจะแจ้งให้ดำเนินการดังกล่าว เมื่อพวกเขาเข้าสู่ระบบแล้ว ผู้ใช้จะได้รับกล่องโต้ตอบเพื่อขออนุญาตเพื่อให้แอปพลิเคชันของคุณเข้าสู่ระบบได้

สมมติว่าผู้ใช้อนุมัติ เซิร์ฟเวอร์ OAuth 2 จะเปลี่ยนเส้นทางกลับไปยัง URI การโทรกลับที่คุณระบุ รวมถึง รหัสการให้สิทธิ์ ในพารามิเตอร์การค้นหา: GET https://api.yourapp.com/oauth2/callback/?code=AUTH_CODE

รหัสการให้สิทธิ์เป็นโทเค็นแบบใช้ครั้งเดียวที่หมดอายุอย่างรวดเร็ว ทันทีที่ได้รับเซิร์ฟเวอร์ของคุณควรหันหลังกลับและยื่นคำร้องต่อผู้ให้บริการ OAuth 2 อีกครั้ง ซึ่งรวมถึงรหัสการให้สิทธิ์และข้อมูลลับไคลเอ็นต์ของคุณ:

 POST https://oauth2provider.com/token/? grant_type=authorization_code& code=AUTH_CODE& redirect_uri=CALLBACK_URI& client_id=CLIENT_KEY& client_secret=CLIENT_SECRET

วัตถุประสงค์ของรหัสการให้สิทธิ์นี้คือการตรวจสอบสิทธิ์คำขอ POST ด้านบน แต่เนื่องจากลักษณะของโฟลว์ จึงจำเป็นต้องกำหนดเส้นทางผ่านระบบของผู้ใช้ ดังนั้นจึงไม่ปลอดภัยโดยเนื้อแท้

ข้อจำกัดเกี่ยวกับรหัสการให้สิทธิ์ (เช่น รหัสหมดอายุอย่างรวดเร็วและใช้ได้เพียงครั้งเดียว) มีไว้เพื่อลดความเสี่ยงในการส่งผ่านข้อมูลรับรองการพิสูจน์ตัวตนผ่านระบบที่ไม่น่าเชื่อถือ

การเรียกนี้ดำเนินการโดยตรงจากเซิร์ฟเวอร์ของคุณไปยังเซิร์ฟเวอร์ของผู้ให้บริการ OAuth 2 เป็นองค์ประกอบหลักของกระบวนการเข้าสู่ระบบฝั่งเซิร์ฟเวอร์ OAuth 2 การควบคุมการโทรหมายความว่าคุณรู้ว่าการโทรนั้นปลอดภัยด้วย TLS ซึ่งช่วยป้องกันการโจมตีจากการดักฟังโทรศัพท์

การรวมรหัสการให้สิทธิ์ช่วยให้แน่ใจว่าผู้ใช้ให้ความยินยอมอย่างชัดแจ้ง การรวมความลับของไคลเอ็นต์ซึ่งผู้ใช้ของคุณจะมองไม่เห็น ทำให้มั่นใจได้ว่าคำขอนี้ไม่ได้มาจากไวรัสหรือมัลแวร์บางตัวในระบบของผู้ใช้ ซึ่งสกัดกั้นรหัสการให้สิทธิ์

หากทุกอย่างตรงกัน เซิร์ฟเวอร์จะส่งคืน โทเค็นการเข้าถึง ซึ่งคุณสามารถโทรไปยังผู้ให้บริการนั้นในขณะที่รับรองความถูกต้องในฐานะผู้ใช้

เมื่อคุณได้รับโทเค็นการเข้าถึงจากเซิร์ฟเวอร์แล้ว เซิร์ฟเวอร์ของคุณจะเปลี่ยนเส้นทางเบราว์เซอร์ของผู้ใช้อีกครั้งไปยังหน้า Landing Page สำหรับผู้ใช้ที่เพิ่งเข้าสู่ระบบ เป็นเรื่องปกติที่จะเก็บโทเค็นการเข้าถึงไว้ในแคชของเซสชันฝั่งเซิร์ฟเวอร์ของผู้ใช้ ดังนั้น ที่เซิร์ฟเวอร์สามารถโทรไปยังผู้ให้บริการโซเชียลที่กำหนดเมื่อจำเป็น

ผู้ใช้ไม่ควรให้โทเค็นการเข้าถึงใช้งานได้!

มีรายละเอียดเพิ่มเติมที่เราสามารถดำดิ่งลงไปได้

ตัวอย่างเช่น Google มี โทเค็นการรีเฟรช ที่ยืดอายุของโทเค็นการเข้าถึงของคุณ ในขณะที่ Facebook ให้จุดสิ้นสุดซึ่งคุณสามารถแลกเปลี่ยนโทเค็นการเข้าถึงที่มีอายุสั้นสำหรับสิ่งที่มีอายุยืนยาวขึ้นได้ อย่างไรก็ตาม รายละเอียดเหล่านี้ไม่สำคัญสำหรับเรา เนื่องจากเราจะไม่ใช้โฟลว์นี้

โฟลว์นี้ยุ่งยากสำหรับ REST API แม้ว่าคุณจะให้ไคลเอ็นต์ front-end สร้างหน้าเข้าสู่ระบบเริ่มต้นและให้ back-end ระบุ URL เรียกกลับ แต่ในที่สุดคุณจะประสบปัญหา คุณต้องการเปลี่ยนเส้นทางผู้ใช้ไปยังหน้า Landing Page ของส่วนหน้าเมื่อคุณได้รับโทเค็นเพื่อการเข้าถึงแล้ว และไม่มีวิธีที่ชัดเจนและสงบในการดำเนินการดังกล่าว

โชคดีที่มีโฟลว์ OAuth 2 อื่นซึ่งใช้งานได้ดีกว่ามากในกรณีนี้

โฟลว์ OAuth 2 ฝั่งไคลเอ็นต์

ในโฟลว์นี้ ส่วนหน้าจะรับผิดชอบในการจัดการกระบวนการ OAuth 2 ทั้งหมด โดยทั่วไปแล้วจะคล้ายกับโฟลว์ฝั่งเซิร์ฟเวอร์ โดยมีข้อยกเว้นที่สำคัญคือ ส่วนหน้าใช้งานได้บนเครื่องที่ผู้ใช้ควบคุม ดังนั้นจึงไม่สามารถมอบความลับของไคลเอ็นต์ได้ วิธีแก้ไขคือเพียงแค่กำจัดขั้นตอนทั้งหมดนั้นออกไป

ขั้นตอนแรก เช่นเดียวกับในโฟลว์ฝั่งเซิร์ฟเวอร์ คือการลงทะเบียนแอปพลิเคชัน

ในกรณีนี้ เจ้าของโครงการยังคงลงทะเบียนแอปพลิเคชัน แต่เป็นเว็บแอปพลิเคชัน ผู้ให้บริการ OAuth 2 จะยังคงให้ รหัสไคลเอ็นต์ แต่ไม่สามารถระบุความลับของไคลเอ็นต์ได้

ส่วนหน้าให้ปุ่มเข้าสู่ระบบโซเชียลแก่ผู้ใช้ ซึ่งนำไปยังหน้าเว็บที่ผู้ให้บริการ OAuth 2 ควบคุม และขออนุญาตสำหรับแอปพลิเคชันของเราในการเข้าถึงบางแง่มุมของโปรไฟล์ผู้ใช้

URL ดูแตกต่างไปเล็กน้อยในครั้งนี้แม้ว่า:

 https://oauth2provider.com/auth? response_type=token& client_id=CLIENT_KEY& redirect_uri=CALLBACK_URI& scope=profile& scope=email

โปรดทราบว่าพารามิเตอร์ response_type ในครั้งนี้ใน URL เป็น token

แล้ว URI การเปลี่ยนเส้นทางล่ะ

นี่เป็นเพียงที่อยู่ใดๆ ที่ส่วนหน้าซึ่งเตรียมไว้เพื่อจัดการโทเค็นการเข้าถึงอย่างเหมาะสม

ขึ้นอยู่กับไลบรารี OAuth 2 ที่ใช้งาน ส่วนหน้าอาจเรียกใช้เซิร์ฟเวอร์ชั่วคราวที่สามารถรับคำขอ HTTP บนอุปกรณ์ของผู้ใช้ได้ชั่วคราว ในกรณีนั้น URL การเปลี่ยนเส้นทางจะอยู่ในรูปแบบ http://localhost:7862/callback/?token=TOKEN

เนื่องจากเซิร์ฟเวอร์ OAuth 2 ส่งคืนการเปลี่ยนเส้นทาง HTTP หลังจากที่ผู้ใช้ยอมรับ และเบราว์เซอร์ประมวลผลการเปลี่ยนเส้นทางนี้ในอุปกรณ์ของผู้ใช้ ที่อยู่นี้จึงถูกตีความอย่างถูกต้อง ทำให้สามารถเข้าถึงโทเค็นส่วนหน้าได้

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

จากจุดนี้ไป ส่วนหน้าสามารถเรียก API ของผู้ให้บริการ OAuth 2 ได้โดยตรงโดยใช้โทเค็น แต่ผู้ใช้ไม่ต้องการสิ่งนั้นจริงๆ พวกเขาต้องการเข้าถึง API ของคุณที่รับรองความถูกต้อง แบ็คเอนด์ทั้งหมดที่ต้องมีคือจุดสิ้นสุดที่ส่วนหน้าสามารถแลกเปลี่ยนโทเค็นการเข้าถึงของผู้ให้บริการโซเชียลเป็นโทเค็นที่อนุญาตให้เข้าถึง API ของคุณได้

เหตุใดจึงอนุญาตให้ทำเช่นนี้ เนื่องจากการจัดหาโทเค็นการเข้าถึงให้กับส่วนหน้ามีความปลอดภัยน้อยกว่าโฟลว์ฝั่งเซิร์ฟเวอร์โดยเนื้อแท้

โฟลว์ฝั่งไคลเอ็นต์อนุญาตให้แยกระหว่าง REST API แบ็คเอนด์และฟรอนต์เอนด์ที่ผู้ใช้เห็นได้ชัดเจนยิ่งขึ้น ไม่มีอะไรหยุดคุณจากการระบุเซิร์ฟเวอร์ส่วนหลังของคุณเป็น URI การเปลี่ยนเส้นทางอย่างเคร่งครัด ผลสุดท้ายจะเป็นแบบไฮบริดโฟลว์บางประเภท

ปัญหาคือเซิร์ฟเวอร์จะต้องสร้างหน้าสำหรับเผชิญหน้ากับผู้ใช้ที่เหมาะสม จากนั้นใช้มือควบคุมกลับไปที่ส่วนหน้าด้วยวิธีใดวิธีหนึ่ง

เป็นเรื่องปกติในโครงการสมัยใหม่ที่จะแยกข้อกังวลอย่างเคร่งครัดระหว่าง UI ส่วนหน้าและส่วนหลังที่จัดการตรรกะทางธุรกิจทั้งหมด โดยทั่วไปแล้วจะสื่อสารผ่าน JSON API ที่กำหนดไว้อย่างดี การไหลแบบไฮบริดที่อธิบายข้างต้นทำให้เกิดความขุ่นมัวซึ่งแยกข้อกังวลออกไป บังคับให้ส่วนหลังทั้งสองให้บริการหน้าที่ผู้ใช้เผชิญหน้า จากนั้นจึงออกแบบการไหลบางส่วนเพื่อควบคุมด้วยมือกลับไปที่ส่วนหน้า

การอนุญาตให้ front-end จัดการกับโทเค็นการเข้าถึงเป็นเทคนิคที่เหมาะสมที่ยังคงแยกข้อกังวล มันค่อนข้างเพิ่มความเสี่ยงจากลูกค้าที่ถูกบุกรุก แต่โดยทั่วไปก็ใช้งานได้ดี

โฟลว์นี้อาจดูซับซ้อนสำหรับฟรอนต์เอนด์ และถ้าคุณต้องการให้ทีมฟรอนต์เอนด์พัฒนาทุกอย่างด้วยตัวเอง อย่างไรก็ตาม ทั้ง Facebook และ Google มีห้องสมุดที่เปิดใช้งานส่วนหน้าเพื่อรวมปุ่มเข้าสู่ระบบที่จัดการกระบวนการทั้งหมดด้วยการกำหนดค่าขั้นต่ำ

นี่คือสูตรสำหรับการแลกเปลี่ยนโทเค็นที่ส่วนหลัง

ภายใต้โฟลว์ไคลเอ็นต์ แบ็กเอนด์ค่อนข้างแยกจากกระบวนการ OAuth 2 อย่าเข้าใจผิด: นี่ไม่ใช่งานง่ายๆ คุณจะต้องการรองรับฟังก์ชันการทำงานต่อไปนี้เป็นอย่างน้อย

  • ส่งคำขออย่างน้อยหนึ่งรายการไปยังผู้ให้บริการ OAuth 2 เพื่อให้แน่ใจว่าโทเค็นที่ส่วนหน้าระบุนั้นถูกต้อง ไม่ใช่สตริงสุ่มโดยอำเภอใจ
  • เมื่อโทเค็นถูกต้อง ให้ส่งคืนโทเค็นที่ถูกต้องสำหรับ API ของคุณ มิฉะนั้น ให้ส่งคืนข้อผิดพลาดของข้อมูล
  • หากเป็นผู้ใช้ใหม่ ให้สร้างโมเดล User สำหรับพวกเขา และเติมข้อมูลให้เหมาะสม
  • หากเป็นผู้ใช้ที่มีโมเดล User อยู่แล้ว ให้จับคู่พวกเขาตามที่อยู่อีเมล เพื่อให้เข้าถึงบัญชีที่มีอยู่ที่ถูกต้องแทนที่จะสร้างใหม่สำหรับการเข้าสู่ระบบโซเชียล
  • อัปเดตรายละเอียดโปรไฟล์ของผู้ใช้ตามสิ่งที่พวกเขาให้ไว้ในโซเชียลมีเดีย

ข่าวดีก็คือการนำฟังก์ชันทั้งหมดนี้ไปใช้ในส่วนแบ็คเอนด์นั้นง่ายกว่าที่คุณคาดไว้มาก

นี่คือความมหัศจรรย์ในการทำให้ทั้งหมดนี้ทำงานในส่วนแบ็คเอนด์ด้วยโค้ดเพียงสองโหล ขึ้นอยู่กับไลบรารี Python Social Auth (“PSA” ต่อจากนี้ไป) ดังนั้นคุณจะต้องรวมทั้ง social-auth-core และ social-auth-app-django ใน requirements.txt ของคุณ

คุณจะต้องกำหนดค่าไลบรารีตามเอกสารที่นี่ด้วย โปรดทราบว่าสิ่งนี้ไม่รวมการจัดการข้อยกเว้นเพื่อความชัดเจน

รหัสเต็มสำหรับตัวอย่างนี้สามารถพบได้ที่นี่

 @api_view(http_method_names=['POST']) @permission_classes([AllowAny]) @psa() def exchange_token(request, backend): serializer = SocialSerializer(data=request.data) if serializer.is_valid(raise_exception=True): # This is the key line of code: with the @psa() decorator above, # it engages the PSA machinery to perform whatever social authentication # steps are configured in your SOCIAL_AUTH_PIPELINE. At the end, it either # hands you a populated User model of whatever type you've configured in # your project, or None. user = request.backend.do_auth(serializer.validated_data['access_token']) if user: # if using some other token back-end than DRF's built-in TokenAuthentication, # you'll need to customize this to get an appropriate token object token, _ = Token.objects.get_or_create(user=user) return Response({'token': token.key}) else: return Response( {'errors': {'token': 'Invalid token'}}, status=status.HTTP_400_BAD_REQUEST, )

มีอะไรอีกเล็กน้อยที่ต้องไปในการตั้งค่าของคุณ (โค้ดแบบเต็ม) จากนั้นคุณก็พร้อมแล้ว:

 AUTHENTICATION_BACKENDS = ( 'social_core.backends.google.GoogleOAuth2', 'social_core.backends.facebook.FacebookOAuth2', 'django.contrib.auth.backends.ModelBackend', ) for key in ['GOOGLE_OAUTH2_KEY', 'GOOGLE_OAUTH2_SECRET', 'FACEBOOK_KEY', 'FACEBOOK_SECRET']: # Use exec instead of eval here because we're not just trying to evaluate a dynamic value here; # we're setting a module attribute whose name varies. exec("SOCIAL_AUTH_{key} = os.environ.get('{key}')".format(key=key)) SOCIAL_AUTH_PIPELINE = ( 'social_core.pipeline.social_auth.social_details', 'social_core.pipeline.social_auth.social_uid', 'social_core.pipeline.social_auth.auth_allowed', 'social_core.pipeline.social_auth.social_user', 'social_core.pipeline.user.get_username', 'social_core.pipeline.social_auth.associate_by_email', 'social_core.pipeline.user.create_user', 'social_core.pipeline.social_auth.associate_user', 'social_core.pipeline.social_auth.load_extra_data', 'social_core.pipeline.user.user_details', )

เพิ่มการแมปไปยังฟังก์ชันนี้ใน urls.py ของคุณและคุณก็พร้อมแล้ว!

เวทมนตร์นั้นทำงานอย่างไร?

Python Social Auth เป็นเครื่องจักรที่เจ๋งและซับซ้อนมาก ยินดีอย่างยิ่งที่จะจัดการการตรวจสอบสิทธิ์และการเข้าถึงผู้ให้บริการตรวจสอบสิทธิ์ทางสังคมหลายสิบราย และใช้งานได้กับเฟรมเวิร์กเว็บ Python ยอดนิยมส่วนใหญ่ เช่น Django, Flask, Pyramid, CherryPy และ WebPy

ส่วนใหญ่แล้ว โค้ดด้านบนนี้เป็นมุมมองตามฟังก์ชัน Django REST framework (DRF) ที่เป็นมาตรฐาน: จะรับฟังคำขอ POST บนเส้นทางใดก็ตามที่คุณแมปไว้ใน urls.py ของคุณและสมมติว่าคุณส่งคำขอใน รูปแบบที่คาดไว้ จากนั้นคุณจะได้รับวัตถุ User หรือ None

หากคุณได้รับอ็อบเจ็กต์ User จะเป็นประเภทโมเดลที่คุณได้กำหนดค่าไว้ที่อื่นในโครงการของคุณ ซึ่งอาจมีหรือไม่มีอยู่แล้ว PSA ได้ดูแลตรวจสอบโทเค็นแล้ว ระบุว่ามีการจับคู่ผู้ใช้หรือไม่ สร้างผู้ใช้หากจำเป็น และอัปเดตรายละเอียดผู้ใช้จากผู้ให้บริการโซเชียล

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

กุญแจสำคัญของเวทย์มนตร์คือ @psa() มัณฑนากรในมุมมอง ซึ่งเพิ่มสมาชิกบางส่วนไปยังอ็อบเจกต์ request ที่ส่งผ่านไปยังมุมมองของคุณ สิ่งที่น่าสนใจที่สุดสำหรับเราคือ request.backend (สำหรับ PSA แบ็กเอนด์คือผู้ให้บริการรับรองความถูกต้องทางสังคม)

เราเลือกแบ็คเอนด์ที่เหมาะสมและผนวกเข้ากับออบเจกต์ request ตามอาร์กิวเมนต์ backend ของมุมมอง ซึ่งได้รับการเติมโดย URL เอง

เมื่อคุณมีออบเจ็กต์ backend ในมือแล้ว ก็ยินดีอย่างยิ่งที่จะรับรองความถูกต้องของคุณกับผู้ให้บริการรายนั้น โดยระบุรหัสการเข้าถึงของคุณ นั่นคือวิธี do_auth ในทางกลับกัน การทำเช่นนี้จะทำให้ SOCIAL_AUTH_PIPELINE มีความสมบูรณ์จากไฟล์ปรับแต่งของคุณ

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

หลังจากนั้นก็กลับมาเป็นโค้ด DRF ปกติ: หากคุณมีอ็อบเจ็กต์ User ที่ถูกต้อง คุณสามารถส่งคืนโทเค็น API ที่เหมาะสมได้อย่างง่ายดาย หากคุณไม่ได้รับออบเจ็กต์ User ที่ถูกต้องกลับมา ทำให้เกิดข้อผิดพลาดได้ง่าย

ข้อเสียเปรียบประการหนึ่งของเทคนิคนี้คือ แม้ว่าการส่งคืนข้อผิดพลาดจะค่อนข้างง่าย หากเกิดขึ้น แต่ก็ยากที่จะได้รับข้อมูลเชิงลึกมากเกี่ยวกับสิ่งที่ผิดพลาดโดยเฉพาะ PSA กลืนรายละเอียดใด ๆ ที่เซิร์ฟเวอร์อาจส่งคืนเกี่ยวกับปัญหาที่เกิดขึ้น

ย้ำอีกครั้งว่าระบบการพิสูจน์ตัวตนที่ออกแบบมาอย่างดีมีความทึบแสงเกี่ยวกับแหล่งที่มาของข้อผิดพลาด หากแอปพลิเคชันบอกผู้ใช้ว่า "รหัสผ่านไม่ถูกต้อง" หลังจากพยายามเข้าสู่ระบบ เท่ากับเป็นการพูดว่า "ยินดีด้วย! คุณเดาชื่อผู้ใช้ที่ถูกต้องได้”

ทำไมไม่ม้วนของคุณเอง?

ในคำ: ความสามารถในการขยาย ผู้ให้บริการ OAuth 2 ทางสังคมน้อยมากที่ต้องการหรือส่งคืนข้อมูลเดียวกันทุกประการในการเรียก API ของตนในลักษณะเดียวกันทุกประการ มีกรณีพิเศษและข้อยกเว้นทุกประเภท

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

ฉันจะทดสอบสิ่งนี้ได้อย่างไร

คำถามที่ดี! unittest.mock ไม่เหมาะสำหรับการเยาะเย้ยการเรียก API ที่ฝังอยู่ใต้เลเยอร์นามธรรมที่อยู่ลึกเข้าไปในไลบรารี เพียงแค่ค้นพบเส้นทางที่แม่นยำในการเยาะเย้ยก็ต้องใช้ความพยายามอย่างมาก

แต่เนื่องจาก PSA ถูกสร้างขึ้นบนไลบรารี Requests คุณจึงใช้ไลบรารี Responses ที่ยอดเยี่ยมเพื่อเยาะเย้ยผู้ให้บริการที่ระดับ HTTP

การอภิปรายอย่างเต็มรูปแบบเกี่ยวกับการทดสอบอยู่นอกเหนือขอบเขตของบทความนี้ แต่รวมตัวอย่างการทดสอบของเราไว้ที่นี่ ฟังก์ชันเฉพาะที่ควรทราบมีตัวจัดการบริบทที่ mocked และคลาส SocialAuthTests

ให้ PSA ทำการยกของหนัก

กระบวนการ OAuth2 มีรายละเอียดและซับซ้อนโดยที่มีความซับซ้อนโดยธรรมชาติมากมาย โชคดีที่คุณสามารถหลีกเลี่ยงความซับซ้อนได้มากด้วยการนำไลบรารี่ที่ทุ่มเทเพื่อจัดการกับมันในแบบที่ไม่เจ็บปวดมากที่สุด

Python Social Auth ทำได้ดีมาก เราได้สาธิตมุมมอง Django/DRF ที่ใช้โฟลว์ OAuth2 ฝั่งไคลเอ็นต์ โดยนัย เพื่อสร้างผู้ใช้ที่ราบรื่นและจับคู่ในโค้ดเพียง 25 บรรทัดเท่านั้น ที่ไม่โทรมเกินไป