Essential Guide 11: Authentication

Demo: Matestack Demo Github Repo: Matestack Demo Application

Welcome to the eleventh part of our tutorial about building a web application with matestack.

Introduction

Our app looks great after finishing the previous guide. To make it more of a real-world example, we add a private area, which is only accessible for logged in admins.

In this guide, we will

  • install and set up the devise gem

  • add a second matestack app for our private administration area

  • move some of the CRUD functionality into the private admin app

  • add a link to the administration area in our demo app

Prerequisites

We expect you to have successfully finished the previous guide.

Setting up Devise

For authentication we use the popular library devise. To install it, we add gem 'devise' to our Gemfile and run bundle install afterwards. To finish the devise installation we run rails generate devise:install.

Then we need to add

in config/environments/development.rb.

Generating our admin model

After a successful setup we can now generate our admin model. Our admins don't need any extra data, so it's enough to run devise generator and specify admin as a name for our model.

Finally, we need to migrate the database by running

and save our changes to Git via

Adding admin app, controllers and routes

Our demo app is responsible for all pages available to everyone, but some actions should only be available by admins. This is one reason to create a seperate admin app. The other reason is that we want another layout for our admin app. Therefore we create a second matestack app under the Admin namespace in app/matestack/admin/app.rb.

In our Admin::App we call a custom notifications component which we will now create under app/matestack/admin/components/notifications.rb. Why do we this time put our components folder inside our admin folder? Because the notifications component should only be used in the admin context and we therefore store components that should only be used in the admin context inside the admin folder. We even create another registry in app/matestack/admin/components/registry.rb to seperate the two contexts better.

While it's similar to the Demo::App, the Admin::App does have some differences. Notice how we hide a part of the navigation if no admin is currently signed in, making use of a Devise helper:

There is also a logout button, using an action component.

We could now use the Admin::App as layout, but we need to set it with matestack_app in the corresponding controller and we need to include our new registry with include Admin::Component::Registry.

Let's create our routes for our admin area and after it the controllers we referred to in our routes.

app/matestack/controllers/admin/persons_controller.rb

Notice the include of our registry and the call for setting our matestack app. Now that we have our admin persons controller and admin app we can create our different pages.

Admin persons index, edit, new pages

First we create an index page in app/matestack/admin/pages/persons/index.rb, which shows all persons in a table. To implement it we use the collection component.

Now we create the new and edit pages. Like we did earlier in our demo page, we create a form page which will contain a form partial, because both views will use the same view. By excluding it in a partial in a form page, both new and edit can inherit from it and reuse our form.

app/matestack/pages/persons/form.rb

