Authentication in-rails

42
Social Testimonial Marketing Ruby on Rails Authentication Mihir Vaidya

description

Slides from my talk at the Eastside Incubator's Rails Chat series.With so many authentication solutions out there (Devise, OmniAuth, AuthLogic, just to name a few), this slide deck goes through various options, and guides with choosing the best authentication solution for your app. The deck covers following areas...Your Own Auth (Authentication from Scratch)Your Own Auth With Facebook ConnectOmniAuth (Facebook + Twitter)OmniAuth (Facebook + Twitter + Identity)Devise (+ Omniauthable, example includes Facebook and Twitter)All source code for this talk is available on GitHub at https://github.com/mvaidya/Authentication-In-Rails

Transcript of Authentication in-rails

Page 1: Authentication in-rails

Social Testimonial Marketing

Ruby on Rails Authentication

Mihir Vaidya

Page 2: Authentication in-rails

About

Mihir A. VaidyaCo-Founder and V.P. EngineeringReadyPulse

https://www.linkedin.com/in/vaidyamihir

https://twitter.com/mihirvaidya

V.P. EngineeringDec 2011 - now

Software EngineerAugust 2010 – Dec 2011

Software EngineerFeb 2006 – August 2010

Software EngineerMay 2004 – Feb 2006

ResearcherMay 2003 – May 2004

Experience Technologies

Page 3: Authentication in-rails

Experience with Rails

• Started dabbling as a hobby in 2008

• Rails 2.3.2 – ReadyPulse (v1)

• Rails 3.2 – ReadyPulse (current)

Page 4: Authentication in-rails

Authentication – What is it? Do I need it?• Identify who is visiting your application– With Trust– And potentially give a customized

experience

Page 5: Authentication in-rails

Big Decision

• What Authentication Strategy should I use?– Manage My Own User Profiles? (database driven)– OAuth?

• Which provider should I pick?– Facebook– Twitter– Google– GitHub– Passport– …

– Hybrid?• Maintain Identities?

Page 6: Authentication in-rails

More Decisions

• Should I use an Authentication Library?• If yes, which one?– https://www.ruby-toolbox.com/categories/rail

s_authentication• What are the benefits of one library over the

other?• How hard is it to build my own

Authentication?• Does Rails provide something native?

Page 7: Authentication in-rails

Rails 3.1 – HTTP Basic Auth

• Limited support exists in Rails 3.1 and onwards to make it easier to write your own authentication– Basic HTTP Auth

• Quick and easy way to make parts of site private where identity is not very important.

• Example:http_basic_authenticate_with

:name => "ror", :password => "rocks", :except=>[:index]

Page 8: Authentication in-rails

Rails 3.1 - secure_password• has_secure_password

– Adds methods to set and authenticate against a BCrypt password– Requires the model to have a password_digest attribute– Handles complexity of

• Adding password confirmations• Validating password fields• Encrypting passwords• Authentication

– Does not handle• Session management• Helpers – current_user, authenticate_user!

– http://apidock.com/rails/ActiveModel/SecurePassword/ClassMethods/has_secure_password

• force_ssl– Called on the controller– Forces specified or all controller actions to operate only over SSL – Production and Test environments only– http://apidock.com/rails/ActionController/ForceSSL/ClassMethods/force_ssl

Page 10: Authentication in-rails

A Complete Authentication Solution…• How flexible is it?

– Can I change the views– Can I change routing logic?

• Important if you want to show different views to different users

• Email Confirmation– Ensure the email addresses your users give are valid and correct

• Forgotten Passwords– Happens all the time!

• Are passwords encrypted?– Remember LinkedIn - http://money.cnn.com/2012/06/06/technology/linkedin-password-hack/index.htm

• Timeouts on inactive sessions– Important if you are a banking application

• Does it federate identities with other providers– This is becoming more and more important– Many web apps allow users to connect using 3rd party identity providers such as Facebook, Twitter, Google, etc.

• How easy is it to update user profiles?• Does it allow changing user-names?• Does it allow an easy way to delete user accounts?• Does it allow an easy way to create users programmatically?• How easy is it to move to a different solution?

Page 11: Authentication in-rails

Options Explored

• Authentication from Scratch• Facebook Connect from Scratch• OmniAuth• Devise• AuthLogic

