Implementing Magic Links in Rails

Implementing Magic Links in Rails
Photo by Artem Maltsev / Unsplash

Magic Links are links that provide instant sign-in without entering a password.

You may have encountered a website that only asks for your email. They will send you an email with a link that you will use to authenticate and access the site.

What solutions are out there?

If this is something that you'd like to implement, naturally you'd see what tools and libraries are available for this service.

There are tools out there like:

Magic: Future-proof passwordless authentication
Fast, secure passwordless login for your app or website. One SDK for email magic links, social login, WebAuthn, and more. Free to get started, ready in minutes.

and gems like:

GitHub - mikker/passwordless: 🗝 Authentication for your Rails app without the icky-ness of passwords
🗝 Authentication for your Rails app without the icky-ness of passwords - GitHub - mikker/passwordless: 🗝 Authentication for your Rails app without the icky-ness of passwords

They provide capabilities to do magic links, however executing magic links is really not a complicated feature and something you can pull off without dependency on any external tools.

GlobalID

GlobalID is a tool that's included in ActiveRecord that easily allows you to identify objects using an identifier.

GitHub - rails/globalid: Identify app models with a URI
Identify app models with a URI. Contribute to rails/globalid development by creating an account on GitHub.

Here's an example of its usage:

These signed global IDs are set to expire in 1 month by default, however you can set the expiration to your liking.

Furthermore, you can scope the usage of the SGID by setting the :for option.

How to implement

To implement this feature on your app, you need to do 2 things:

  1. Add a controller action that will handle the magic link.
  2. Create an email that will send out the magic link.

1. Magic link handler

In one of your controllers (ideally sessions_controller.rb), you will create an action that will look similar to below that will retrieve the sgid from params.

def magic_link
  sgid = params.require(:sgid)

  user = GlobalID::Locator.locate_signed(sgid, for: 'login')

  if user.nil? || !user.is_a?(User)
    redirect_to root_path
  else
    sign_in(user)
    redirect_to root_path
  end
end

Also remember to add a route for this action!

2. Create the magic link for the email

Create an SGID for the user. You can optionally set the :expires_in and :for arguments.

user.to_sgid(expires_in: 30.days, for: 'login').to_s

In your email, you set the button's href to your controller action with the sgid as a param.

magic_link_sessions_url(sgid: sgid)

And that's it!

With minimal lines of code, you have implemented magic links for your application without any reliance on external libraries.

Go Rails!