Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

เชี่ยวชาญ Devise ใน Rails: OmniAuth, API Auth และ Authtrail

ใน part one ของ series นี้ เราแนะนำ Devise โดยใช้ example app เพื่อสำรวจ modules, helpers, views, controllers และ routes

ใน part นี้ เราจะสำรวจ advanced usages ของ Devise มากขึ้น โดยเฉพาะการใช้ OmniAuth, API authentication และ Authtrail

มา dive straight in!

Authentication ด้วย OmniAuth สำหรับ Ruby

ในปัจจุบันเกือบทุก web application ที่คุณพบ จะเสนอ option ให้ login ผ่าน wide selection ของ authentication providers ตั้งแต่ social networks อย่าง Twitter และ Facebook ไป Google, GitHub และอื่นๆ อีกมากมาย

ในหลายกรณี convenient multi-provider authentication นี้ powered by library ที่เรียกว่า OmniAuth OmniAuth เป็น flexible และ powerful authentication library สำหรับ Ruby ที่อนุญาตให้คุณ integrate กับ multiple external providers

มันให้ simple และ unified API สำหรับเชื่อมต่อกับ various OAuth providers OmniAuth มีประโยชน์โดยเฉพาะใน situations ที่คุณอยากให้ users มี option sign up หรือ log in โดยใช้ social media accounts ด้วย OmniAuth คุณสามารถเพิ่ม social login functionality เข้าไปใน Rails application ได้ง่าย

เมื่อ OmniAuth ใช้ร่วมกับ Devise gem มันจะง่ายขึ้นอีกในการจัดการ user authentication และ authorization คุณสามารถ leverage Devise's built-in authentication features ขณะใช้ OmniAuth สำหรับ external provider sign-in options

Getting Started with OmniAuth และ Devise

เช่นที่กล่าวไว้ OmniAuth ให้คุณ integrate กับ number of third-party authentication providers สำหรับ purposes ของบทความนี้ เราจะใช้ GitHub

ติดตั้ง OmniAuth Gems

ใน Gemfile ของแอป เพิ่มบรรทัดต่อไปนี้:


และหากคุณใช้ OmniAuth 2.0+ gem คุณจะต้องเพิ่มอีก:


Gem omniauth-rails_csrf_protection ให้แน่ใจว่า GET requests ไป OAuth flow ถูก disabled มันยัง insert Rails CSRF token verifier ก่อน OAuth request phase สอง actions นี้ meant to mitigate cross-site forgery attacks ที่มุ่งไปที่ OAuth authentication flow

ต่อไป รัน bundle install เพื่อติดตั้ง gems

สร้าง GitHub OAuth App ใหม่

ตอนนี้เราต้องสร้าง OAuth app ใหม่บน GitHub App นี้จะทำหน้าที่เป็น user ที่มี authentication permissions ซึ่งสามารถ revoked ได้ง่าย หากจำเป็น

ขั้นแรกคือไปที่ settings page ภายใต้ GitHub account profile แล้ว left-hand menu คลิก "Developer settings" คุณจะเห็นหน้าจอแบบด้านล่าง ที่คุณสร้าง OAuth app ใหม่:

เชี่ยวชาญ Devise ใน Rails: OmniAuth, API Auth และ Authtrail

เมื่อคุณคลิก "Register new application" คุณจะได้หน้าจอแบบนี้:

เชี่ยวชาญ Devise ใน Rails: OmniAuth, API Auth และ Authtrail

กรอกรายละเอียดฟอร์มดังนี้:

  • Application name - ให้ OAuth app ใหม่ชื่อที่เหมาะสม
  • Homepage url - ตอนนี้ใช้ https://localhost:3000/ ใน production คุณจะใช้ actual homepage url ของแอป
  • Application description - ไม่จำเป็น แตคุณยังสามารถมีได้หากคุณมีหลาย apps และต้อง differentiate ระหว่างพวกมัน
  • Authorization callback url - นี่คือ required input และโดยทั่วไปจะ follow OAuth callback url format เช่น https://<app-url>/users/auth/<application-provider>/callback นั่น said บาง OAuth providers เช่น Google อาจไม่ follow format นี้ ดังนั้นคุณต้องจดบันทึกไว้

