Adding Magic Links to Rails 8 Authentication
This article was originally publish on Build a SaaS by Rails Designer Let me cut to the chase: I am not a fan of “passwordless” or “magic links”. It's slower, annoying and prone to pull you out of the zone (because that one email suddenly needed an answer). No, for me, a password manager is at least one factor faster. But, I am self-aware enough, to know that I am not the average web-app user. Also they're simpler to implement than you might think, and they solve a bunch of common authentication headaches for the common internet user. This article builds on top of basic Rails 8 authentication. See all the previous commits in this repo. First, you'll need a simple signup form. Here's what that looks like: Nothing fancy—just an email field and a submit button. The invisible_captcha is there to keep the bots away (more on that in my spam prevention article). The real magic happens in the MagicSignin class: # app/models/magic_signin.rb class MagicSignin AUTO_GENERATED_PASSWORD = SecureRandom.hex(32) include ActiveModel::Model include ActiveModel::Attributes attribute :email_address, :string validates :email_address, presence: true validates :email_address, is_not_spam: true def save return unless valid? User.where(email_address: email_address).first_or_create.tap do |user| if user.new_record? user.password = AUTO_GENERATED_PASSWORD user.save create_workspace_for user end send_magic_link_to user end end end This class handles both new signups and existing users. If it's a new user, it creates an account with a random password (they'll never need it) and sets up their workspace. Either way, it sends them a magic link. Speaking of magic links, you'll need to add token support to your User model: # app/models/user.rb class User < ApplicationRecord + + generates_token_for :signin, expires_in: 5.minutes end This gives you secure, time-limited tokens for your magic links. Five minutes is usually enough time for users to click the link in their email. The magic link email itself is straightforward: # app/mailers/magic_signup_mailer.rb class MagicSignupMailer

This article was originally publish on Build a SaaS by Rails Designer
Let me cut to the chase: I am not a fan of “passwordless” or “magic links”. It's slower, annoying and prone to pull you out of the zone (because that one email suddenly needed an answer). No, for me, a password manager is at least one factor faster. But, I am self-aware enough, to know that I am not the average web-app user. Also they're simpler to implement than you might think, and they solve a bunch of common authentication headaches for the common internet user.
This article builds on top of basic Rails 8 authentication. See all the previous commits in this repo.
First, you'll need a simple signup form. Here's what that looks like:
<%# app/views/magic_signups/new.html.erb %>
<%= form_with model: @signup, url: magic_signups_path do |form| %>
<%= form.email_field :email_address,
required: true,
autofocus: true,
autocomplete: "username",
placeholder: "Enter your email address" %>
<%= invisible_captcha %>
<%= form.submit "Sign up" %>
<% end %>
Nothing fancy—just an email field and a submit button. The invisible_captcha
is there to keep the bots away (more on that in my spam prevention article).
The real magic happens in the MagicSignin
class:
# app/models/magic_signin.rb
class MagicSignin
AUTO_GENERATED_PASSWORD = SecureRandom.hex(32)
include ActiveModel::Model
include ActiveModel::Attributes
attribute :email_address, :string
validates :email_address, presence: true
validates :email_address, is_not_spam: true
def save
return unless valid?
User.where(email_address: email_address).first_or_create.tap do |user|
if user.new_record?
user.password = AUTO_GENERATED_PASSWORD
user.save
create_workspace_for user
end
send_magic_link_to user
end
end
end
This class handles both new signups and existing users. If it's a new user, it creates an account with a random password (they'll never need it) and sets up their workspace. Either way, it sends them a magic link.
Speaking of magic links, you'll need to add token support to your User model:
# app/models/user.rb
class User < ApplicationRecord
+
+ generates_token_for :signin, expires_in: 5.minutes
end
This gives you secure, time-limited tokens for your magic links. Five minutes is usually enough time for users to click the link in their email.
The magic link email itself is straightforward:
# app/mailers/magic_signup_mailer.rb
class MagicSignupMailer < ApplicationMailer
def magic_link(user)
@user = user
mail to: @user.email_address
end
end
With a simple template:
<%# app/views/magic_signup_mailer/magic_link.html.erb %>
Here is your magic link: <%= magic_session_url(@user.generate_token_for(:signin)) %>
(of course you want to extend the message in the email a bit—we are not savages!