Slide 1

Slide 1 text

What Devise is doing when you are not looking

Slide 2

Slide 2 text

@lucasmazza

Slide 3

Slide 3 text

EQPUWNVKPIaCPFaUQHVYCTGaGPIKPGGTKPI

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

October, ‘09 8m+ downloads Rails 3.2+

Slide 7

Slide 7 text

Full stack Rails Authentication

Slide 8

Slide 8 text

Models Controllers Views

Slide 9

Slide 9 text

Models Controllers Rack Views

Slide 10

Slide 10 text

Models Controllers Middlewares Views

Slide 11

Slide 11 text

Models Controllers Router Views

Slide 12

Slide 12 text

Models Controllers Engines Views

Slide 13

Slide 13 text

hassox/warden

Slide 14

Slide 14 text

warden is awesome

Slide 15

Slide 15 text

Authentication infrastructure, like rack is HTTP infrastructure

Slide 16

Slide 16 text

User session management Failure handling Lifecycle callbacks

Slide 17

Slide 17 text

SessionStore … Rails & Devise … … Warden

Slide 18

Slide 18 text

So, what is inside Devise?

Slide 19

Slide 19 text

Models

Slide 20

Slide 20 text

Models Router

Slide 21

Slide 21 text

Models Router Controllers & Views

Slide 22

Slide 22 text

Models Router Controllers & Views Warden Callbacks & Failure App

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Everything starts with the models

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

$ 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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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]

Slide 33

Slide 33 text

unauthenticated?

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

Nothing but a Rack endpoint

Slide 37

Slide 37 text

Authentication lifecycle

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

❤ warden ❤

Slide 40

Slide 40 text

Security

Slide 41

Slide 41 text

Everything should be secure by default

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

Bcrypt by default Encrypted tokens Timing attacks

Slide 44

Slide 44 text

Friendly Reminder ! If you found a security bug, please do not open an Issue on GitHub. Report to opensource@plataformatec.com.br instead.

Slide 45

Slide 45 text

Extending it

Slide 46

Slide 46 text

Model API (it could use some extra documentation)

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

Custom controllers

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

❤ I18n ❤

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

Extending the Warden integration

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

And a boatload of configurations

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

Extensions in the wild

Slide 66

Slide 66 text

scambra/devise_invitable

Slide 67

Slide 67 text

mhfs/devise-async

Slide 68

Slide 68 text

tinfoil/devise-two-factor

Slide 69

Slide 69 text

phatworx/devise_security_extension

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

The Future

Slide 72

Slide 72 text

Keep it stable Keep it tidy

Slide 73

Slide 73 text

No new modules Pluggable APIs when required

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

More docs for the public/private API

Slide 76

Slide 76 text

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