เมื่อทำเสร็จ คลิก "Register application" ในหน้าจอถัดไป generate app secret ใหม่ และจดมันไว้ที่ปลอดภัย (เพราะมันจะแสดงให้คุณเห็นแค่ครั้งเดียว)

Config Devise Initializer

เปิด Devise initializer config/initializers/devise.rb และ navigate ไป OmniAuth section specific to GitHub มันน่าจะ commented out ดังนั้น uncomment และ edit ด้วย ID และ secret ของ GitHub OAuth app ใหม่:


สร้าง OmniAuth Callbacks Controller

หากคุณ generate Devise controllers แล้ว คุณจะพบ OmniauthCallbacksController พร้อมสำหรับ customize ตามลำดับ หากไม่มี ให้สร้าง manual และ edit ดังนี้:


สองสิ่งที่ต้องสังเกตเกี่ยวกับโค้ดข้างต้น:

  • from_omniauth เป็น method ที่เราจะ implement ภายใน User model
  • sign_in_and_redirect เป็น method ภายใน OAuth

เพิ่ม Migration เพื่อปรับ User Model

ตอนนี้เราต้องเพิ่มคอลัมน์บางส่วนไปที่ User model โดยเฉพาะคอลัมน์ provider และ uid:


รัน bundle exec rails db:migrate เพื่อ finalize ขั้นตอนนี้

ทำให้ Devise Model Omniauthable

ที่นี่ เราต้อง edit User model โดยเพิ่ม Devise Omniauthable module เข้าไป:


หลังจากนี้ เพิ่มเมธอด from_omni_auth นี้จะถูกเรียกจาก Users::OmniauthCallbacksController ที่เรา setup ไปเมื่อกี้:


ตอนนี้เราเหลือแค่หนึ่งอย่าง: เพิ่ม login links ไปที่ Devise views

Setup Login Links

โดย default Devise จะ automatically เพิ่ม login link ของ provider ที่เหมาะสมให้คุณใน user registration และ login views Link นี้จะใช้ GET method แตเรารู้ว่า OmniAuth 2.0+ ชอบ POST requests ดังนั้นเราต้อง disable link และ insert ของเราเองโดยใช้ POST requests:


ด้วยนั้น เรา successfully set up Ruby on Rails 7 app ด้วย Devise และ GitHub OAuth authentication คุณสามารถ grab full source code ของ companion app ได้ที่นี่

ต่อไป เราจะเข้า advanced use case อีกอย่าง: การใช้ Devise เพื่อ authenticate API call

API Authentication ด้วย Devise สำหรับ Ruby

วันนี้ ไม่ uncommon ที่ users คาดหวังจะ connect กับแอปของคุณ via API ใน section นี้ เราจะดูว่าเราสามารถ safely authenticate user requests เหล่านี้โดยใช้ Devise อย่างไร

ในขณะที่ browser-based authentication โดยทั่วไปเป็น cookie-based ส่วนใหญ่ API authentication เกิดขึ้นผ่าน tokens ที่เรียกว่า JSON Web Tokens (หรือ JWTs) ซึ่งถูก passed around ใน headers

Tip: สำหรับ purposes ของ section นี้ เราจะ assume เราทำงานกับ Rails API-only app เพื่อ follow along สร้างหนึ่งด้วย rails new app_name --api

JWT-based Authentication Flow

เช่นที่กล่าวไว้ API authentication based on JWT tokens และมันสำคัญที่เราเข้าใจว่า JWT-based authentication flow เกิดขึ้นอย่างไร Basically มัน follow general sequence of steps ตาม outline ด้านล่าง:

  • User client ทำ call ไป API app
  • API app responds ด้วย JSON Web Token (JWT) authentication token ที่ใช้แทน cookies
  • Subsequent requests โดย user client ทำโดยใช้ token นี้ใน Authorization header
  • User สามารถ hit Devise 'session destroy' action ซึ่งทำให้ token ถูก destroy และ user ถูก logged out

ตอนนี้ มา put flow นี้ into action เริ่มจากสิ่งที่เรียกว่า cross-origin resource sharing (CORS)

การตั้งค่า CORS

