NoPassword
NoPassword is a Ruby on Rails gem that allows session authentication with a token or a magic link via an email sent to the user; there is no need for a password.
Table of contents
- Requirements
- Installation
- Configuration
- Initializer
- Locales
- Email configuration
- Custom views
- Usage
- Action filter
- Helper methods
- Callback
- Generators
- Develooment and tests
- License
Requirements
NoPassword is a Rails Engine that needs Ruby on Rails 7.0 or better and Ruby 3.0.2 o better. NoPassword flow is composed of two views for the user to enter an email, and the received token. The engine uses TailwindCSS via tailwindcss-rails and StimulusJS served with the browser's Importmaps.
Customization for CSS and Javascript is possible; refer to Views personalization to learn how to extract gem templates.
Installation
Add NoPassword gem reference to your application's Gemfile
.
gem "no_password_auth", require: "no_password"
Or add gem reference from its Github repository.
gem "no_password_auth", git: "https://github.com/creditario/nopassword.git", require: "no_password"
In any case, execute bundle
to install the gem.
Next, use the gem installer to install migrations and the gem initializer.
$ bin/rails no_password:install
Don't forget to execute database migration after this step.
$ bin/rails db:migrate
The following section explains configuration options for the NoPassword gem with its initializer file.
Configuration
After installing the NoPassword gem, the engine is mounted at the /p
path inside the config/routes.rb
file.
Initializer
After installing the NoPassword gem, the engine is mounted at the /p
path inside the config/routes.rb
file.
NoPassword initializer is located at config/initializers/no_password.rb
. The gem provides three configuration options. Session expiration by default is set to two hours after a new session is started. To adjust this time, use session_expiration
attribute to set a new value.
Token expiration is set by default to 15 minutes. Update token_expiration
increase or decrease this time.
secret_key
is the key used to sign magic links URLs; if this value is nil
, then Ruby on Rails secret_key
is used for this cryptographic task.
NoPassword.configure do |config|
# Session expiration time
# config.session_expiration = 2.hours
#
# Token expiration time
# config.token_expiration = 15.minutes
#
# Secret key to cypher tokens, if none, then Rails secret key is used
# config.secret_key = nil
end
Locales
NoPassword gem includes locales for Spanish and English languages. Set your language configuration at the main application config/application.rb
.
config.i18n.default_locale = :es
config.i18n.available_locales = [:en, :es]
Email configuration
NoPassword uses ActionMailer to email the user its token and magic link. If a background strategy is setup for ActiveJob, like Sidekiq then emails are sent in the background.
Remember to configure default_url_options
and asset_host
inside config/enviroments/production.rb
file.
config.action_mailer.default_url_options = { host: "my-domain.com" }
config.action_mailer.asset_host = "https://my-domain.com"
In development mode, having a tool like letter opener helps to open emails inline, preventing sending emails. Add letter opener gem reference to your Gemfile
and run bundle
to install it.
gem "letter_opener", group: :development
Configure the letter opener by opening config/enviroments/development.rb
and adding the following configuration.
config.action_mailer.delivery_method = :letter_opener
config.action_mailer.perform_deliveries = true
Custom views
This step is necessary only if you plan to customize the views provided by NoPassword. The following generator copies all engine views into views/no_password
and views/layouts/no_password
.
$ rails no_password:install:copy_templates
Usage
After installing NoPassword, you can use the filters and helpers provided by the gem to query if there is an active session or to send the user to the sign in process.
Action filter
The filter authenticate_session!
is defined when the module NoPassword::ControllerHelpers
is included in a controller. This filter is used with a before_action
to secure a controller or controller action. When a user reaches a controller protected by this filter, and there is no active session, the user is redirected to the sign-in process.
include NoPassword::ControllerHelpers
before_action :authenticate_session!
Helper methods
The module NoPassword::ControllerHelpers
includes methods signed_in_session?
and current_session
to query if there is an active session or to get more information about an active session. These methods are also exposed as helpers in the Rails view context.
<% if signed_in_session? %>
<%= current_session.email %>
<%= button_to "Sign out", no_password.session_path(current_session.id), method: :delete %>
<% else %>
<%= link_to "Sign in", no_password.new_session_path %>
<% end %>
Callback
NoPassword sign in flow has two pages; the first one receives the user email, creates a token and a magic link, and then sends it via email. The second page accepts the token and decides whether the user can have a session.
However, an application may require a different flow, which is why the callback after_sign_in!
exists.
This is an example of a custom flow that mimics a Single Page flow.
The callback is called on every intent to start a session, whether the sign was successful or not.
after_sign_in!(current_session, by_url)
It receives two parameters.
-
current_session
: An object that represents the active session. -
by_url
: A boolean value that indicates if the login happens with the magic link or entered token manually.
The SessionConfirmationsController
controller expects any of the following possible values from the callback.
-
nil
: indicates callback was executed but is returning flow control to the controller. -
redirect path
: it is a string path that indicates callback was executed and want to redirect to specific path.
after_sign_in!
callback is implemented by creating a app/controllers/no_password/session_confirmations_controller.rb
file in your application. The original controller from NoPassword engine is loaded, and then the callback is added with a class_eval
.
load NoPassword::Engine.root.join("app", "controllers", "no_password", "session_confirmations_controller.rb")
NoPassword::SessionConfirmationsController.class_eval do
def after_sign_in!(current_session, by_url)
return do_something_different if signed_in # Do something different if user signed in successfully
return nil if !by_url # Return control if failed to sign in with magic link
flash[:alert] = "Your code is not valid"
main_app.demo_path # Redirect somewhere else if token is invalid
end
end
Generators
NoPassword gem has four Ruby on Rails generators. The first one is the installer which performs the following actions.
- Creates the gem initializer.
- Mount the engine in the routes file.
- Adds the gem Helpers into
ApplicationController
. - Copy NoPassword migrations to the application.
- Generates the Tailwind config file and generates de CSS for NoPassword.
$ rails no_password:install
copy_templates
generator copies gem templates to the application to allow views customization. Files are copied to app/views
inside a no_password
folder.
$ rails no_password:install:copy_templates
migrations
generator copies the gem migrations to the application.
$ rails no_password:install:migrations
tailwindcss::build
and tailwindcss:watch
generators work together to generate de Tailwind CSS for gem views. Both generate a tailwind.config.js
file that needs to be added to .gitignore
file.
$ rails no_password:tailwindcss:build
tailwindcss::build
generator connects automatically to the assets compilation process in production to generate the Tailwind CSS for the gem.
$ rails no_password:tailwindcss:watch
tailwindcss:watch
generator is used in development to generate the Tailwind CSS for the gem. Add it to Procfile.dev
file to ensure that CSS is generated in development when using Foreman or Overmind.
no_password_css: bin/rails app:no_password:tailwindcss:watch
Development and tests
A development environment for this gem is easy to create, clone the repository and run the script bin/setup
to install dependencies and prepare a database (Postgresql). The script assumes that you have Overmind installed, which used the Procfile.dev
to start the sample application on port 3090.
$ bin/setup
To execute the gem tests, use the script bin/ci
, which also runs the linters.
$ bin/ci
License
The gem is available as open source under the terms of the MIT License.