Take a closer look at the form group partial here. Every form element has the same wrapping elements and the same label with its classes. In order to not repeat ourselfs and write less code we can use a partial, which takes a label text and a block and renders the wrapping elements, label and yields the block. Our form code therefore looks much cleaner now and we kept it DRY (don't repeat yourself).

app/matestack/pages/persons/new.rb

app/matestack/pages/persons/edit.rb

Nothing special for our new and edit page. Both inheriting from the form page, overwriting the form config and using the form partial.

With the pages we added the possibility to maintain the persons as admin, by editing, deleting existing ones or creating new ones.

But we still need a login page. Go ahead and add it in app/matestack/admin/pages/sessions/sign_in.rb with this content:

Admin Controllers

Now that we have our pages, we will need to update our persons controller

Update it accordingly:

We added all required actions according to our routes. Did you notice our controller now inherits from Admin::BaseController? We need to create it in the next step, but why did we create one? Because all routes and corresponding actions belonging to the admin should not be visible without logging in as admin. Therefore we implement a before_action hook which calls devise authenticate_admin! helper, making sure that every action can only be called by a logged in admin. We could do this in our persons controller, but as we might add other controllers later they only need to inherit from our base controller and are also protected. Let's create the base controller in app/controllers/admin/base_controller.rb.

In order for devise to use our sign in page, we need to create a custom session controller. We also specify a path admins should be redirected to after sign out.

See below on how to create the session controller for devise.

app/controllers/admin/sessions_controller.rb

We also need to create a custom failure app to return the expected response for login attempts with wrong credentials.

lib/devise/json_failure_app.rb

And tell devise to use it by requiring it in the initializer and updating the config. We also need to update the sign_out_via configuration parameter to use http GET requests for sign out instead of DELETE.

config/intializers/devise.rb

Finally remember to update the routes in order to tell devise to use the correct controller.

config/routes.rb

If you want more information on why these changes and configurations are necessary take a look at our devise guide.

But if you try to start your application locally, visiting the admin pages doesn't work yet - what's going on?

Styling, Layout & JavaScript

Notice that we added layout 'administration' inside our admin controllers. This looks for file called administration.html.erb in app/views/layouts/, which we need to create. Add the following content to it:

In there, we reference a different javascript_pack_tag(that is, 'administration') than in our application.html.erb layout (which uses 'application') - so we need to set it up via Webpacker!

Add a new file in app/javascript/packs/administration.js, with the following content:

When you compare it to app/javascript/packs/application.js which sits right next to it, you will see that the two diverge in one point: The admin pack imports 'css/custom-admin-bootstrap', so we need to create it.

Here's the custom-admin-bootstrap.scss which you need to add in app/javascript/css/. It looks very familiar because it is. There's not much difference between it and our custom-bootstrap.scss. But we may want to theme our admin app differently than our demo app, so in case we can by just changing the colors in this file for example.

Updating the notifications component

As you might saw or experienced, our admin person edit page doesn't really do anything if we update a user. Thats because we have no show page to which we would normally transition. If we transition to the edit page, you wouldn't see any difference and therefore will not know if the update was successfull. Here comes our notification component in handy. We will implement bootstrap alerts popping up showing success or failure messages by using the toggle component in combination with the form success: { emit: '...' } and failure: { emit: '...' } configuration options. As you can see above we emit a person_form_success or person_form_failure in our edit and new forms depending on the result. We therefore can use a toggle component with these events. Let's update our notification component to do that.

We again created a partial which will take a few parameters and a block in order to wrapp the block inside a bootstrap alert. We use a toggle component with show_on to display the alert when the corresponding event is triggered and use hide_after to hide it after a 3000ms automatically. The .alert-wrapper was styled so it appears in the right corner beneath the navigation.

When we now edit a user we will see an alert in the corner communicating the status of the update.

We also added a notification for our earlier defined login_failure event of our sign in page in order to communicate a failed login attempt.

After creating the Admin::App, let's cut some functionality from the Demo::App and make it exclusively available to a logged-in admin!

Remove the following lines from both app/matestack/demo/pages/edit.rb and app/matestack/demo/pages/new.rb:

Also, remove :role from the person_params in app/controllers/persons_controller.rb - if we forget to do that, a sophisticated, not-logged-in user still could edit roles through sending requests directly to our backend!

Since any page visitor can now create new person records in the database but only admins can edit the :role attribute, let's add a default value through a migration:

Changing the default is quite straightforward:

And, to make it work, we need to migrate the database by running

Finally, add a link to the sign_in page to the navigation partial in app/matestack/demo/app.rb:

To finish things off, let's add the recent changes to Git via

and commit them by running

More information on Devise & Matestack

What exactly is going on under the hood with all the admin sign in stuff, you may wonder?

Here's a quick overview: Instead of implementing loads of (complex) functionality with a load of implications and edge cases, we use the Devise gem for a rock-solid authentication. It takes care of hashing, salting and storing the password and managing session cookies. All that's left for us to do is check for authentication of admins by using the authenticate_admin! helper.

Devise could do a lot more, but as this is a basic guide, we will leave it with that. For even more fine-grained control over access rights (authorization) within your application (e.g. by introducing a superadmin or having regional and national manager roles) we recommend you take a look at two other popular gems: Pundit and CanCanCan.

If you want to know more about using devise with matestack, checkout our devise guide.

Creating a admin

In order to use the admin app we need to create an admin with credentials which we can now use to sign in.

Recap & outlook

By adding a working authentication functionality and an admin app protected via a login, our project now much better resembles a real-world software application! On the way, we covered some advanced topics like authentication via the Devise gem, serving different JavaScript packs using Webpacker and Rails layouts. We leared how to structure components and pages with different namespaces and how to use different registries.

While the application is good as it is right now, go ahead and check out the next part of this guide where we will deploy our application to heroku.

Last updated

Was this helpful?