---
title: "كيف تطبّقون المصادقة بلا كلمة مرور في Ruby on Rails"
description: "تحدثنا مؤخرًا عن فوائد المصادقة بلا كلمة مرور. لنرَ الآن كيف نطبّقها عمليًا في Ruby on Rails باستخدام روابط تسجيل الدخول السحرية."
canonical: https://oozou.com/ar/blog/how-to-implement-passwordless-authentication-in-ruby-on-rails-154
locale: ar
date: 2020-12-31
author: "OOZOU Team"
---

# كيف تطبّقون المصادقة بلا كلمة مرور في Ruby on Rails

> تحدثنا مؤخرًا عن فوائد المصادقة بلا كلمة مرور. لنرَ الآن كيف نطبّقها عمليًا في Ruby on Rails.

Published: 2020-12-31 · Author: OOZOU Team (Engineering) · 4 min read

في [المقال السابق](https://oozou.com/blog/going-passwordless-for-a-more-secure-and-welcoming-future-153) تحدثت عن فوائد المصادقة بلا كلمة مرور. وفي هذا المقال سأوضح كيف نطبّق رسائل البريد التي تحمل رابط تسجيل دخول سحريًا (magic link) في Ruby on Rails. لمن لا يعرف الفكرة: تتيح روابط تسجيل الدخول السحرية للمستخدم الدخول إلى حسابه من دون إدخال كلمة مرور. فبدلًا من النموذج التقليدي للبريد الإلكتروني وكلمة المرور، يكفي أن يُدخل المستخدم بريده الإلكتروني. يرسل النظام بعد ذلك رابطًا يحتوي على token مرتبط بحسابه. وعندما ينقر المستخدم على الرابط، ينتقل إلى التطبيق ويُسجَّل دخوله إلى حسابه مباشرة. وبهذه الطريقة يمكننا أيضًا تسجيل دخوله في التطبيقات الأصلية (native) باستخدام deeplinks.

# لنكتب بعض الكود

أول ما نحتاج إليه هو إضافة عمود جديد إلى جدول Users لتخزين login tokens. سنستخدم UUIDs لتبسيط عملية التوليد.

```
rails g migration add_login_token_to_users login_token:uuid
```

لننشئ الآن controller يتولى عمليات تسجيل الدخول. لن يكون هذا الـ controller متوافقًا تمامًا مع RESTful، لأننا لا نستطيع إرسال طلب POST من برنامج البريد لدى المستخدم. عادةً أستخدم magic_logins_controller حتى نحتفظ بـ sessions controller لتسجيل الدخول بكلمة المرور، إن أردنا يومًا الجمع بين الطريقتين.

```ruby
class MagicLoginsController < ApplicationController
  def new; end

  def create
    if (user = User.find_by(login_token_params))
      user.generate_login_token!
      LoginMailer.with(user: user).magic_login_email.deliver_later
      redirect_to(new_magic_login_path, notice: 'Please check your inbox')
    else
      redirect_to(new_magic_login_path, alert: 'It looks like that email is not registered')
    end
  end

  private def login_token_params
    params.require(:login_token).permit(:email)
  end
end
```
```ruby
Rails.application.routes.draw do
  resources :magic_logins, only: %i[new create]
end
```

لنُضف view للـ action المسمّى new. لن أضيف أي تنسيق، ويمكنكم تنسيقه كما تشاؤون، سنكتفي بنموذج بسيط كمثال.

```erb
<%= form_for(User.new, url: magic_logins_path, method: :post do %>
  <%= f.label :email %>
  <%= f.email_field :email %>
  <%= f.submit 'Send me a magic login link' %>
<% end %>
```

بعد ذلك نحتاج إلى إضافة method في الـ model المسمّى User لتوليد token جديد.

```ruby
class User < ApplicationRecord
  def generate_login_token!
    update_column(:login_token, SecureRandom.uuid)
  end
end
```

لا نحتاج إلى أي تحقق هنا لأننا نستخدم UUIDs، فنحن في أمان. حسنًا، لننشئ الآن _magic_login_email.
_

```ruby
class LoginMailer < ApplicationMailer
  def magic_login_email
    user = params[:user]
    @magic_login_url = magic_logins_url(token: user.login_token)
    mail(to: user.email, subject: "Here's your login link")
  end
end
```
```erb
<h1>Here's your login link</h1>
<%= link_to 'Log in now', @magic_login_url %>
```

ربما لاحظتم أننا نستخدم رابط index ونمرّر الـ token في query string. هناك طرق أخرى لفعل ذلك، لكن هذا النهج بسيط ويتماشى مع ما يوفّره Rails أصلًا. وهو بالطبع يخالف مبادئ RESTful، لذلك قد تفضّلون نهجًا آخر.
الآن لم يبقَ سوى إضافة منطق تسجيل دخول المستخدمين. سأفترض أنكم تستخدمون Devise. إن كنتم تستخدمون gem آخر أو تديرون الجلسات يدويًا، فستحتاجون إلى تعديل هذا المنطق.

```ruby
class MagicLoginsController < ApplicationController
  def index
    redirect_to root_path unless params[:token].present?

    user = User.find_by(login_token: params[:token])
    if user
      sign_in(user)
      user.update_column(:login_token, nil)
      redirect_to after_sign_in_path, notice: 'Logged in successfully!'
    else
      redirect_to new_magic_login_path, alert: 'The link you used was invalid. Please request a new login link'
    end
  end
end
```

كما ترون، نسجّل دخول المستخدم ثم نحذف login token من قاعدة البيانات، وهذا يمنع استخدام الـ token أكثر من مرة. أصبح كل شيء مترابطًا، ويمكن للمستخدمين الآن تسجيل الدخول عبر الروابط السحرية. وقد ترغبون أيضًا في تحديد مدة صلاحية الـ tokens. لفعل ذلك يكفي أن نضيف عمودًا آخر لتتبّع وقت إنشاء الـ token والتحقق من أنه لم تنتهِ صلاحيته

```
rails g migration add_login_token_generated_at_to_users login_token_generated_at:datetime
```
```ruby
class MagicLoginsController < ApplicationController
  def index
    redirect_to root_path unless params[:token].present?

    user = User.find_by(login_token: params[:token])
    if user
      if user.login_token_generated_at < 15.minutes.ago
        redirect_to new_magic_login_path, alert: 'The link you used has expired. Please request a new login link'
      end
      sign_in(user)
      user.update_column(:login_token, nil)
      redirect_to after_sign_in_path, notice: 'Logged in successfully!'
    else
      redirect_to new_magic_login_path, alert: 'The link you used was invalid. Please request a new login link'
    end
  end
end
```
```ruby
Rails.application.routes.draw do
  resources :magic_logins, only: %i[index new create]
end
```

هذا كل ما في الأمر. التطبيق بسيط جدًا في الحقيقة.

---

Contact OOZOU: hello@oozou.com · +66 2544 0000 · https://oozou.com/ar/contact

More for AI agents: https://oozou.com/llms.txt · every page has a markdown twin at its URL plus ".md"
