Skip to content

markdenardo/login_required_readme

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Login Required Readme

Overview

Sometimes you want to require that a user is logged in to access a route. Here's how.

Objectives

  1. Restrict a route to logged in users.
  2. Skip a filter for particular controller actions.

First pass: manual checks

Let's say we have a DocumentsController. Its show method looks like this:

def show
  @document = Document.find(params[:id])
end

Now let's add a new requirement: documents should only be shown to users when they're logged in. From a technical perspective, what does it actually mean for a user to log in? When a user logs in, all we are doing is using cookies to add their :user_id to their session.

The first thing you might do is to just add some code into DocumentsController#show:

def show
  return head(:forbidden) unless session.include? :user_id
  @document = Document.find(params[:id])
end

The first line is a return guard. Unless the session includes :user_id, we return an error. head(:forbidden) is a controller method that returns the specified HTTP status code—in this case, if a user isn't logged in, we return 403 Forbidden.

Refactor

This code works fine, so you use it in a few places. Now your DocumentsController looks like this:

class DocumentsController < ApplicationController
  def show
    return head(:forbidden) unless session.include? :user_id
    @document = Document.find(params[:id])
  end

  def index
    return head(:forbidden) unless session.include? :user_id
  end

  def create
    return head(:forbidden) unless session.include? :user_id
    @document = Document.create(author_id: user_id)
  end

  def update
    return head(:forbidden) unless session.include? :user_id
    @document = Document.find(params[:id])
    # code to update a document
  end
end

That doesn't look so DRY. I really wish there were a way to ask Rails to run a check before any controller action.

Fortunately, Rails gives us a solution: before_action. We can refactor our code like so:

class DocumentsController < ApplicationController
  before_action :require_login

  def show
    @document = Document.find(params[:id])
  end

  def index
  end

  def create
    @document = Document.create(author_id: user_id)
  end

  private

  def require_login
    return head(:forbidden) unless session.include? :user_id
  end
end

Let's look at the code we've added:

before_action :require_login

This is a call to the ActionController class method before_action. before_action registers a filter. A filter is a method which runs before, after, or around, a controller's action. In this case, the filter runs before all DocumentsController's actions, and kicks requests out with 403 Forbidden unless they're logged in.

Skipping filters for certain actions

What if we wanted to let anyone see a list of documents, but keep the before_action filter for other DocumentsController methods? We could do this:

class DocumentsController < ApplicationController
  before_action :require_login
  skip_before_action :require_login, only: [:index]

  # ...
end

This class method,

skip_before_action :require_login, only: [:index]

Tells Rails to skip the require_login filter only on the index action.

Resources

View Login Required Readme on Learn.co and start learning to code for free.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published