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

What Devise is doing when you are not looking

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
Tweet

More Decks by Lucas Mazza

Other Decks in Technology

Transcript

  1. What Devise is doing
    when you are not looking

    View full-size slide

  2. EQPUWNVKPIaCPFaUQHVYCTGaGPIKPGGTKPI

    View full-size slide

  3. “[…]Everything is deterministic and known,
    there is nothing magical. […]“
    Xavier Noria (@fxn)

    View full-size slide

  4. October, ‘09
    8m+ downloads
    Rails 3.2+

    View full-size slide

  5. Full stack Rails
    Authentication

    View full-size slide

  6. Models
    Controllers
    Views

    View full-size slide

  7. Models
    Controllers
    Rack
    Views

    View full-size slide

  8. Models
    Controllers
    Middlewares
    Views

    View full-size slide

  9. Models
    Controllers
    Router
    Views

    View full-size slide

  10. Models
    Controllers
    Engines
    Views

    View full-size slide

  11. hassox/warden

    View full-size slide

  12. warden is
    awesome

    View full-size slide

  13. Authentication infrastructure,
    like rack is HTTP infrastructure

    View full-size slide

  14. User session management
    Failure handling
    Lifecycle callbacks

    View full-size slide

  15. SessionStore

    Rails & Devise


    Warden

    View full-size slide

  16. So, what is
    inside Devise?

    View full-size slide

  17. Models Router

    View full-size slide

  18. Models Router
    Controllers
    & Views

    View full-size slide

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

    View full-size slide

  20. Models Router
    Controllers
    & Views
    Warden Callbacks
    & Failure App
    and a ton of configurations

    View full-size slide

  21. Everything starts
    with the models

    View full-size slide

  22. class User < ActiveRecord::Base
    devise :database_authenticatable, :trackable, :confirmable
    end
    Which modules to pick up
    Can load routes and controllers on the app

    View full-size slide

  23. 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

    View full-size slide

  24. Thingy::Application.routes.draw do
    devise_for :users
    end
    The router macro get things started

    View full-size slide

  25. Thingy::Application.routes.draw do
    devise_for :users, controllers: { registrations: 'users/registrations' }
    end
    Custom flows → custom controllers

    View full-size slide

  26. $ 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

    View full-size slide

  27. 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

    View full-size slide

  28. Devise::SessionsController
    Devise::PasswordsController
    Devise::RegistrationsController
    Devise::ConfirmationsController
    Devise::UnlocksController
    Builtin controllers: workflow & redirects

    View full-size slide

  29. 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]

    View full-size slide

  30. unauthenticated?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  33. Nothing but a
    Rack endpoint

    View full-size slide

  34. Authentication lifecycle

    View full-size slide

  35. Session expiration
    Unconfirmed accounts
    Account locking
    Track user requests
    Cleanup cookies/tokens/etc

    View full-size slide

  36. ❤ warden ❤

    View full-size slide

  37. Everything should be
    secure by default

    View full-size slide

  38. I don’t want to worry (so
    much) about security while
    working on my app.

    View full-size slide

  39. Bcrypt by default
    Encrypted tokens
    Timing attacks

    View full-size slide

  40. Friendly Reminder
    !
    If you found a security bug, please do not open
    an Issue on GitHub.
    Report to [email protected]
    instead.

    View full-size slide

  41. Extending it

    View full-size slide

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

    View full-size slide

  43. 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

    View full-size slide

  44. class User < ActiveRecord::Base
    # Delivers a welcome notification when the user confirms
    # its email.
    def after_confirmation
    send_devise_notification(:welcome)
    end
    end

    View full-size slide

  45. class User < ActiveRecord::Base
    devise :database_authenticatable, :trackable, :confirmable
    # Unconfirmed users can still log in.
    def confirmation_required?
    false
    end
    end

    View full-size slide

  46. 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

    View full-size slide

  47. 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

    View full-size slide

  48. Custom controllers

    View full-size slide

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

    View full-size slide

  50. 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

    View full-size slide

  51. 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

    View full-size slide

  52. 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

    View full-size slide

  53. ❤ I18n ❤

    View full-size slide

  54. 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."
    # ...

    View full-size slide

  55. ❤ I18n ❤
    https://github.com/plataformatec/devise/wiki/I18n

    View full-size slide

  56. Extending the Warden integration

    View full-size slide

  57. 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

    View full-size slide

  58. 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

    View full-size slide

  59. And a boatload of configurations

    View full-size slide

  60. 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

    View full-size slide

  61. Extensions in the wild

    View full-size slide

  62. scambra/devise_invitable

    View full-size slide

  63. mhfs/devise-async

    View full-size slide

  64. tinfoil/devise-two-factor

    View full-size slide

  65. phatworx/devise_security_extension

    View full-size slide

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

    View full-size slide

  67. Keep it stable
    Keep it tidy

    View full-size slide

  68. No new modules
    Pluggable APIs when required

    View full-size slide

  69. Rails 4.0/4.1+ only
    !
    Remove our TokenGenerator
    StrongParameters by default
    Compatibility hacks are boring

    View full-size slide

  70. More docs for the
    public/private API

    View full-size slide

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

    View full-size slide