Page 12: Authentication in-rails

Authentication from Scratch• You write your own Models, Controllers, Views, and Helpers• Not so hard to get basic setup working• Advantages

– Most Flexible– Easy to setup Basic Functionality– Easy to understand

• Disadvantages– Too much work to make it complete

• Forgotten passwords, email Confirmations, field validations, restrictions, to name a few

– Lost focus from the core app– Re-inventing the wheel

Page 13: Authentication in-rails

Facebook Connect from Scratch• Can be done in two ways

– Server side• Pros

– More secure– No dependency on Facebook’s JS SDK (all.js)

• Cons– You need to understand the flow – The user does a full page redirect to facebook.com

» Workaround• Perform all authentication in a HTML POPUP with your own handler pages before and after Facebook OAuth calls

– Client side• Mostly JS based• Pros

– Easier to implement since FB SDK encapsulates all complexity– Authentication happens in a Pop-up – User stays on your website all the time

• Cons– all.js is bulky – 170KB (as of 7/9/2012)– Less Secure. Facebook cookie and Access Tokens are stored in local cookies. Easy to extract out

Page 14: Authentication in-rails

Facebook Connect – Server Flow

• http://developers.facebook.com/docs/authentication/server-side/

Page 15: Authentication in-rails

Extending the Authentication from Scratch to include Facebook Connect

• Create a Facebook App on https://developers.facebook.com/apps

• Add a handler in SessionsController to handle Facebook Connect callbacks– Ref: Sessions#fb_auth in the sample code

• Create Sessions in the Facebook Connect callback

Page 16: Authentication in-rails

Facebook Connect from Scratch

• Source Code– https://

github.com/mvaidya/Authentication-In-Rails/tree/master/Code/YourOwnAuthWithFBConnect/Posts• Performs identity matching• Allows the user to login using Facebook or

create a password on the website or both

Page 17: Authentication in-rails

OmniAuth• One of the best solutions to support Authentication from multiple providers• Implemented as a rack middleware

– Very easy to keep is separate from the core application• Follows a very modular Strategy Pattern – all strategies are listed here

– E.g. omniauth-facebook, omniauth-twitter, etc.– Each Strategy is a separate gem

• Cool – easy to maintain

• Does not provide – controller logic– session management (session[:user_id]) – helpers (current_user, authenticate_user!)

• No views required for Registration and Login• omniauth-identity is an afterthought!

Page 18: Authentication in-rails

OmniAuth – Big Steps• Create a Facebook App - https://developers.facebook.com/apps

• Create a controller to manage login sessions

• Create a model to store user info including Authentication provider and user ids– E.g. User(provider:string, uid:string, name:string)

• Add gems for all providers u want to support– gem ‘omniauth-twitter’– gem ‘omniauth-facebook’– bundle install

• Add an initializer– config/omniauth.rb

• You register OmniAuth as a rack middleware and configure providersRails.application.config.middleware.use OmniAuth::Builder do

provider :twitter, APP_CONFIG[:twitter]['consumer_key'],APP_CONFIG[:twitter]['consumer_secret']

provider :facebook,APP_CONFIG[:facebook]['app_id'],APP_CONFIG[:facebook]['app_secret'],:client_options => { :ssl => { :ca_file => "#{Rails.root}/config/ca-bundle.crt" } }

End

• Add login links that point to “/auth/:provider”• Setup auth callbacks (/auth/:provider/callback) as SessionsController, create action

– request.env[“omniauth.auth”] has the hash containing user information and tokens from the identity provider (facebook, twitter, etc)

Page 19: Authentication in-rails

OmniAuth - Gotchas

• Facebook Strategy – Facebook’s SSL Root Cert will not be found– Solutions:

• https://github.com/intridea/omniauth/issues/260 • http://

stackoverflow.com/questions/3977303/omniauth-facebook-certificate-verify-failed

• How to handle Login failures?– Add the following in your omniauth.rb initializerOmniAuth.config.on_failure = -> env do

env[ActionDispatch::Flash::KEY] ||= ActionDispatch::Flash::FlashHash.newenv[ActionDispatch::Flash::KEY][:error] = "Authentication failed, please try again."SessionsController.action(:new).call(env) #call whatever controller/action that displays your signup form

