วิธีสร้างปุ่ม SSO – บทแนะนำการเข้าสู่ระบบขวด

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

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

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

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

SSO มักจะขับเคลื่อนโดยโปรโตคอล OpenId Connect (OIDC) หรือ SAML SAML ส่วนใหญ่จะใช้ในแอปพลิเคชันระดับองค์กร OIDC สร้างขึ้นจาก OAuth2 และใช้โดยผู้ให้บริการข้อมูลประจำตัวทางสังคม เช่น Facebook, Google เป็นต้น ในโพสต์นี้ เราจะเน้นที่โปรโตคอล OIDC/OAuth2

ในบทช่วยสอนการเข้าสู่ระบบ Flask นี้ เราจะเขียนคำแนะนำทีละขั้นตอนเพื่อเพิ่มปุ่มเข้าสู่ระบบ SSO ลงในแอปพลิเคชัน Flask ที่มี SimpleLogin และ Facebook เป็นผู้ให้บริการข้อมูลประจำตัว สามารถทำได้โดยไม่ต้องใช้ไลบรารีภายนอกใดๆ แต่เพื่อลดความซับซ้อนของ OAuth เราจะใช้ Requests-OAuthlib ซึ่งเป็นไลบรารีเพื่อรวมผู้ให้บริการ OAuth หากคุณสนใจที่จะใช้ SSO ตั้งแต่เริ่มต้น โปรดดู Implement SSO Login – the raw way

ในตอนท้ายของบทความนี้ คุณควรมีแอพ Flask ที่มีหน้าต่อไปนี้:

  • หน้าแรกที่มีปุ่มเข้าสู่ระบบ
  • หน้าข้อมูลผู้ใช้ ซึ่งเมื่อเข้าสู่ระบบสำเร็จแล้ว ผู้ใช้จะสามารถดูข้อมูล เช่น ชื่อ อีเมล และรูปแทนตัว

รหัสทั้งหมดสำหรับบทช่วยสอนนี้มีอยู่ในที่เก็บ flask-social-login-example

นอกจากนี้ยังมีการสาธิตที่นี่ อย่าลังเลที่จะรีมิกซ์โค้ดบน Glitch

ขั้นตอนที่ 1: Bootstrap Flask App

ติดตั้ง flask และ Requests-OAuthlib คุณยังสามารถใช้ virtualenv หรือ pipenv เพื่อแยกสภาพแวดล้อม

pip ติดตั้งขวดร้องขอ_oauthlib

สร้าง app.py และเส้นทางที่แสดงปุ่มเข้าสู่ระบบในหน้าแรก:

 import flask app = flask.Flask(__name__) @app.route("/") def index(): return """ <a href="/login">Login</a> """ if __name__ == '__main__': app.run(debug=True)

มาเรียกใช้แอพนี้และตรวจสอบว่าทุกอย่างทำงานได้ดี:

 python app.py

คุณควรเห็นหน้านี้เมื่อเปิด http://localhost:5000 รหัสเต็มอยู่ที่ step1.py

เข้าสู่ระบบด้วย SimpleLogin

ขั้นตอนที่ 2: ข้อมูลประจำตัวผู้ให้บริการข้อมูลประจำตัว

ปัจจุบันมีผู้ให้บริการข้อมูลประจำตัวหลายร้อย (ถ้าไม่ใช่หลายพัน) ที่ได้รับความนิยมมากที่สุด ได้แก่ Facebook, Google, GitHub และ Instagram สำหรับโพสต์นี้ SimpleLogin ได้รับเลือกเนื่องจากเป็นมิตรกับนักพัฒนา รหัสเดียวกันนี้จะใช้ได้กับผู้ให้บริการข้อมูลประจำตัว OAuth2 ทุกราย (ข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้ร่วมก่อตั้งของ SimpleLogin ซึ่ง—อะแฮ่ม—อาจเป็นปัจจัยในการตัดสินใจใช้งานของฉัน)

โปรดไปที่ SimpleLogin และสร้างบัญชี หากคุณยังไม่มีบัญชี ให้สร้างแอปใหม่ในแท็บนักพัฒนาซอฟต์แวร์

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

การตั้งค่า OAuth2

 export CLIENT_ID={your AppID} export CLIENT_SECRET={your AppSecret}

ใน app.py โปรดเพิ่มบรรทัดเหล่านี้ที่ด้านบนของไฟล์เพื่อรับ client id client secret

 import os CLIENT_ID = os.environ.get("CLIENT_ID") CLIENT_SECRET = os.environ.get("CLIENT_SECRET")

