---
title: "How to Implement Passwordless Authentication in Ruby on Rails"
description: "We recently discussed the benefits of passwordless authentication. Now let's take a look at how to implement passwordless authentication in Ruby on Rails"
canonical: https://oozou.com/blog/how-to-implement-passwordless-authentication-in-ruby-on-rails-154
locale: en
date: 2020-12-31
author: "OOZOU Team"
---

# How to Implement Passwordless Authentication in Ruby on Rails

> We recently discussed the benefits of passwordless authentication. Now let's take a look at how to implement passwordless authentication in Ruby on Rails

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

In the [previous article](https://oozou.com/blog/going-passwordless-for-a-more-secure-and-welcoming-future-153) I discussed the benefits of passwordless authentication. In this article I'm going to demonstrate how to implement magic sign in link emails in Ruby on Rails. For those unfamiliar, magic login links allow a user to log into their account without entering a password. Instead of the traditional email and password form, the user only needs to enter their email address. The system will then send a link to the user which contains a token linked to their account. When the user clicks the link they will be taken to the application and seamlessly logged into their account. With this technique we can even log them into native applications through the usage of deeplinks.

# Let's Write Some Code

The first thing we need to do is add a new column to our Users to store the login tokens. We'll use UUIDs to make generation simpler.

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

Now let's create a controller to handle the log ins. This controller won't be strictly RESTful as we can't use a post request from the users email client. I normally use magic_logins_controller so we can also reserve sessions controller for password logins should we ever want to have both.

```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
```

Let's add a view for the new action. I'm not going to style this, you can style however you like, we'll simply add a form for an example.

```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 %>
```

Next we need to add a method to generate a new token to our User model.

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

We don't need to validate anything here as we're using UUIDs so we should be safe. Ok, so next, let's create our _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 %>
```

You might have noticed we're using the index url and passing the token as a query string. You can do this other ways but this approach is simple and fits in with what Rails already offers. It does of course violate RESTful principles so you might prefer to use another approach.
Now we just need to add the logic to sign in the users. I'm going to assume you're using Devise. If you're using another gem or are managing sessions manually you'll need to adjust this logic.

```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
```

As you can see we are signing in the user and then removing the login token from the database. This prevents the token from being used more than once. Everything is all hooked up and users can now login using magic links. You may also like to restrict how long the tokens are valid for. To do this we can simply add another column to track when the token was created and verify it hasn't expired

```
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
```

That's all there is to it. It's really quite simple to implement.

---

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

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