end

Page 20: Authentication in-rails

OmniAuth - Resources

• Sample Code– Has code to authenticate with Facebook as well as

Twitter– Add Facebook application id and secret to

app_config.yml– Add Twitter consumer key and secret to app_config.yml– https://github.com/mvaidya/Authentication-In-Rails/tree/

master/Code/OmniAuth/Posts• Resources

– https://github.com/intridea/omniauth/wiki– http://railscasts.com/episodes/241-simple-omniauth

Page 21: Authentication in-rails

OmniAuth Identity

• Follow instructions here to add database authentication (or omniauth-identity strategy) to your existing omniauth solution– https://github.com/intridea/omniauth-identity

• This will enable users to login using an OAuth provider, or register and create an account in your website

• A big Afterthought. Done almost right – Needs an additional Identities table which is different from the Users table

Page 22: Authentication in-rails

OmniAuth Identity - Gotchas

• You also need to support "POST" on this route– '/auth/:provider/callback' => 'sessions#create'

• Login uses the email address by default. – It can be customized

• It provides it’s own Registration and Login UI, which won’t look anything like your app– Validations on the Identity model wont show up on the

register/login UI– UI can be customized

• Does not tie identities together!

Page 23: Authentication in-rails

OmniAuth Identity – Gotchas

• Handle Registration Failures!– In your omniauth initializer, add the

on_failed_registration key to identity provider

provider :identity, on_failed_registration: lambda { |env|# lambda is used so that the class IdentitiesController is not cached (important for dev environment).

# That way, changes to the controller will be picked up automatically since # lamda is the rack application to handle failures and not IndentitiesController#new directly

IdentitiesController.action(:new).call(env)}

Page 25: Authentication in-rails

Devise• One of the most popular and very customizable• Versatile and most Complete

– Comprised of 12 customizable modules• Very easy to configure• Very good and rich documentation

– It’s wiki has covered almost all possible scenarios in which it can be customized• Strong community support• Devise is a Engine

– It gives you the entire stack – Models, Views, Controllers, Helpers• No need to write your own session handlers, or helpers (current_user, authenticate_user!)

– This could cause a major drawback• Hard to understand what is going on in the background!• Have to override its controllers and views to customize

• Third Party auth providers can be easily integrated using omniauth– Omniauthable module

Page 26: Authentication in-rails

Devise – Big Steps• Install the gem

– gem ‘devise’– bundle

• Run generators and run migrations– rails g devise:install

• The script will display additional instructions on the console• Make sure you follow them all

– rails g devise user– rake db:migrate

• Customize views?– rake g devise:views

• This will copy devise views to views/devise directory. You can change them any way you want

• Additional Customizations?– config/initializers/devise– https://github.com/plataformatec/devise/wiki/_pages

Page 27: Authentication in-rails

Devise – Gotchas

• Remember: Devise is an Engine– All customizations will require you to override on or

more of the following• Controllers• Routes• Helpers• Views• Libs

– But almost all scenarios are well documented on their wiki pages.

Page 28: Authentication in-rails

Devise – Pleasant Surprise

• If you are using simple_form, generated views use simple_form as well :)

• Add OmniAuth to devise– https://

github.com/plataformatec/devise/wiki/OmniAuth%3A-Overview• Cool: it detects duplicate identities and resolves

them– E.g. what happens if a user connects with a Facebook

account, whose email address has already been used?

Page 29: Authentication in-rails

Devise – Resources

• Source Code– https://github.com/mvaidya/Authentication-In-Rails/tree

/master/Code/Devise/Posts• Resources

– https://github.com/plataformatec/devise– https://github.com/plataformatec/devise/wiki/_pages– Devise needs an email provider if you are using

Confirmable• Setup email with AWS SES and SMTP

– http://blog.readypulse.com/2012/01/06/amazon-ses-smtp-emails-using-rails-3-1-in-three-easy-steps/

– http://railscasts.com/episodes?utf8=%E2%9C%93&search=devise

Page 30: Authentication in-rails

AuthLogic

• https://github.com/binarylogic/authlogic/• One of the oldest Authentication Systems• Advantages

– Handles the logic of authenticating users– Flexible