CORS ตั้งค่า API app ให้ allow requests จาก external sources CORS เป็น HTTP-based security policy ที่ define ว่า external requests จะถูก handled โดยแอปพลิเคชันอย่างไร โดย default CORS จะ block request ใดๆ ที่ originate จาก domain ต่างจากที่ทำ initial request (กล่าวคือ request มาจาก "origin" ที่ต่างกัน)

เพื่อ handle CORS properly เราจะใช้ nifty gem rack-cors ใน Gemfile uncomment บรรทัดด้านล่าง แล้วรัน bundle install:


เพิ่มเติม เปิด CORS initializer file ที่ตรงกันและปรับมันดังนี้:


important notes เกี่ยวกับสิ่งที่เราทำไป:

  • origins "*" - เพียงแค่หมายความว่า API app ของเราสามารถรับ requests จาก source ใดๆ ได้ตอนนี้
  • expose: %w[Authorization Uid] - โดย default rack-cors gem ไม่ expose authorization และ UID headers แตเราต้องการพวกมันเพราะเราจะ pass authorization tokens ผ่าน

เมื่อทำเสร็จ มาติดตั้ง Devise และ Devise-JWT gem ที่มาพร้อมกัน

เพิ่ม Devise และ Devise-JWT Gems เข้า Rails App

Gem devise-jwt เป็น extension ของ Devise ที่จะให้เราทำงานกับ JWT tokens เพิ่ม gems ไป Gemfile แล้วรัน bundle install:


รัน Devise install generator bundle exec rails g devise:install

Generate และ Configure Models

เราต้อง setup สอง models: normal Devise user model (bundle exec rails g devise User) และ model ที่เราจะใช้สำหรับ revocation strategy (กล่าวคือ ว่าผู้ใช้จะ sign out ของ API ยังไง):


เรา modify normal Devise user model สำหรับ API authentication โดยเพิ่ม JWT token authenticatable module และ define token revocation strategy ใช้ model ที่สอง JwtDenylist:


ต่อไป เรา configure model ที่สองโดย reference revocation strategy และ revocation table ที่จะใช้:


ในส่วนต่อไป เราจะ outline token revocation และทำไมเราต้องการมัน

ความสำคัญของ Token Revocation

ทำไม token revocation สำคัญ? เพราะ JWT tokens เป็น stateless Server ไม่รู้อะไรเกี่ยวกับพวกมันนอกจากการ sign พวกมัน ใน scenario แบบนี้ server ไม่มีทาง sign out user โดย revoke corresponding token เนื่องจากไม่มีทาง revoke individual tokens เราต้อง build one out และบอก server ให้ใช้มัน

เมื่อ revoke token สิ่งที่เกิดขึ้นจริงภายใต้ hood คือ unique piece ของ token, jti (JWT ID) ถูก extract และใช้ตาม defined revocation strategy

แน่นอนสิ่งนี้ยกคำถามอีกอย่างว่า token revocation strategy คืออะไร In a nutshell มันคือ definition ของว่าการ revoke token จะถูก handled โดย server อย่างไร มีสาม basic revocation strategies:

  • JTIMatcher strategy - ที่นี่ unique column ชื่อ "jti" ถูกเพิ่มไปที่ user model ซึ่งก็ทำหน้าที่เป็น revocation table ทุกครั้งที่ user ทำ request jti ใน header จะ matched against stored tokens และ access allowed เฉพาะเมื่อ match พบ
  • Denylist strategy - สำหรับอันนี้ jti และ token expiry (exp ของ revoked tokens) ถูกเก็บใน database table สำหรับทุก request ที่ user ทำ check จะทำ against table นี้ เปรียบเทียบ jti ของ current token ของ user ต่อ revoked ones ใน database หาก match พบ request ของ user นั้นถูก denied
  • Allowlist strategy - ในทางหนึ่ง strategy นี้คล้าย strategy แรก แตที่นี่ table ที่เก็บ JWT IDs อยู่ใน one-to-many relationship กับอีก table ที่เก็บ user tokens ทุกครั้งที่ request ทำ jti ของ user ที่เก็บใน Allowlist table matched against สิ่งที่เก็บใน matching table พร้อม tokens Access allowed เฉพาะเมื่อ match พบ

แน่นอนนี่เป็น overview ที่ simplified มากของ token revocation และคุณสามารถเรียนรู้เพิ่มเติมได้ที่นี่

