The project is in a healthy, maintained state
Passwordless (email-only) login strategy for Devise
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
 Project Readme

Devise::Passwordless

A passwordless a.k.a. "magic link" login strategy for Devise

Features

  • No special database migrations needed - magic links are stateless encrypted tokens
  • Magic links are sent from your app - not a mounted Rails engine - so path and URL helpers work as expected
  • Supports multiple user (resource) types
  • All the goodness of Devise!

Installation

First, install and set up Devise.

Then add this gem to your application's Gemfile:

gem "devise-passwordless"

And then execute:

$ bundle install

Finally, run the install generator:

$ rails g devise:passwordless:install

See the customization section for details on what gets installed and how to configure and customize.

Usage

This gem adds a :magic_link_authenticatable strategy that can be used in your Devise models for passwordless authentication. This strategy plays well with most other Devise strategies (see notes on other Devise strategies).

For example, if your Devise model is User, enable the strategy like this:

# app/models/user.rb
class User < ApplicationRecord
  devise :magic_link_authenticatable #, :registerable, :rememberable, ...
end

Then, you'll need to set up your Devise routes like so to use the passwordless controllers to modify Devise's default session create logic and to handle processing magic links:

# config/routes.rb
Rails.application.routes.draw do
  devise_for :users,
    controllers: { sessions: "devise/passwordless/sessions" }
  devise_scope :user do
    get "/users/magic_link",
      to: "devise/passwordless/magic_links#show",
      as: "users_magic_link"
  end
end

Finally, you'll want to update Devise's generated views to remove references to passwords, since you don't need them any more!

These files/directories can be deleted entirely:

app/views/devise/passwords
app/views/devise/mailer/password_change.html.erb
app/views/devise/mailer/reset_password_instructions.html.erb

And these should be edited to remove password references:

  • app/views/devise/registrations/new.html.erb
    • Delete fields :password and :password_confirmation
  • app/views/devise/registrations/edit.html.erb
    • Delete fields :password, :password_confirmation, :current_password
  • app/views/devise/sessions/new.html.erb
    • Delete field :password

Customization

Configuration options are stored in Devise's initializer at config/initializers/devise.rb:

# ==> Configuration for :magic_link_authenticatable

# Need to use a custom Devise mailer in order to send magic links
require "devise/passwordless/mailer"
config.mailer = "Devise::Passwordless::Mailer"

# Time period after a magic login link is sent out that it will be valid for.
# config.passwordless_login_within = 20.minutes

# The secret key used to generate passwordless login tokens. The default value
# is nil, which means defer to Devise's `secret_key` config value. Changing this
# key will render invalid all existing passwordless login tokens. You can
# generate your own secret value with e.g. `rake secret`
# config.passwordless_secret_key = nil

# When using the :trackable module, set to true to consider magic link tokens
# generated before the user's current sign in time to be expired. In other words,
# each time you sign in, all existing magic links will be considered invalid.
# config.passwordless_expire_old_tokens_on_sign_in = false

To customize the magic link email subject line and other status and error messages, modify these values in config/locales/devise.en.yml:

en:
  devise:
    passwordless:
      not_found_in_database: "Could not find a user for that email address"
      magic_link_sent: "A login link has been sent to your email address. Please follow the link to log in to your account."
    failure:
      magic_link_invalid: "Invalid or expired login link."
    mailer:
      magic_link:
        subject: "Here's your magic login link ✨"

To customize the magic link email body, edit app/views/devise/mailer/magic_link.html.erb

Multiple user (resource) types

Devise supports multiple resource types, so we do too.

For example, if you have a User and Admin model, enable the :magic_link_authenticatable strategy for each:

# app/models/user.rb
class User < ApplicationRecord
  devise :magic_link_authenticatable # , :registerable, :rememberable, ...
end

# app/models/admin.rb
class Admin < ApplicationRecord
  devise :magic_link_authenticatable # , :registerable, :rememberable, ...
end

Then just set up your routes like this:

# config/routes.rb
Rails.application.routes.draw do
  devise_for :users,
    controllers: { sessions: "devise/passwordless/sessions" }
  devise_scope :user do
    get "/users/magic_link",
      to: "devise/passwordless/magic_links#show",
      as: "users_magic_link"
  end
  devise_for :admins,
    controllers: { sessions: "devise/passwordless/sessions" }
  devise_scope :admin do
    get "/admins/magic_link",
      to: "devise/passwordless/magic_links#show",
      as: "admins_magic_link"
  end
end

And that's it!

Messaging can be customized per-resource using Devise's usual I18n support:

en:
  devise:
    passwordless:
      user:
        not_found_in_database: "Could not find a USER for that email address"
        magic_link_sent: "A USER login link has been sent to your email address. Please follow the link to log in to your account."
      admin:
        not_found_in_database: "Could not find an ADMIN for that email address"
        magic_link_sent: "An ADMIN login link has been sent to your email address. Please follow the link to log in to your account."
    failure:
      user:
        magic_link_invalid: "Invalid or expired USER login link."
      admin:
        magic_link_invalid: "Invalid or expired ADMIN login link."
    mailer:
      magic_link:
        user_subject: "Here's your USER magic login link ✨"
        admin_subject: "Here's your ADMIN magic login link ✨"

Notes on other Devise strategies

If using the :rememberable strategy for "remember me" functionality, you'll need to add a remember_token column to your resource, as by default that strategy assumes you're using a password auth strategy and relies on comparing the password's salt to validate cookies:

change_table :users do |t|
  t.string :remember_token, limit: 20
end

If using the :confirmable strategy, you may want to override the default Devise behavior of requiring a fresh login after email confirmation (e.g. this or this approach). Otherwise, users will have to get a fresh login link after confirming their email, which makes little sense if they just confirmed they own the email address.

Alternatives

Other Ruby libraries that offer passwordless authentication:

License

The gem is available as open source under the terms of the MIT License.