โปรดเพิ่ม OAuth URL เหล่านี้ที่ด้านบนของ app.py ที่จะใช้ในขั้นตอนต่อไป นอกจากนี้ยังสามารถคัดลอกได้ในหน้าปลายทาง OAuth

 AUTHORIZATION_BASE_URL = "https://app.simplelogin.io/oauth2/authorize" TOKEN_URL = "https://app.simplelogin.io/oauth2/token" USERINFO_URL = "https://app.simplelogin.io/oauth2/userinfo"

เนื่องจากเราไม่ต้องการกังวลเกี่ยวกับการตั้งค่า SSL ในตอนนี้ มาบอก Requests-OAuthlib ว่าสามารถใช้ HTTP ธรรมดาได้:

 # This allows us to use a plain HTTP callback os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

ตามปกติแล้ว รหัสสำหรับขั้นตอนนี้จะอยู่ที่ step2.py

ขั้นตอนที่ 3: เข้าสู่ระบบเปลี่ยนเส้นทาง

เมื่อผู้ใช้คลิกที่ปุ่มเข้าสู่ระบบ:

  1. ผู้ใช้จะถูกเปลี่ยนเส้นทางไปยังหน้าการให้สิทธิ์ผู้ให้บริการเข้าสู่ระบบข้อมูลประจำตัวเพื่อสอบถามว่าผู้ใช้ต้องการแชร์ข้อมูลของตนกับแอปของคุณหรือไม่
  2. เมื่อผู้ใช้อนุมัติแล้ว พวกเขาจะถูกเปลี่ยนเส้นทางกลับไปที่หน้าในแอปของคุณพร้อมกับ code ใน URL ที่แอปของคุณจะใช้เพื่อแลกเปลี่ยน access token ที่ช่วยให้คุณรับข้อมูลผู้ใช้จากผู้ให้บริการได้ในภายหลัง

ดังนั้นเราจึงต้องการสองเส้นทาง: เส้นทางการ login ที่เปลี่ยนเส้นทางผู้ใช้ไปยังผู้ให้บริการข้อมูลประจำตัว และเส้นทางการ callback ที่ได้รับ code และแลกเปลี่ยนเป็น access token เส้นทางการโทรกลับมีหน้าที่ในการแสดงข้อมูลผู้ใช้ด้วย

 @app.route("/login") def login(): simplelogin = requests_oauthlib.OAuth2Session( CLIENT_ID, redirect_uri="http://localhost:5000/callback" ) authorization_url, _ = simplelogin.authorization_url(AUTHORIZATION_BASE_URL) return flask.redirect(authorization_url) @app.route("/callback") def callback(): simplelogin = requests_oauthlib.OAuth2Session(CLIENT_ID) simplelogin.fetch_token( TOKEN_URL, client_secret=CLIENT_SECRET, authorization_response=flask.request.url ) user_info = simplelogin.get(USERINFO_URL).json() return f""" User information: <br> Name: {user_info["name"]} <br> Email: {user_info["email"]} <br> Avatar <img src="{user_info.get('avatar_url')}"> <br> <a href="/">Home</a> """

การคลิกที่ปุ่มเข้าสู่ระบบควรนำคุณไปสู่ขั้นตอนต่อไปนี้ รหัสเต็มสามารถพบได้ใน GitHub ที่ step3.py

เข้าสู่ระบบด้วย SimpleLogin เพื่ออนุญาตให้ผู้ใช้ข้อมูล

เข้าสู่ระบบด้วย Facebook

การตั้งค่าการเข้าสู่ระบบ Facebook, Google และ Twitter ค่อนข้างซับซ้อนและต้องมีขั้นตอนเพิ่มเติม เช่น การตั้งค่า SSL หรือการเลือกขอบเขตที่เหมาะสม สิ่งเหล่านี้อยู่นอกเหนือขอบเขตของบทความนี้

นอกเหนือจาก UI ที่ซับซ้อนแล้ว ส่วนที่ยากที่สุดในการผสานรวม Facebook อาจเป็นการหาวิธีให้บริการเว็บแอปของคุณบน HTTPS ในเครื่อง เนื่องจาก Facebook SDK เวอร์ชันใหม่ไม่อนุญาตให้ใช้ HTTP ธรรมดาในเครื่อง ฉันแนะนำให้ใช้ Ngrok ซึ่งเป็นเครื่องมือฟรีที่มี HTTPS URL อย่างรวดเร็ว

