Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What Devise is doing when you are not looking

5a90a67fa1a92e6a4b605cfd8da5e375?s=47 Lucas Mazza
September 04, 2014

What Devise is doing when you are not looking

Devise is the go-to authentication gem for Rails apps and you probably worked with it once or twice, but do you know how Devise works inside your app? We are going to talk about how Devise is designed to provide a full stack authentication solution, who are the main building blocks of the library and how we can extend their behaviour to our will in our applications.


Lucas Mazza

September 04, 2014


  1. What Devise is doing when you are not looking

  2. @lucasmazza


  4. None
  5. “[…]Everything is deterministic and known, there is nothing magical. […]“

    Xavier Noria (@fxn)
  6. October, ‘09 8m+ downloads Rails 3.2+

  7. Full stack Rails Authentication

  8. Models Controllers Views

  9. Models Controllers Rack Views

  10. Models Controllers Middlewares Views

  11. Models Controllers Router Views

  12. Models Controllers Engines Views

  13. hassox/warden

  14. warden is awesome

  15. Authentication infrastructure, like rack is HTTP infrastructure

  16. User session management Failure handling Lifecycle callbacks

  17. SessionStore … Rails & Devise … … Warden

  18. So, what is inside Devise?

  19. Models

  20. Models Router

  21. Models Router Controllers & Views

  22. Models Router Controllers & Views Warden Callbacks & Failure App

  23. Models Router Controllers & Views Warden Callbacks & Failure App

    and a ton of configurations
  24. Everything starts with the models

  25. class User < ActiveRecord::Base devise :database_authenticatable, :trackable, :confirmable end Which

    modules to pick up Can load routes and controllers on the app
  26. database_authenticatable => Devise::Models::DatabaseAuthenticatable confirmable => Devise::Models::Confirmable lockable => Devise::Models::Lockable omniauthable

    => Devise::Models::Omniauthable recoverable => Devise::Models::Recoverable registerable => Devise::Models::Registerable rememberable => Devise::Models::Rememberable timeoutable => Devise::Models::Timeoutable trackable => Devise::Models::Trackable validatable => Devise::Models::Validatable Everything is opt in
  27. Thingy::Application.routes.draw do devise_for :users end The router macro get things

  28. Thingy::Application.routes.draw do devise_for :users, controllers: { registrations: 'users/registrations' } end

    Custom flows → custom controllers
  29. $ bin/rake routes Prefix Verb URI Pattern Controller#Action new_user_session GET

    /users/sign_in(.:format) devise/sessions#new user_session POST /users/sign_in(.:format) devise/sessions#create destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy user_password POST /users/password(.:format) devise/passwords#create new_user_password GET /users/password/new(.:format) devise/passwords#new edit_user_password GET /users/password/edit(.:format) devise/passwords#edit PATCH /users/password(.:format) devise/passwords#update PUT /users/password(.:format) devise/passwords#update cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel user_registration POST /users(.:format) devise/registrations#create new_user_registration GET /users/sign_up(.:format) devise/registrations#new edit_user_registration GET /users/edit(.:format) devise/registrations#edit PATCH /users(.:format) devise/registrations#update PUT /users(.:format) devise/registrations#update DELETE /users(.:format) devise/registrations#destroy
  30. Thing::Application.routes.draw do authenticate :user, -> { |user| user.admin? } do

    mount Resque::Server.new, at: 'resque' end ! devise_scope :user do get 'sign_in', to: 'devise/sessions#new' end end
  31. Devise::SessionsController Devise::PasswordsController Devise::RegistrationsController Devise::ConfirmationsController Devise::UnlocksController Builtin controllers: workflow & redirects

  32. app/views/devise/ "## confirmations $ &## new.html.erb "## mailer $ "##

    confirmation_instructions.html.erb $ "## reset_password_instructions.html.erb $ &## unlock_instructions.html.erb "## passwords $ "## edit.html.erb $ &## new.html.erb "## registrations $ "## edit.html.erb $ &## new.html.erb "## sessions $ &## new.html.erb "## shared $ &## _links.html.erb &## unlocks &## new.html.erb rails g devise:views [-v registrations confirmations]
  33. unauthenticated?

  34. unauthenticated? (›°□°ʣ›ớ ᵲᴸᵲ

  35. unauthenticated? (›°□°ʣ›ớ ᵲᴸᵲ FailureApp

  36. Nothing but a Rack endpoint

  37. Authentication lifecycle

  38. Session expiration Unconfirmed accounts Account locking Track user requests Cleanup

  39. ❤ warden ❤

  40. Security

  41. Everything should be secure by default

  42. I don’t want to worry (so much) about security while

    working on my app.
  43. Bcrypt by default Encrypted tokens Timing attacks

  44. Friendly Reminder ! If you found a security bug, please

    do not open an Issue on GitHub. Report to opensource@plataformatec.com.br instead.
  45. Extending it

  46. Model API (it could use some extra documentation)

  47. class User < ActiveRecord::Base # Deliver notifications using Rails 4.2

    ‘deliver_later'. def send_devise_notification(email, *args) devise_mailer.send(email, self, *args).deliver_later end end
  48. class User < ActiveRecord::Base # Delivers a welcome notification when

    the user confirms # its email. def after_confirmation send_devise_notification(:welcome) end end
  49. class User < ActiveRecord::Base devise :database_authenticatable, :trackable, :confirmable # Unconfirmed

    users can still log in. def confirmation_required? false end end
  50. Devise.timeout_in = 30.minutes ! class User < ActiveRecord::Base devise :database_authenticatable,

    :timeoutable def timeout_in staff? ? 10.minutes : 2.hours end end Instance based configuration
  51. class User < ActiveRecord::Base devise :database_authenticatable, :timeoutable, timeout_in: 2.hours end

    ! class Admin < ActiveRecord::Base devise :database_authenticatable, :timeoutable, timeout_in: 10.minutes end Scope based configuration
  52. Custom controllers

  53. class SessionsController < Devise::SessionsController def after_sign_out_path_for(resource) 'goodbye' end end Custom

  54. after_resending_confirmation_instructions_path_for after_confirmation_path_for after_omniauth_failure_path_for after_resetting_password_path_for after_sending_reset_password_instructions_path_for after_sign_up_path_for after_inactive_sign_up_path_for after_update_path_for after_sending_unlock_instructions_path_for after_unlock_path_for

    after_sign_in_path_for after_sign_out_path_for
  55. class RegistrationsController < Devise::RegistrationsController # Track down registration origins through

    a param def build_resource(hash = nil) super self.resource.origin = params['_origin'] self.resource end end
  56. class ApplicationController < ActionController::Base # Include the subdomain in the

    i18n interpolation options. def devise_i18n_options(options) options.merge(subdomain: current_account.subdomain) end end
  57. ❤ I18n ❤

  58. en: devise: confirmations: confirmed: "Your email address has been successfully

    confirmed." send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." failure: already_authenticated: "You are already signed in." inactive: "Your account is not activated yet." invalid: "Invalid email or password." locked: "Your account is locked." last_attempt: "You have one more attempt before your account is locked." not_found_in_database: "Invalid email address or password." # ...
  59. ❤ I18n ❤ https://github.com/plataformatec/devise/wiki/I18n

  60. Extending the Warden integration

  61. config.warden do |manager| manager.failure_app = MyFailureApp end ! class MyFailureApp

    < Devise::FailureApp def redirect_url warden_options[:checkout] ? checkout_sign_in_path : super end end Custom Failure
  62. class RemoteAuthenticable < Devise::Strategies::Authenticatable # ... end ! Warden::Strategies.add :remote_authenticable,

    RemoteAuthenticable Devise.add_module :remote_authenticable, strategy: true ! class User < ActiveRecord::Base devise :remote_authenticable end Alternative Strategies
  63. And a boatload of configurations

  64. Devise.email_regexp Devise.password_length Devise.remember_for Devise.scoped_views Devise.parent_controller # ... Check your config/initializers/devise.rb

    and https://github.com/plataformatec/devise/blob/v3.3.0/lib/devise.rb
  65. Extensions in the wild

  66. scambra/devise_invitable

  67. mhfs/devise-async

  68. tinfoil/devise-two-factor

  69. phatworx/devise_security_extension

  70. Other ORMs Bundled i18ns And more stuff that I don’t

  71. The Future

  72. Keep it stable Keep it tidy

  73. No new modules Pluggable APIs when required

  74. Rails 4.0/4.1+ only ! Remove our TokenGenerator StrongParameters by default

    Compatibility hacks are boring
  75. More docs for the public/private API

  76. Thanks! https://twitter.com/lucasmazza https://speakerdeck.com/lucas