Now that we’ve learned the basics of authenticating with Bcrypt and Devise, let's discuss authorization. Authentication is the process of logging in a user while authorization is the process of determining which resources a user can access.
For instance, if we only want admins to be able to add, edit and delete new products in our site, we need to ensure only admins have access to these routes. Likewise, if we have content on our site that’s only for registered users, we need to protect that content from users that aren’t logged in.
Let’s add some basic authorization to our application. Note that you can also apply these authorization techniques to Bcrypt as well.
Let’s say our website includes routes that should only be made available to users that are logged in. For instance, a user might need to be logged in to add comments to an article. However, all users should be able to view comments, regardless of whether they’re authenticated. We could add this code to our comments controller to protect only the new comment route:
class CommentsController
before_action :authenticate_user!, :only => [:new]
end
This code will ensure users are authenticated before they can add comments. before_action
is an example of a filter. Filters are methods that are used when a controller action is called. In our case, we want the filter to run before the controller action is called so we can prohibit users from accessing a route. Check out the Rails documentation for more information on filters: Rails Documentation: Filters.
We could also create a filter that looks like this:
before_action :authenticate_user!, :except => [:index]
This allows all users to access the index route, but only authenticated users could access other routes in the comments controller.
Note that :authenticate_user!
is a method provided by Devise, so if you're using Bcrypt, you'll have to create your own custom method. You'll still be able to use before_action
, though.
On the other hand, let’s say only the index page of our site should be available to users that aren’t logged in. Now we want our filter to apply to all controllers, not just the comments controller. It would make more sense to move our before_action
to the application controller:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :authenticate_user!
end
Since all other controllers inherit from the application controller, this code will apply to all controllers.
We can now make exceptions where necessary, such as for our index route:
class HomeController < ApplicationController
skip_before_action :authenticate_user!, :only => [:index]
def index
end
end
Here we specify that we should skip the authenticate_user!
filter that lives in our application controller, but only for the index route.
We now have a way to determine whether logged-in users can access content on our site. There’s only one problem. We don’t want all users to access routes that should only be available to admins. For instance, while our users can add new reviews to a commerce site, they shouldn’t be able to add new products. Here's some sample code that will protect our new and edit routes. It looks like the code from our Bcrypt lesson.
ProductsController < ApplicationController
before_action :only => [:new, :edit] do
redirect_to new_user_session_path unless current_user && current_user.admin
end
end
This filter states that users should be redirected to the login page unless they’re both logged in (current_user
) and an admin. If the current_user
snippet isn’t included, the page will have an error when unauthenticated users try to log in because current_user
will be undefined.
Now our routes are protected, but we may still have content in our views that should only be accessible to certain users. For example, on our index page (which is accessible to all users), we might want to add a link to add new products (which should only be accessible to admins).
We can handle this by adding a conditional to the view. Here’s an example:
<% if current_user && current_user.admin %>
<%= link_to “Add product", new_product_path %>
<% end %>
If the current_user
is an admin, the link will show. If not, the link won't be on the page.
Remember, this will only hide content in the view. It won’t actually protect the route itself. If the route isn’t protected, a user can still enter the url /products/new and reach the new product page.
before_action :authenticate_user!, :only => [:new]
before_action :authenticate_user!, :except => [:index]
before_action :only => [:new, :edit] do
redirect_to new_user_session_path unless current_user && current_user.admin
end
<% if current_user && current_user.admin %>
Admin content goes here!
<% end %>
Lesson 12 of 27
Last updated July 14, 2022