Implement a passwordless authentication with Sorcery

What is passwordless authentication?

Passwordless authentication is a verification process that determines whether someone is, in fact, who they say they are without requiring that person to enter their username and password.

Instead you use a security token to authenticate the user.

Here are some benefits of passwordless auth:

  • improved UX - the user doesn't need to remember passwords and request new password if they forget their old one
  • increased security - reduced chance of attacks, password re-use and leaks
  • relatively simple to create - you don't need to create a lot of views

What is Sorcery?

Even though that I am a big fan of Devise, I started to like how simple and clean Sorcery is. It doesn't take much code to get Sorcery up and running, especially if you are implementing passwordless authentication.

Authentication through email

This is the basic premise of using email to authenticate the user

  • a user inputs their email on an input link
  • we send an email with a magic link (one time, randomly generated code)
  • The user clicks the link and the service being used will identify the token and exchange it for a live token, logging the user in.

Enough theory, let's get to coding

Our goal in this article is to create a passwordless authentication system for the User model and send the user an email containing a magic link. When a user click on the magic link, they are automatically signed in.

Create a user controller and model

Pay attention to the login_token and the login_token_valid_until - we will use those to store information about the passwordless auth.

rails g controller users index edit update
rails g model User name:string email:string login_token:string login_token_valid_until:datetime
rake db:migrate

Create the skeleton for the authentication logic

In these controllers, we will validate the token and login the user.

rails g controller logins create
rails g controller sessions create destroy

Update routes file

Rails.application.routes.draw do
  get 'sessions/create'
  delete 'sessions/destroy'

  get 'logins/new'
  post 'logins/create'
  resources :users
  root 'users#index'

Configure Sorcery

Let's go to Github and see how to configure Sorcery, in order to link it to the User model.

We won't need to use all the steps in the example, because we don't want passwords.

In the Gemfile, include

gem 'sorcery'

and in the terminal execute the command


Next, we hook Sorcery to the User model by simple writing

class User < ApplicationRecord

Update Application Controller to configure current_user.

We also let rails know what should happen if the user is not authenticated successfully.

class ApplicationController < ActionController::Base
  def user_class

  def not_authenticated
    redirect_to root_path, alert: 'Not authenticated'

Finally, include require_login in the UsersController in order to let Sorcery know which actions require authentication.

class UsersController < ApplicationController
  before_action :require_login, only: [:edit, :update]
  def index

  def edit

  def update

Create a form that sends an email

Install the simple form gem

gem 'simple_form'

In logins/new.html.erb, we define a simple form that submits an email address.

<%= simple_form_for @user, url: logins_create_path, method: :post do |f| %>
  <%= f.input :email, label: "Email" %>
  <%= f.submit "Get a magic link" %>
<% end %>

Next, let's update our LoginsController to look like this

class LoginsController < ApplicationController
  def new
    @user =

  def create
    # the user might already exist in our db or it might be a new user
    user = User.find_or_create_by!(email: params[:user][:email])

    # create a login_token and set it up to expiry in 60 minutes
    user.update!(login_token: SecureRandom.urlsafe_base64,
      login_token_valid_until: + 60.minutes)
    # create the url which is to be included in the email
    url = sessions_create_url(login_token: user.login_token)

    # send the email
    LoginMailer.send_email(user, url).deliver_later

    redirect_to root_path, notice: 'Login link sent to your email'

Create the LoginMailer

We defined the LoginMailer, now we need to create it.

Create a file in app/mailers/login_mailer that will look like this

class LoginMailer < ApplicationMailer
  def send_email(user, url)
    @user = user
    @url  = url

    mail to:, subject: 'Sign in into'

We need to create the view of the letter. Create a new file app/views/login_mailer/send_email.html.erb

<!DOCTYPE html>
    <meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
    <h1>Welcome <%= %>,</h1>
    <a href="<%= @url %>">Magic link</a>

(Optional) Configure LetterOpener

LetterOpener is a great gem that allows us to view emails that have been sent in development.

Once again, add the gem in the Gemfile and bundle

gem "letter_opener", :group => :development

In development.rb, include these two lines

config.action_mailer.delivery_method = :letter_opener
config.action_mailer.perform_deliveries = true

We should now be able to send and view the emails we send!

config.action_mailer.delivery_method = :letter_opener
config.action_mailer.perform_deliveries = true

We should now be able to send and view the emails we send!

Sessions controller

class SessionsController < ApplicationController
  def create
    # we don't log in the user if a login token has expired 
    user = User.where(login_token: params[:login_token])
                .where('login_token_valid_until > ?',

    if user
      # nullify the login token so it can't be used again
      user.update!(login_token: nil, login_token_valid_until: 1.year.ago)

      # sorcery helper which logins the user

      redirect_to root_path, notice: 'Congrats. You are signed in!'
      redirect_to root_path, alert: 'Invalid or expired login link'

  def destroy
    # sorcery helper which logouts the user

    redirect_to root_path, notice: 'You are signed out'

Congrats! You have a passwordless authentication

Final touches - UsersController and user views

Let's finish this article by actually doing something with the passwordless auth.

Update your UsersController which allows us to update the user name.

class UsersController < ApplicationController
  before_action :require_login, only: [:edit, :update]
  def index

  def edit

  def update
    current_user.update! user_params
    redirect_to root_path

  def user_params

Update views/users/index.html.erb

<% if current_user %>
  <h1><%= ? "My name is #{}" : "Input your name using the link below" %></h1>
  <%= link_to  "Edit current user", edit_user_path(current_user) %> <br>
  <%= link_to  "Sign out", sessions_destroy_url, method: :delete %> <br>
<% else %>
  <h1>Login to input your name</h1>
  <%= link_to  "Login to edit", logins_new_path %>
<% end %>

and finally views/users/edit.html.erb

<%= simple_form_for current_user do |f| %>
  <%= f.input :name, label: "Name" %>
  <%= f.submit "Update name" %>
<% end %>

To make sure that the authentication works, go to incognito and enter the edit page URL. You shouldn't be able to access the page unless you are logged in with the correct account.

Level up your web development skills

Get articles, guides and interviews right in your inbox. Join a community of fellow developers.

No spam. Unsubscribe at any time.

Full Stack Heroes logo

© 2020 Full Stack Heroes