• leaves the user-flow including all MVC up to the developer• So the developer can do anything he wants

• Disadvantages– Takes up significantly more setup time as compared to Devise

and OmniAuth– Poor support for Third Party providers

Page 31: Authentication in-rails

OAuth with AuthLogic

• Supported by way of AuthLogic “add ons”• For Facebook, they use Facebooker2 gem– Pretty poor choice

Page 32: Authentication in-rails

My Favorite• Devise

– I find this as the most complete solution– It hides almost everything from you

• I get to focus more on my app– But

• It has a very rich wiki which has so far addressed all my customization needs easily

• Devise + OmniAuth (omniauthable)– Good, if you don’t care a lot of complicated actions with Facebook graph API. It could be

get complicated – especially if you have multiple providers in the mix• Devise + Facebook + Twitter + Google from Scratch

– Facebook and Twitter have their authentication protocols very well documented– Best to do it on your own

• Gives you solid understanding• You can perform advanced functions on the graph much more easily since you have control

on your code• Identity matching!

Page 33: Authentication in-rails

What do we use at ReadyPulse?

• Devise• Facebook Connect (from scratch)• Twitter OAuth (using twitter_oauth gem)• Why?

– Devise was the most complete solution that worked out of the box– Highly customizable to fit all of our scenarios– Awesome documentation– We don’t use Facebook and Twitter for user authentication

• Users add their Facebook and Twitter accounts in ReadyPulse to create a holistic view of their brands

– Identity matching

Page 34: Authentication in-rails

Reminder: Always Encrypt Passwords• Never store plain passwords• Use a one way encryption algorithm

– Hash(password) <unique_encrypted_password>• bcrypt-ruby

– Ruby binding for the OpenBSD bcrypt() password hashing algorithm– https://github.com/codahale/bcrypt-ruby#readme

• Want more secure passwords?– Use SALT– Small chunk of random data added to the beginning of the password before it is

encrypted– Hash(salt + password) <unique_hard_to_break_encrypted_password>

• Even more secure?– Use iterations

• A good read - http://www.jasypt.org/howtoencryptuserpasswords.html

Page 35: Authentication in-rails

Use Iterations for even stronger Passwords

• Feed the results of a hash function back to the same hash function

• Do it “n” times

• Save the “n” in user table along with the SALT and Digest

Page 36: Authentication in-rails

We are hiring

Ruby on Rails engineers• http://www.readypulse.com/jobs• [email protected]

Page 37: Authentication in-rails

THANK YOUQuestions & Answers

Page 38: Authentication in-rails

APPENDIX

Page 39: Authentication in-rails
Page 40: Authentication in-rails

Authentication from Scratch – Big Steps• Models

– User• Manages the user profile and Identity• Validations • Callbacks

– UserSession• Manages the users’ current logged in sessions

• Controllers – UsersController

• Actions– new // New User Profile Form– create // Save User Profile to Database– edit // Edit Profile Form

– UserSessionsController• Actions

– new // Login Form– create // Validate credentials and store user info in the session – session[:user_id] = user.id– destroy // Logout (destroy session object) -- session[:user_id] = nil

• Views• Helper Methods

– current_user // @current_user ||= User.find_by_id(session[:user_id]) if session[:user_id]– authenticate_user! // used as a before filter on controller actions that need authenticated user to be present

Page 41: Authentication in-rails

Authentication from Scratch - Remember• Do not store passwords in Plain Text!

– Use a password Salt to create a Hash the User’s password– Save password_salt and password_hash both in the database– Verfication time: Use the password_salt to compute the hash

and compare with the stored password_hash– bcrypt-ruby gem provides a salt based encryption

• password_salt = BCrypt::Engine.generate_salt• password_hash = BCrypt::Engine.hash_secret(password, password_salt)

• Do not allow mass assignment on password_salt and password_hash fields– Use attr_accessible

Page 42: Authentication in-rails

Authentication from Scratch – Resources

• Sample Code– https://github.com/mvaidya/Authentication-

In-Rails/tree/master/Code/YourOwnAuth/Posts

• Resources– http://

railscasts.com/episodes/250-authentication-from-scratch?autoplay=true• Does not talk about authenticate_user!

Before filter– https://github.com/ryanb/nifty-generators