ขั้นตอนที่ 1: สร้างแอพ Facebook

โปรดไปที่ https://developers.facebook.com และสร้างแอปใหม่:

สร้าง ID แอพใหม่

จากนั้นเลือก "ผสานการเข้าสู่ระบบ Facebook" ในหน้าจอถัดไป:

รวมการเข้าสู่ระบบ Facebook

ขั้นตอนที่ 2: ข้อมูลรับรอง OAuth ของ Facebook

คลิก "การตั้งค่า/พื้นฐาน" ทางด้านซ้าย แล้วคัดลอก App ID และ App Secret จริงๆ แล้วเป็น OAuth client-id และ client-secret

การตั้งค่าพื้นฐาน

อัปเดต client-id และ client-secret

 export FB_CLIENT_ID={your facebook AppId} export FB_CLIENT_SECRET={your facebook AppSecret}

อัปเดต AUTHORIZATION_BASE_URL และ TOKEN_URL:

 FB_AUTHORIZATION_BASE_URL = "https://www.facebook.com/dialog/oauth" FB_TOKEN_URL = "https://graph.facebook.com/oauth/access_token"

หน้าแรก:

 @app.route("/") def index(): return """ <a href="/fb-login">Login with Facebook</a> """

ขั้นตอนที่ 3: เข้าสู่ระบบและปลายทางการโทรกลับ

หากแอปให้บริการหลัง ngrok โดยใช้คำสั่ง ngrok http 5000 เราจำเป็นต้องตั้งค่า URL ปัจจุบันเป็น ngrok URL

 # Your ngrok url, obtained after running "ngrok http 5000" URL = "https://abcdefgh.ngrok.io"

โปรดตรวจสอบให้แน่ใจว่าได้เพิ่ม URL ในการเข้าสู่ระบบ/การตั้งค่า Facebook ของคุณ การตั้งค่า OAuth Redirect URI ที่ถูกต้อง:

URI การเปลี่ยนเส้นทาง OAuth ที่ถูกต้อง

ในการเข้าถึงอีเมลของผู้ใช้ คุณต้องเพิ่ม email ใน scope :

 FB_SCOPE = ["email"] @app.route("/fb-login") def login(): facebook = requests_oauthlib.OAuth2Session( FB_CLIENT_ID, redirect_uri=URL + "/fb-callback", scope=FB_SCOPE ) authorization_url, _ = facebook.authorization_url(FB_AUTHORIZATION_BASE_URL) return flask.redirect(authorization_url)

เส้นทางการ callback นั้นซับซ้อนกว่าเล็กน้อย เนื่องจาก Facebook ต้องการการแก้ไขการปฏิบัติตามข้อกำหนด:

 from requests_oauthlib.compliance_fixes import facebook_compliance_fix @app.route("/fb-callback") def callback(): facebook = requests_oauthlib.OAuth2Session( FB_CLIENT_ID, scope=FB_SCOPE, redirect_uri=URL + "/fb-callback" ) # we need to apply a fix for Facebook here facebook = facebook_compliance_fix(facebook) facebook.fetch_token( FB_TOKEN_URL, client_secret=FB_CLIENT_SECRET, authorization_response=flask.request.url, ) # Fetch a protected resource, ie user profile, via Graph API facebook_user_data = facebook.get( "https://graph.facebook.com/me?fields=id,name,email,picture{url}" ).json() email = facebook_user_data["email"] name = facebook_user_data["name"] picture_url = facebook_user_data.get("picture", {}).get("data", {}).get("url") return f""" User information: <br> Name: {name} <br> Email: {email} <br> Avatar <img src="{picture_url}"> <br> <a href="/">Home</a> """

ตอนนี้เมื่อคลิกเข้าสู่ระบบด้วย Facebook คุณจะสามารถผ่านขั้นตอนทั้งหมดได้

เข้าสู่ระบบด้วยกระบวนการ Facebook

รหัสเต็มอยู่ที่ facebook.py

บทสรุป

ขอแสดงความยินดี คุณรวมการเข้าสู่ระบบ SSO เข้ากับแอป Flask สำเร็จแล้ว!

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

แอพนี้ยังต้องให้บริการบน https ในการผลิตซึ่งสามารถทำได้ง่ายในวันนี้ด้วย Let's Encrypt

มีความสุข OAuthing!