Skip to content

[rails, bcrypt, authentication, authorization, auth]

Notifications You must be signed in to change notification settings

SF-WDI-LABS/rails-auth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 

Repository files navigation

Auth in Rails

Why is this so dang important?

This workshop is important because:

Many apps track user data and allow users to sign up, log in, and log out. Today we'll look at a strategy to implement these features.

What are the objectives?

After this workshop, developers will be able to:

  • Build routes, controllers, and views for user sign up, log in, and log out.
  • Implement a User model that securely stores passwords with has_secure_password.
  • Differentiate authentication and authorization.

Where should we be now?

Before this workshop, developers should already be able to:

  • Describe the role of routes, controllers, views, and models.

Aside: Devise

Some of you have heard of Devise, a gem that will handle auth for you. That's cool and all, but the Devise docs recommend that if you're new to rails:

If you are building your first Rails application, we recommend you do not use Devise. Devise requires a good understanding of the Rails Framework. In such cases, we advise you to start a simple authentication system from scratch. ... (docs)

That's what we're going to do!

What is "auth"?

People generally mean two different things when they say "auth":

  1. Authentication - confirming that a user is who they say they are.
  2. Authorization - determining whether a user is permitted to perform an action.

Auth Strategy: Sessions

HTTP is designed to be stateless. We could have users submit login details for every request, but that would get really tedious really quickly.

We'll store information about the logged in user in a session. (See Rails Security Guide Sessions section for a refresher.)

Below is a summary of what we will use:

Routes

  get '/login', to: 'sessions#new'
  post '/login', to: 'sessions#create'
  get '/logout', to: 'sessions#destroy'

  get '/signup', to: 'users#new'
  post '/users', to: 'users#create'

Models

We'll need one model: Users. We'll add name, email and password_digest attributes.

# Table name: users
#
#  id              :integer          not null, primary key
#  name            :string
#  email           :string
#  password_digest :string

Handling Passwords

We'll use the gem bcrypt so that we never store user passwords as plain text. BCrypt is a hashing algorithm, and people like it because you can customize how secure a job it does based on a "cost" factor. The bcrypt gem will let us simply and easily hash and salt our passwords to obscure them with the BCrypt algorithm. Let's try it out.

Independent Practice: What does bcrypt do?

  1. In your Terminal, install the bcrypt gem: gem install bcrypt.

  2. Start up irb or pry, and try out the console commands below:

  2.3.0 :001 > require 'bcrypt'
   => true
  2.3.0 :002 > my_password = BCrypt::Password.create('secret')
   => "$2a$10$7z71iDWik1luWbDR.2uC2ObB5fPT5jSbWFfsp3YGnsZrcRIHIkjne"
  2.3.0 :003 > my_password == "secret"
   => true
  2.3.0 :004 > my_password == "oops"
   => false
  1. Play around with different values. What happens if you run "secret" through the bcrypt gem again? Do you get the same output?

has_secure_password

In Rails, the bcrypt gem gives us an intensely easy-to-use has_secure_password method that we can add to a class we want to authenticate with.

class User < ActiveRecord::Base
  # first make sure bcrypt is included in Gemfile
  has_secure_password

end

This will make sure the passwords are NOT stored as plain text in the database. It will store the password in the database under the column password_digest.

It also adds an authenticate method which can check a given password against the hashed and salted version in the database:

  # some controller somewhere
  user = User.find_by_email(params[:email])
  if user && user.authenticate(params[:password])
    # ...

Note that we could use bcrypt to write these methods ourselves - in fact, that's exactly what the source code does! See has_secure_password's documentation and source code.

Session Store for User ID

Whenever we authenticate a user, we will set the session hash's user_id to the user.id:

session[:user_id] = user.id

You'll want to do this at signup and log in.

Check for understanding: How would we use the session hash to log a user out?

Authorization

You've seen authentication, but for many apps you'll still want to add authorization.

  • We suggest making a current_user method in your ApplicationController or in a helper file so that each controller doesn't need to implement a way to check which user is logged in.
  def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
  end
  helper_method :current_user
  • Then, in each of your controllers, check whether the current_user should be allowed to do the action they're trying to. You can use before_action to keep this code DRYer.
  def authorize
    redirect_to '/login' unless current_user
  end
  • Consider adding conditionals to your views. For example, don't show links to users who can't use them.

Conclusion

That's it, authentication and authorization in a nutshell. Not too bad, right?

For an example, see a WDI lab solution implementing auth in Rails 5.

Alternative Strategies

Local Authentication Gems

There are a lot of gems that help implement authentication. For Rails, Devise is a very popular gem that will handle your site's authentication for you. Cancancan is a popular authorization gem. Some "local auth" strategies don't use sessions at all - you could explore JSON Web Tokens.

Third Party Authentication and Authorization Gems

Sometimes authenticating with your site isn't enough - sometimes you want data that another organization controls. Here, you're getting into third-party authorization strategies like OAuth (which comes in flavors: 1 or 2). If your Rails app needs data from a third-party API, you could check:

  • have the API creators provided an official gem for Rails developers to use?
  • is there an OmniAuth strategy for the API?
  • do I want to try building more myself with tools like Oauth2 gems?
OAuth 2 Authorization

OAuth 2 is a framework for third-party authorization - allowing one application to access data from another application, on a user's behalf.

In the real world, this might correspond to me walking into a bank and telling the teller you said I could have some of your money every month. We'd hope the teller would be skeptical. Maybe they'd ask that you come into the bank yourself and authorize me to withdraw some specific amount of money on a specific schedule. They'd probably want a copy of my id so they could verify it was me coming back to withdraw money each time. If everything checked out, the bank would give me a special token or passphrase that I could use to get money now and smooth out the transaction now and for some specified amount of time.

On the web, a classic example is an application that aggregates information stored elsewhere. Say we have a user named Sam. Sam wonders: do the times I commit to github have any relationship to the times I'm most active on facebook? If Sam wants to use a GitFace app that promises to answer that question, he might have to log into his facebook account and github account so the app can access and process his data.

Sam may not want to give GitFace his login information for both of those sites, so instead GitFace makes an arrangement with github and facebook. Let's consider facebook. When GitFace needs to access restricted data, it will link Sam to a special authorization page the app has set up with facebook. This page is entirely controlled by facebook - and it relies on GitFace's facebook app id as well as the specific information GitFace is requesting. Sam will enter in his information in a form on that page.

GitFace never interacts directly with Sam's login information. If Sam is authenticated and agrees to share the resource GitFace needs, facebook's server sends back a response that redirects Sam back to the GitFace app. The response also includes a special code specific to the data GitFace has requested.

In the background, GitFace then sends a new request to facebook - it needs to convert Sam's permission code into a token. In order to get the code converted, GitFace also needs to securely identify itself to facebook by telling facebook its client secret.

If the permission code and client secret check out, facbook issues a token that GitFace can use to access the materials approved by Sam. This usually has some expiration time so that the user doesn't have to re-authenticate on every step.

About

[rails, bcrypt, authentication, authorization, auth]

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published