Setting up authentication for your Ruby app is important to ensure it is secure. In this post, we’ll explore the rodauth-omniauth
gem for Ruby to implement authentication in your app via third-party providers.
First, let’s quickly define authentication before exploring the benefits of OmniAuth and setting up the rodauth-omniauth gem for a Rails app.
Let’s dive straight in!
What Is Authentication?
Authentication is the act of verifying that someone or something is who or what they say they are to grant them access to requested resources. So if user A
wants access to an application, they have to provide identification that is acceptable and confirmable by the system to be granted access.
Authentication can take many forms — for example:
- Providing a password that matches an account’s password as stored in an app’s database.
- Using facial recognition or fingerprints.
- The use of a code sent to a registered channel, like an email or phone number, or an authenticator app.
There are various descriptions of authentication — we have single-factor authentication
, two-factor authentication
, and multi-factor authentication
.
What Does OmniAuth Bring to the Table?
Authentication can be tiring for a user, especially when they have to constantly remember their various usernames and passwords for various applications. One of the solutions OAuth (Open Authentication) provides is to allow one service provider to authenticate a user using their identity with another service provider. This means that as a user, you only need to remember one username and password. Then every other application you need access to can delegate your authentication to your preferred OAuth provider.
Also, you do not need to give your password to the application that requires authentication. Instead, you are redirected to your provider, where you can be authenticated, and then redirected to the application you intend to access (thereby keeping your credentials safe and secure). Your provider, on the other hand, then shares the relevant information about you with the requesting application. Some common OAuth service providers are Google, Facebook, Twitter, and GitHub.
OmniAuth is a library that standardizes multi-provider authentication for web applications. It uses a strategy pattern for the different providers, and these strategies are generally released individually as Ruby gems. The rodauth-omniauth
gem:
Offers login and registration via multiple external providers using OmniAuth, together with the persistence of external identities.
Source: rodauth-omniauth GitHub repo
Setting Up rodauth-omniauth for a Rails Application
Let’s start by creating a new Rails project titled “rodauth_test_app”.
$ rails new rodauth_test_app
$ cd rodauth_test_app
It is important to note that the rodauth-omniauth gem is an extension to the rodauth gem, and as such, we need to have already set up Rodauth to use it. For a Rails app, a quick way to do that is to run the following commands:
$ bundle add rodauth-rails
$ rails generate rodauth:install
$ rails db:migrate
To have access to the views rodauth
provides, you can run either of the following commands:
$ rails g rodauth:views # to have access to all the views that rodauth provides
$ rails g rodauth:views login # to access only the login page which is what we need
The rails rodauth:routes
command gives us a list of the routes handled by the RodauthApp, making it possible for us to get to the different pages we’re interested in.
Among the tables created during our installation, the most important one to us is the Accounts
table. This is the table where all user accounts are stored and will be the reference for the AccountIdentities
table we will create.
Creating an Account Identities Table
A person can have a driver’s license, international passport, and voter’s card. All these means of identification point to the same person and can be used interchangeably.
Similarly, a user can have several means of identification (account identities), and this user may choose to log in at any time using any of these identities. That is why we’ll create an AccountIdentities
table to store all the identities of a specific user account, identified by a unique email address.
With a quick look in our db
folder at the ..._create_rodauth.rb
file, we find the schema for the accounts table as follows:
create_table :accounts do |t|
t.integer :status, null: false, default: 1
t.string :email, null: false
t.index :email, unique: true, where: "status IN (1, 2)"
t.string :password_hash
end
To create a table to store our account identities, we run the following command:
$ rails g migration CreateAccountIdentities
We find the generated file in the db/migrate
folder. Let’s go ahead and define its schema.
class CreateAccountIdentities < ActiveRecord::Migration[7.0]
def change
create_table :account_identities do |t|
t.references :account, null: false, foreign_key: { on_delete: :cascade }
t.string :provider, null: false
t.string :uid, null: false
t.index [:provider, :uid], unique: true
# timestamps are not included -> to be explained later
end
end
end
Within this table, we ensure the following:
- Every identity must belong to an account.
- When an account is deleted, all associated identities also get deleted.
- The provider must be declared (e.g., Google, Twitter, Facebook, GitHub).
- A
uid
must be present. - The combination of the provider and the
uid
must be unique to ensure that all account identities are unique for each provider.
It is also very important to have a homepage — a root route — to redirect users to after they are successfully authenticated. To ensure that, create a home controller with an index method that serves as the homepage.
$ rails g controller home index
In our config/routes.rb
file, we add the root route as follows:
Setting the OAuth Provider
We’ll use GitHub as the OAuth provider for this app, so install the GitHub OAuth gem and the rodauth-omniauth gem.
$ bundle add rodauth-omniauth omniauth-github
Having installed these gems, enable the omniauth feature and register your OAuth provider in the Rodauth configuration. The config file can be found at app/misc/rodauth_main.rb
.
configure do
# ...
enable :omniauth
omniauth_provider :github, ENV["GITHUB_CLIENT_ID"], ENV["GITHUB_CLIENT_SECRET"], name: :github
# name is only important here if it's different from the provider e.g. :google_oauth2 with name: :google
# ...
end
To get the client id and client secret for this app, we need to create the OAuth app and set the callback URL to {root_url}/auth/{provider}/callback
. In our case, this translates to https://localho