Set Up Signing Key สำหรับ JWT Tokens

เนื่องจากเราจะใช้ secure tokens authenticate users และ requests ของพวกเขา เราต้องการวิธี sign พวกมัน นี่คือที่ที่ secret key เข้ามา แนะนำให้ generate new key ต่างจาก Rails secret key, secret_key_base

รัน bundle exec rake secret เพื่อ generate unique key แล้ว edit Devise initializer ให้ include key นี้:


สุดท้าย มา setup controllers

Controller Set Up

ขั้นตอนสุดท้ายในการ implement Devise สำหรับ API authentication คือ setup controllers ของเรา เพื่อ simplicity เราจะ setup สอง controllers: หนึ่งสำหรับ registration และอีกหนึ่งสำหรับ sessions

มา create เอง เริ่มจาก registrations controller:


สิ่งที่เกิดขึ้นกับ controller นี้:

  • เรา config ให้ respond to requests ด้วย JSON
  • เราระบุ respond_with action ที่คืนค่า result ของ successful หรือ failed registration

และตอนนี้ sessions controller:


เหมือน registrations controller ที่นี่เราระบุว่า controller จะ respond ด้วย JSON เรายัง define respond_with action สำหรับเมื่อ user login สำเร็จ และ respond_to_on_destroy เพื่อ handle user signing out

และด้วยนั้น คุณควรมี working API authentication flow powered by Devise และ JWT tokens!

ส่วนสุดท้ายของเราจะ quick look ที่ว่าคุณสามารถ track user logins โดยใช้ Devise และ Authtrail อย่างไร

การ Track Devise Logins ด้วย Authtrail

สมมติคุณอยากส่ง notification email ไปให้ app users ทุกครั้งที่มีคน login เข้าบัญชี พร้อม details เช่น IP address และ timestamp ของ login คุณจะ accomplish สิ่งนี้อย่างไร?

คุณจะต้อง track user logins แล้วใช้ข้อมูลนั้นใน notification emails ที่คุณส่งให้ users แตก่อน คุณจะต้องการวิธี track user logins สิ่งนี้สามารถ accomplish ได้โดยใช้ nifty gem ที่เรียกว่า Authtrail ซึ่งก็ pair well กับ Devise

การติดตั้ง Authtrail

ขั้นแรกคือติดตั้ง gem ด้วย bundle add authtrail เพิ่มเติม เนื่องจากคุณจะ store user-identifiable information เช่น emails และ IP addresses ใน app database มัน highly recommended ที่คุณ encrypt data นี้ใน production โดยใช้ combination ของ Lockbox และ Blindindex gems

ต่อไป รัน Authtrail generator เพื่อสร้าง initializer และ accompanying table migration ที่จะเก็บ login data:


Authtrail ทำงานอย่างไร

ทุกครั้งที่ user พยายาม log in Authtrail record ใหม่ถูกสร้างพร้อม details สำคัญต่อไปนี้:

  • Login email address ที่ใช้
  • Whether login successful หรือไม่
  • Reason ที่ login failed (ถ้า login เป็น failure)
  • User's IP address, referrer และอีกมากมาย

คุณสามารถใช้ข้อมูลนี้ได้ตามที่คุณต้องการ ตัวอย่างเช่น คุณสามารถส่ง notification email ให้ user รวมถึง email และ IP address information เพื่อแจ้งให้พวกเขารู้ว่ามี login attempt บนบัญชีของพวกเขา

อ่าน Authtrail's documentation เพื่อดู possibilities ทั้งหมดที่ available ให้คุณ

สรุป

ใน series นี้ เรา deep dive เข้า Devise gem

ก่อนอื่น เราจับ basics ของ Devise รวมถึงว่า modules, helpers, views, controllers และ routes ทำงานอย่างไร ใน part สองและ final นี้ เราสำรวจการใช้ Devise กับ OAuth, Authtrail และสำหรับ API authentication

หวังว่า series นี้จะเป็น helpful guide สำหรับ all things Devise authentication

Happy coding!

P.S. หากคุณต้องการอ่านโพสต์ Ruby Magic ทันทีที่เผยแพร่ ให้สมัครสมาชิก newsletter Ruby Magic ของเรา และจะไม่พลาดโพสต์เด็ดๆ ไป!