quo_vadis 1.4.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +11 -8
  3. data/CHANGELOG.md +5 -0
  4. data/Gemfile +14 -1
  5. data/Gemfile.lock +178 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +435 -127
  8. data/Rakefile +15 -9
  9. data/app/controllers/quo_vadis/confirmations_controller.rb +56 -0
  10. data/app/controllers/quo_vadis/logs_controller.rb +20 -0
  11. data/app/controllers/quo_vadis/password_resets_controller.rb +65 -0
  12. data/app/controllers/quo_vadis/passwords_controller.rb +26 -0
  13. data/app/controllers/quo_vadis/recovery_codes_controller.rb +54 -0
  14. data/app/controllers/quo_vadis/sessions_controller.rb +50 -132
  15. data/app/controllers/quo_vadis/totps_controller.rb +72 -0
  16. data/app/controllers/quo_vadis/twofas_controller.rb +26 -0
  17. data/app/mailers/quo_vadis/mailer.rb +73 -0
  18. data/app/models/quo_vadis/account.rb +59 -0
  19. data/app/models/quo_vadis/account_confirmation_token.rb +17 -0
  20. data/app/models/quo_vadis/log.rb +57 -0
  21. data/app/models/quo_vadis/password.rb +52 -0
  22. data/app/models/quo_vadis/password_reset_token.rb +17 -0
  23. data/app/models/quo_vadis/recovery_code.rb +26 -0
  24. data/app/models/quo_vadis/session.rb +55 -0
  25. data/app/models/quo_vadis/token.rb +42 -0
  26. data/app/models/quo_vadis/totp.rb +56 -0
  27. data/bin/console +15 -0
  28. data/bin/rails +21 -0
  29. data/bin/setup +8 -0
  30. data/config/locales/quo_vadis.en.yml +50 -23
  31. data/config/routes.rb +40 -12
  32. data/db/migrate/202102150904_setup.rb +48 -0
  33. data/lib/generators/quo_vadis/install_generator.rb +4 -23
  34. data/lib/quo_vadis.rb +100 -98
  35. data/lib/quo_vadis/controller.rb +227 -0
  36. data/lib/quo_vadis/crypt.rb +43 -0
  37. data/lib/quo_vadis/current_request_details.rb +11 -0
  38. data/lib/quo_vadis/defaults.rb +18 -0
  39. data/lib/quo_vadis/encrypted_type.rb +17 -0
  40. data/lib/quo_vadis/engine.rb +9 -11
  41. data/lib/quo_vadis/hmacable.rb +26 -0
  42. data/lib/quo_vadis/ip_masking.rb +31 -0
  43. data/lib/quo_vadis/model.rb +86 -0
  44. data/lib/quo_vadis/version.rb +3 -1
  45. data/quo_vadis.gemspec +18 -25
  46. metadata +46 -246
  47. data/app/controllers/controller_mixin.rb +0 -109
  48. data/app/mailers/quo_vadis/notifier.rb +0 -30
  49. data/app/models/model_mixin.rb +0 -128
  50. data/lib/generators/quo_vadis/templates/migration.rb.erb +0 -18
  51. data/lib/generators/quo_vadis/templates/quo_vadis.rb.erb +0 -96
  52. data/test/dummy/.gitignore +0 -2
  53. data/test/dummy/Rakefile +0 -7
  54. data/test/dummy/app/controllers/application_controller.rb +0 -3
  55. data/test/dummy/app/controllers/articles_controller.rb +0 -20
  56. data/test/dummy/app/controllers/users_controller.rb +0 -17
  57. data/test/dummy/app/helpers/application_helper.rb +0 -2
  58. data/test/dummy/app/helpers/articles_helper.rb +0 -2
  59. data/test/dummy/app/models/article.rb +0 -2
  60. data/test/dummy/app/models/person.rb +0 -3
  61. data/test/dummy/app/models/user.rb +0 -3
  62. data/test/dummy/app/views/articles/index.html.erb +0 -1
  63. data/test/dummy/app/views/articles/new.html.erb +0 -11
  64. data/test/dummy/app/views/layouts/application.html.erb +0 -30
  65. data/test/dummy/app/views/layouts/sessions.html.erb +0 -3
  66. data/test/dummy/app/views/quo_vadis/notifier/change_password.text.erb +0 -9
  67. data/test/dummy/app/views/quo_vadis/notifier/invite.text.erb +0 -8
  68. data/test/dummy/app/views/sessions/edit.html.erb +0 -11
  69. data/test/dummy/app/views/sessions/forgotten.html.erb +0 -13
  70. data/test/dummy/app/views/sessions/invite.html.erb +0 -31
  71. data/test/dummy/app/views/sessions/new.html.erb +0 -15
  72. data/test/dummy/app/views/users/new.html.erb +0 -14
  73. data/test/dummy/config.ru +0 -4
  74. data/test/dummy/config/application.rb +0 -21
  75. data/test/dummy/config/boot.rb +0 -10
  76. data/test/dummy/config/database.yml +0 -22
  77. data/test/dummy/config/environment.rb +0 -5
  78. data/test/dummy/config/environments/development.rb +0 -26
  79. data/test/dummy/config/environments/production.rb +0 -49
  80. data/test/dummy/config/environments/test.rb +0 -37
  81. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  82. data/test/dummy/config/initializers/inflections.rb +0 -10
  83. data/test/dummy/config/initializers/mime_types.rb +0 -5
  84. data/test/dummy/config/initializers/quo_vadis.rb +0 -77
  85. data/test/dummy/config/initializers/rack_patch.rb +0 -16
  86. data/test/dummy/config/initializers/secret_token.rb +0 -7
  87. data/test/dummy/config/initializers/session_store.rb +0 -8
  88. data/test/dummy/config/locales/en.yml +0 -5
  89. data/test/dummy/config/locales/quo_vadis.en.yml +0 -21
  90. data/test/dummy/config/routes.rb +0 -5
  91. data/test/dummy/db/migrate/20110124125037_create_users.rb +0 -13
  92. data/test/dummy/db/migrate/20110124131535_create_articles.rb +0 -14
  93. data/test/dummy/db/migrate/20110127094709_add_authentication_to_users.rb +0 -18
  94. data/test/dummy/db/migrate/20111004112209_create_people.rb +0 -13
  95. data/test/dummy/db/migrate/20111004132342_add_authentication_to_people.rb +0 -18
  96. data/test/dummy/db/schema.rb +0 -33
  97. data/test/dummy/public/404.html +0 -26
  98. data/test/dummy/public/422.html +0 -26
  99. data/test/dummy/public/500.html +0 -26
  100. data/test/dummy/public/favicon.ico +0 -0
  101. data/test/dummy/public/javascripts/application.js +0 -2
  102. data/test/dummy/public/javascripts/controls.js +0 -965
  103. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  104. data/test/dummy/public/javascripts/effects.js +0 -1123
  105. data/test/dummy/public/javascripts/prototype.js +0 -6001
  106. data/test/dummy/public/javascripts/rails.js +0 -175
  107. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  108. data/test/dummy/script/rails +0 -6
  109. data/test/integration/activation_test.rb +0 -108
  110. data/test/integration/authenticate_test.rb +0 -39
  111. data/test/integration/blocked_test.rb +0 -23
  112. data/test/integration/config_test.rb +0 -118
  113. data/test/integration/cookie_test.rb +0 -67
  114. data/test/integration/csrf_test.rb +0 -41
  115. data/test/integration/forgotten_test.rb +0 -93
  116. data/test/integration/helper_test.rb +0 -18
  117. data/test/integration/locale_test.rb +0 -197
  118. data/test/integration/navigation_test.rb +0 -7
  119. data/test/integration/sign_in_person_test.rb +0 -26
  120. data/test/integration/sign_in_test.rb +0 -24
  121. data/test/integration/sign_out_test.rb +0 -20
  122. data/test/integration/sign_up_test.rb +0 -21
  123. data/test/quo_vadis_test.rb +0 -7
  124. data/test/support/integration_case.rb +0 -11
  125. data/test/test_helper.rb +0 -86
  126. data/test/unit/user_test.rb +0 -75
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0dee59600eb3e1e3dc8110ca0c42d25594d01677
4
- data.tar.gz: aeb164c5593a7640c0af0ead8398da99def841b2
2
+ SHA256:
3
+ metadata.gz: db2382851913e7455425f7014c13fe2f88eeb19209077c042507b948bef37426
4
+ data.tar.gz: da813baac8751b9cbc998c403b824c40eb7b2bf5e9710cde7c88fef0c0a0255f
5
5
  SHA512:
6
- metadata.gz: 174aab836f3fd1e89a8f2bff303dcae413e2567e0174bed21bb195c18c0fd76eaef3d9c541239ada7f1ac747bea1c7424ebfccd9f410855b134dc316cbd91ccd
7
- data.tar.gz: 69871c9b4b3344786638d8b17131810f860f05d5fbbf4112c1cf08a65a6442989158fda6de3cdf9ba933e05c952f054056927426fb536d031d0cbf7a1a58fc2d
6
+ metadata.gz: 837432b39ed54b1150d6982bf0385a923a908fe089a8c20b8753a256764d4d76c47da8588c14a99ae76622508cc7f727d1c4589f9a2f6f9870420310283ac6fd
7
+ data.tar.gz: 3c90dbeac8aea06b5eef1cd1afd51097e6228e15a5bdd9984e030d27888ff63fdb61721b4f40d41fc0dad3a91f9f044052fbecca598795395891581880c7f9dd
data/.gitignore CHANGED
@@ -1,8 +1,11 @@
1
- pkg/*
2
- *.gem
3
- Gemfile.lock
4
- .bundle
5
- test/dummy/log/*
6
- test/dummy/tmp/*
7
- rdoc/*
8
- NOTES
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ /test/dummy/log/
10
+ /test/dummy/tmp/
11
+ /test/dummy/db/*.sqlite3
data/CHANGELOG.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # CHANGELOG
2
2
 
3
3
 
4
+ ## 2.0.0 (14 May 2021)
5
+
6
+ * Total rewrite from scratch.
7
+
8
+
4
9
  ## 1.4.0 (12 October 2016)
5
10
 
6
11
  * Internationalise emails' subject lines.
data/Gemfile CHANGED
@@ -1,3 +1,16 @@
1
- source "http://rubygems.org"
1
+ # frozen_string_literal: true
2
2
 
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in quo_vadis.gemspec
3
6
  gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rails", ">= 6" # from rodauth-rails
11
+ gem 'sqlite3'
12
+ gem 'capybara'
13
+ gem 'rotp'
14
+ gem 'rqrcode'
15
+
16
+ # gem "minitest", "~> 5.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,178 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ quo_vadis (2.0.0)
5
+ bcrypt (~> 3.1.7)
6
+ rails (>= 6)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ actioncable (6.1.1)
12
+ actionpack (= 6.1.1)
13
+ activesupport (= 6.1.1)
14
+ nio4r (~> 2.0)
15
+ websocket-driver (>= 0.6.1)
16
+ actionmailbox (6.1.1)
17
+ actionpack (= 6.1.1)
18
+ activejob (= 6.1.1)
19
+ activerecord (= 6.1.1)
20
+ activestorage (= 6.1.1)
21
+ activesupport (= 6.1.1)
22
+ mail (>= 2.7.1)
23
+ actionmailer (6.1.1)
24
+ actionpack (= 6.1.1)
25
+ actionview (= 6.1.1)
26
+ activejob (= 6.1.1)
27
+ activesupport (= 6.1.1)
28
+ mail (~> 2.5, >= 2.5.4)
29
+ rails-dom-testing (~> 2.0)
30
+ actionpack (6.1.1)
31
+ actionview (= 6.1.1)
32
+ activesupport (= 6.1.1)
33
+ rack (~> 2.0, >= 2.0.9)
34
+ rack-test (>= 0.6.3)
35
+ rails-dom-testing (~> 2.0)
36
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
37
+ actiontext (6.1.1)
38
+ actionpack (= 6.1.1)
39
+ activerecord (= 6.1.1)
40
+ activestorage (= 6.1.1)
41
+ activesupport (= 6.1.1)
42
+ nokogiri (>= 1.8.5)
43
+ actionview (6.1.1)
44
+ activesupport (= 6.1.1)
45
+ builder (~> 3.1)
46
+ erubi (~> 1.4)
47
+ rails-dom-testing (~> 2.0)
48
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
49
+ activejob (6.1.1)
50
+ activesupport (= 6.1.1)
51
+ globalid (>= 0.3.6)
52
+ activemodel (6.1.1)
53
+ activesupport (= 6.1.1)
54
+ activerecord (6.1.1)
55
+ activemodel (= 6.1.1)
56
+ activesupport (= 6.1.1)
57
+ activestorage (6.1.1)
58
+ actionpack (= 6.1.1)
59
+ activejob (= 6.1.1)
60
+ activerecord (= 6.1.1)
61
+ activesupport (= 6.1.1)
62
+ marcel (~> 0.3.1)
63
+ mimemagic (~> 0.3.2)
64
+ activesupport (6.1.1)
65
+ concurrent-ruby (~> 1.0, >= 1.0.2)
66
+ i18n (>= 1.6, < 2)
67
+ minitest (>= 5.1)
68
+ tzinfo (~> 2.0)
69
+ zeitwerk (~> 2.3)
70
+ addressable (2.7.0)
71
+ public_suffix (>= 2.0.2, < 5.0)
72
+ bcrypt (3.1.16)
73
+ builder (3.2.4)
74
+ capybara (3.35.3)
75
+ addressable
76
+ mini_mime (>= 0.1.3)
77
+ nokogiri (~> 1.8)
78
+ rack (>= 1.6.0)
79
+ rack-test (>= 0.6.3)
80
+ regexp_parser (>= 1.5, < 3.0)
81
+ xpath (~> 3.2)
82
+ chunky_png (1.4.0)
83
+ concurrent-ruby (1.1.8)
84
+ crass (1.0.6)
85
+ erubi (1.10.0)
86
+ globalid (0.4.2)
87
+ activesupport (>= 4.2.0)
88
+ i18n (1.8.8)
89
+ concurrent-ruby (~> 1.0)
90
+ loofah (2.9.0)
91
+ crass (~> 1.0.2)
92
+ nokogiri (>= 1.5.9)
93
+ mail (2.7.1)
94
+ mini_mime (>= 0.1.1)
95
+ marcel (0.3.3)
96
+ mimemagic (~> 0.3.2)
97
+ method_source (1.0.0)
98
+ mimemagic (0.3.5)
99
+ mini_mime (1.0.2)
100
+ mini_portile2 (2.5.0)
101
+ minitest (5.14.1)
102
+ nio4r (2.5.5)
103
+ nokogiri (1.11.1)
104
+ mini_portile2 (~> 2.5.0)
105
+ racc (~> 1.4)
106
+ nokogiri (1.11.1-x86_64-darwin)
107
+ racc (~> 1.4)
108
+ public_suffix (4.0.6)
109
+ racc (1.5.2)
110
+ rack (2.2.3)
111
+ rack-test (1.1.0)
112
+ rack (>= 1.0, < 3)
113
+ rails (6.1.1)
114
+ actioncable (= 6.1.1)
115
+ actionmailbox (= 6.1.1)
116
+ actionmailer (= 6.1.1)
117
+ actionpack (= 6.1.1)
118
+ actiontext (= 6.1.1)
119
+ actionview (= 6.1.1)
120
+ activejob (= 6.1.1)
121
+ activemodel (= 6.1.1)
122
+ activerecord (= 6.1.1)
123
+ activestorage (= 6.1.1)
124
+ activesupport (= 6.1.1)
125
+ bundler (>= 1.15.0)
126
+ railties (= 6.1.1)
127
+ sprockets-rails (>= 2.0.0)
128
+ rails-dom-testing (2.0.3)
129
+ activesupport (>= 4.2.0)
130
+ nokogiri (>= 1.6)
131
+ rails-html-sanitizer (1.3.0)
132
+ loofah (~> 2.3)
133
+ railties (6.1.1)
134
+ actionpack (= 6.1.1)
135
+ activesupport (= 6.1.1)
136
+ method_source
137
+ rake (>= 0.8.7)
138
+ thor (~> 1.0)
139
+ rake (13.0.3)
140
+ regexp_parser (2.0.3)
141
+ rotp (6.2.0)
142
+ rqrcode (1.2.0)
143
+ chunky_png (~> 1.0)
144
+ rqrcode_core (~> 0.2)
145
+ rqrcode_core (0.2.0)
146
+ sprockets (4.0.2)
147
+ concurrent-ruby (~> 1.0)
148
+ rack (> 1, < 3)
149
+ sprockets-rails (3.2.2)
150
+ actionpack (>= 4.0)
151
+ activesupport (>= 4.0)
152
+ sprockets (>= 3.0.0)
153
+ sqlite3 (1.4.2)
154
+ thor (1.1.0)
155
+ tzinfo (2.0.4)
156
+ concurrent-ruby (~> 1.0)
157
+ websocket-driver (0.7.3)
158
+ websocket-extensions (>= 0.1.0)
159
+ websocket-extensions (0.1.5)
160
+ xpath (3.2.0)
161
+ nokogiri (~> 1.8)
162
+ zeitwerk (2.4.2)
163
+
164
+ PLATFORMS
165
+ ruby
166
+ x86_64-darwin-18
167
+
168
+ DEPENDENCIES
169
+ capybara
170
+ quo_vadis!
171
+ rails (>= 6)
172
+ rake (~> 13.0)
173
+ rotp
174
+ rqrcode
175
+ sqlite3
176
+
177
+ BUNDLED WITH
178
+ 2.2.8
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Andy Stewart
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md CHANGED
@@ -1,211 +1,519 @@
1
- # Quo Vadis?
1
+ # Quo Vadis
2
2
 
3
- Quo Vadis adds simple username/password authentication to Rails 3 applications.
3
+ Multifactor authentication for your Rails 6 app.
4
4
 
5
- Why bother with yet another authentication gem? Well, I find all the others over-engineered. Code should be easy to use and easy to read. As far as I'm concerned, none of the others ticks both boxes.
5
+ Designed in accordance with the [OWASP Application Security Verification Standard](https://owasp.org/www-project-application-security-verification-standard/) and relevant [OWASP Cheatsheets](https://cheatsheetseries.owasp.org).
6
6
 
7
- Features:
7
+ Simple to integrate into your application. The main task is customising the example views' markup to match your look-and-feel.
8
8
 
9
- * Minimal effort to add authentication to your app: get up and running in 5 minutes.
10
- * No surprises: it does what you expect.
11
- * Easy to customise.
12
- * Uses BCrypt to encrypt passwords.
13
- * Sign in, sign out, forgotten password, authenticate actions, remember user between browser sessions, user activation.
14
- * Block accounts.
15
- * Let you choose which model(s) to authenticate (defaults to `User`).
16
9
 
17
- Forthcoming features:
10
+ ## Features
18
11
 
19
- * Generate the views for you (for now, copy the examples given below).
20
- * Let you choose the identification field (currently `username`).
21
- * HTTP basic/digest authentication (probably).
22
- * Generate model plus migration if it doesn't exist.
23
- * Detect presence of `has_secure_password` (see below) and adapt appropriately.
12
+ ### General features
24
13
 
25
- What it doesn't and won't do:
14
+ - Works with any model, e.g. `User` or `Person`.
15
+ - Works with any identifier, e.g. `:username` or `:email`.
16
+ - Minimal footprint in your models and controllers.
17
+ - Does not touch your existing database tables.
18
+ - Secrets (password, TOTP secret, 2FA recovery codes) are encrypted at rest.
26
19
 
27
- * Authorisation.
28
- * Work outside Rails 3.
29
- * OpenID, OAuth, LDAP, CAS, etc.
30
- * Separate identity from authentication services (cf OmniAuth).
31
- * Allow you to have multiple models/scope signed in simultaneously (cf Devise).
32
- * Offer so much flexibility that it takes more than 10 minutes to wrap your head around it (cf Devise, Authlogic).
20
+ ### Authentication features
33
21
 
22
+ - Authentication by password.
23
+ - Two-factor authentication (2FA) by TOTP with recovery codes as a backup factor. Can be optional or mandatory.
24
+ - Change password.
25
+ - Reset password.
26
+ - Account confirmation (optional).
27
+ - Tokens (account confirmation, password reset), TOTPs, and recovery codes are all one-time-only.
28
+ - Sessions expired after lifetime or idle time exceeded.
29
+ - Session replaced after any privilege change.
30
+ - View active sessions, log out of any of them.
31
+ - Email-notifications of updates to authentication details.
32
+ - Audit trail.
34
33
 
35
- ## Quick Start
36
34
 
37
- If this takes you more than 5 minutes, you can have your money back ;)
35
+ ## Installation
38
36
 
39
- Install and run the generator: add `gem 'quo_vadis'` to your Gemfile, run `bundle install`, then `rails generate quo_vadis:install [MODEL_NAME]` (where model name is optional and defaults to `User`).
37
+ Add the gem to your Gemfile:
40
38
 
41
- Edit and run the generated migration to add the authentication columns: `rake db:migrate`. Note the migration (currently) assumes you already have a table for your model.
39
+ ```ruby
40
+ gem 'quo_vadis', '~> 2.0'
41
+ ```
42
42
 
43
- In your `User` (or whichever) model, add `authenticates`:
43
+ Then run `bundle install`.
44
44
 
45
- class User < ActiveRecord::Base
46
- authenticates
47
- end
45
+ Next, add the database tables:
48
46
 
49
- Note Quo Vadis validates the presence and uniqueness of the username, and the presence of the password, but it's up to you to add any other validations you want.
47
+ ```
48
+ $ rails quo_vadis:install:migrations
49
+ $ rails db:migrate
50
+ ```
50
51
 
51
- Use `:authenticate` in a `before_filter` to protect your controllers' actions. For example:
52
+ All the database tables are prefixed with `qv_`.
52
53
 
53
- class ArticlesController < ActionController::Base
54
- before_filter :authenticate, :except => [:index, :show]
55
- end
54
+ Finally, copy the example views across:
56
55
 
57
- Write the sign-in view. Your sign-in form must:
56
+ ```
57
+ $ rails generate quo_vadis:install
58
+ ```
58
59
 
59
- * be in `app/views/sessions/new.html.:format`
60
- * POST the parameters `:username` and `:password` to `sign_in_url`
61
60
 
62
- You have to write the view yourself because you'd inevitably want to change whatever markup I generated for you. You can find an example in the [test app](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/sessions/new.html.erb).
61
+ ## Usage
63
62
 
64
- Remember to serve your sign in form over HTTPS -- to avoid [the credentials being stolen](http://blog.jgc.org/2011/01/code-injected-to-steal-passwords-in.html).
65
63
 
66
- In your layout, use `current_user` to retrieve the signed-in user; and `sign_in_path`, `sign_out_path`, and `forgotten_sign_in_path` as appropriate. You can also use `authenticated?`.
64
+ ### Model
67
65
 
66
+ Your model must have an `:email` attribute. All authentication-related emails will be sent to this address.
68
67
 
69
- ## Forgotten Password
68
+ Your model must have an identifier, e.g. `:email` (default) or `:username`, with a uniqueness validation.
70
69
 
71
- Here's the workflow:
70
+ All you need do is add a call to `authenticates`, somewhere after your identifier's uniqueness validation.
72
71
 
73
- 1. [Sign in page] The user clicks the "I've forgotten my password" link.
74
- 2. [Forgotten password page] The user enters their username in the form and submits it.
75
- 3. Quo Vadis emails the user a message with a change-password link. The link is valid for 3 hours.
76
- 4. [The email] The user clicks the link.
77
- 5. [Change password page] The user types in a new password and saves it.
78
- 6. Quo Vadis changes the user's password and signs the user in.
72
+ For example, let's say you have a `User` model and the identifier is `:email`:
79
73
 
80
- It'll take you about 5 minutes to implement this.
74
+ ```ruby
75
+ class User < ApplicationRecord
76
+ validates :email, uniqueness: {case_sensitive: false}
77
+ authenticates
78
+ end
79
+ ```
81
80
 
82
- On your sign-in page, link to the forgotten-password view at `forgotten_sign_in_url`.
81
+ If instead you had a `Person` model with a `:username` identifier:
83
82
 
84
- Write the forgotten-password view ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/sessions/forgotten.html.erb)). The form must:
83
+ ```ruby
84
+ class Person < ApplicationRecord
85
+ validates :username, uniqueness: {case_sensitive: false}
86
+ authenticates identifier: :username
87
+ end
88
+ ```
85
89
 
86
- * be in `app/views/sessions/forgotten.html.:format`
87
- * POST the parameter `:username` to `forgotten_sign_in_url`
90
+ When __creating__ a model instance, include a `:password` attribute and, optionally, `:password_confirmation` attribute.
88
91
 
89
- Now write the mailer view, i.e. the email which will be sent to your forgetful users ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/notifier/change_password.text.erb)). The view must:
92
+ When __updating__ a model instance, do not include a `:password` attribute. To change someone's password, use the Change Password feature (see below).
90
93
 
91
- * be at `app/views/quo_vadis/notifier/change_password.text.erb`
92
- * render `@url` somewhere (this is the link the user clicks to go to the change-password page)
94
+ The minimum password length is configured by `QuoVadis.password_minimum_length` (12 by default).
93
95
 
94
- You can also refer to `@username` in the email view.
95
96
 
96
- Configure the email's from address in `config/initializers/quo_vadis.rb`.
97
+ ### Controllers
97
98
 
98
- Configure the default host so ActionMailer can generate the URL. In `config/environments/<env>.rb`:
99
+ You can use these methods in your controllers.
99
100
 
100
- config.action_mailer.default_url_options = {:host => 'yourdomain.com'}
101
+ __`require_password_authentication`__
101
102
 
102
- Finally, write the change-password page ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/sessions/edit.html.erb)). The form must:
103
+ Use this to restrict actions to password-authenticated users. It is aliased to `:require_authentication` for convenience.
103
104
 
104
- * be in `app/views/sessions/edit.html.:format`
105
- * PUT the parameter `:password` to `change_password_url(params[:token])`
105
+ ```ruby
106
+ class FoosController < ApplicationController
107
+ before_action :require_password_authentication
108
+ end
109
+ ```
106
110
 
111
+ __`require_two_factor_authentication`__
107
112
 
108
- ## Sign up (directly, without activation)
113
+ Use this to restrict actions to users authenticated with both a password and a second factor. (You do not need to use `:require_password_authentication` for these actions.)
109
114
 
110
- When you create a user, you need to sign them in. Do this by calling `sign_in(user)` in your controller. For example:
115
+ ```ruby
116
+ class BarsController < ApplicationController
117
+ before_action :require_two_factor_authentication
118
+ end
119
+ ```
111
120
 
112
- # In your app
113
- class UsersController < ApplicationController
114
- def create
115
- @user = User.new params[:user]
116
- if @user.save
117
- sign_in @user # <-- NOTE: sign in your user here
118
- else
119
- render 'new'
120
- end
121
- end
122
- end
121
+ __`login(model, browser_session = true)`__
122
+
123
+ To log in a user who has authenticated with a password, call `#login(model, browser_session = true)`. For the `browser_session` argument, pass `true` to log in for the duration of the browser session, or `false` to log in for `QuoVadis.session_lifetime` (which could be the browser session anyway).
124
+
125
+ __`request_confirmation(model)`__
126
+
127
+ This is used to sent an account confirmation email to the user. See the Account Confirmation feature below for details.
128
+
129
+ __`authenticated_model`__
130
+
131
+ Call this to get the authenticated user. Feel free to alias this to `:current_user` or set it into an `ActiveSupport::CurrentAttributes` class.
132
+
133
+ Available in controllers and views.
134
+
135
+ __`logged_in?`__
136
+
137
+ Call this to find out whether a user has authenticated with a password.
138
+
139
+ Available in controllers and views.
140
+
141
+
142
+ ### Views
143
+
144
+ You can use `authenticated_model` and `logged_in?` in your views. For example:
123
145
 
124
- The `sign_in(user)` method will redirect the user appropriately (you can configure this in `config/initializers/quo_vadis.rb`), as well as running any sign-in hook you may have defined in the initializer.
146
+ ```erb
147
+ <% if logged_in? %>
148
+ <%= link_to 'My profile', authenticated_model %>
149
+ <% end %>
150
+ ```
125
151
 
152
+ In your own views, you must prefix QuoVadis's routes with `quo_vadis.`. For example:
126
153
 
127
- ## Sign up (with activation)
154
+ ```ruby
155
+ link_to 'Log in', quo_vadis.login_path
156
+ ```
128
157
 
129
- To create a user who must activate their account (via email) before they can sign in, do this:
158
+ When you are customising QuoVadis's views, you must prefix your app's routes with `main_app.`. For example:
130
159
 
131
- # In your app
132
- class UsersController < ApplicationController
133
- def create
134
- @user = User.new_for_activation params[:user] # <-- NOTE: different constructor
135
- if @user.save
136
- QuoVadis::SessionsController.new.invite_to_activate @user # <-- NOTE: email user here
137
- redirect_to root_path, notice: "Emailed sign-in instructions to #{@user.name}" # or whatever
138
- else
139
- render 'new'
140
- end
141
- end
160
+ ```ruby
161
+ link_to 'Home', main_app.root_path
162
+ ```
163
+
164
+
165
+ ## Features
166
+
167
+ The example views show the forms and fields you need. You should only need to adapt the markup to suit your app's appearance.
168
+
169
+ In the snippets below we assume a `User` model whose identifier is `:email`. You can of course use anything you like.
170
+
171
+
172
+ ### Sign up
173
+
174
+ Your new user sign-up form ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/users/new.html.erb)) must include:
175
+
176
+ - a `:password` field;
177
+ - optionally a `:password_confirmation` field;
178
+ - a field for their identifier;
179
+ - an `:email` field if the identifier is not their email.
180
+
181
+ In your controller, use the `#login` method to log in your new user. The optional second argument sets the length of the session (defaults to the browser session) - see the description above in the Controllers section).
182
+
183
+ After logging in the user, redirect them to `qv.path_after_authentication` which resolves to a route named `:after_login`, if you have one, or your root route.
184
+
185
+ ```ruby
186
+ class UsersController < ApplicationController
187
+ def create
188
+ @user = User.new user_params
189
+ if @user.save
190
+ login @user
191
+ redirect_to qv.path_after_authentication
192
+ else
193
+ # ...
142
194
  end
195
+ end
196
+
197
+ private
198
+
199
+ def user_params
200
+ params.require(:user).permit(:name, :email, :password, :password_confirmation)
201
+ end
202
+ end
203
+ ```
204
+
205
+ ```ruby
206
+ # config/routes.rb
207
+ get '/dashboard', as: 'after_login'
208
+ ```
209
+
143
210
 
144
- The user will receive an email with a link which takes them to a page (which you must write) where they can choose a username and password for themselves. When they submit the form their new credentials are stored and they are signed in.
211
+ ### Sign up with account confirmation
145
212
 
146
213
  Here's the workflow:
147
214
 
148
- 1. [New user page, without username/password fields] You or user fills in and submits form.
149
- 2. [Users controller] Create user and invite to activate. See code snippet above.
150
- 3. Quo Vadis emails the user a message with an invitation link. The link is valid for 3 hours.
151
- 4. [The email] The user clicks the link.
152
- 5. [Invitation page] The user fills in their new username and password.
153
- 6. Quo Vadis sets the user's username and password and signs the user in.
215
+ 1. [Sign up page] The user fills in their details.
216
+ 2. [Your controller] Your code tells QuoVadis to email the user a confirmation link. The link is valid for `QuoVadis.account_confirmation_token_lifetime`.
217
+ 3. [The email] The user clicks the link.
218
+ 4. [Account-confirmation confirmation page] The user clicks a button to confirm their account. (This step is to prevent any link prefetching in the user's mail client from confirming them unintentionally.)
219
+ 5. QuoVadis confirms the user's account and logs them in.
220
+
221
+ Your new user sign-up form ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/users/new.html.erb)) must include:
222
+
223
+ - a `:password` field;
224
+ - optionally a `:password_confirmation` field;
225
+ - a field for their identifier;
226
+ - an `:email` field if the identifier is not their email.
227
+
228
+ In your controller, call `#request_confirmation`:
229
+
230
+ ```ruby
231
+ class UsersController < ApplicationController
232
+ def create
233
+ @user = User.new user_params
234
+ if @user.save
235
+ request_confirmation @user
236
+ redirect_to qv.confirmations_path # a page where you advise the user to check their email
237
+ else
238
+ # ...
239
+ end
240
+ end
241
+
242
+ private
243
+
244
+ def user_params
245
+ params.require(:user).permit(:name, :email, :password, :password_confirmation)
246
+ end
247
+ end
248
+ ```
249
+
250
+ QuoVadis will send the user an email with a link. Write the email view ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/mailer/account_confirmation.text.erb)). It must be in `app/views/quo_vadis/mailer/account_confirmation.{text,html}.erb` and output the `@url` variable.
251
+
252
+ See the Configuration section below for how to set QuoVadis's emails' from addresses, headers, etc.
253
+
254
+ Now write the page to where the user is redirected while they wait for the email ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/confirmations/index.html.erb)). It must be in `app/views/quo_vadis/confirmations/index.html.:format`.
255
+
256
+ It's a good idea for that page to link to `new_confirmation_path` where the user can request another email if need be.
257
+
258
+ Next, write the page to which the link in the email points ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/confirmations/edit.html.erb)). It must be in `app/views/quo_vadis/confirmations/edit.html.:format`.
259
+
260
+ Finally, write the page where people can put in their identifier (not their email, unless the identifier is email) again to request another confirmation email ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/confirmations/new.html.erb)). It must be in `app/views/quo_vadis/confirmations/new.html.:format`.
261
+
262
+ After the user has confirmed their account, they will be logged in and redirected to the first of these that exists:
263
+
264
+ - a route named `:after_login`;
265
+ - your root route.
266
+
267
+ So add whichever works best for you.
268
+
269
+
270
+ ### Login
271
+
272
+ Use `before_action :require_password_authentication` or `before_action :require_authentication` in your controllers.
273
+
274
+ Write the login view ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/sessions/new.html.erb)). Your login form must be in `app/views/quo_vadis/sessions/new.html.:format`. Note it must capture the user's identifier (not email, unless the identifier is email).
275
+
276
+ If you include a `remember` checkbox in your login form:
277
+
278
+ - if the user checks it, they will be logged in for `QuoVadis.session_lifetime`;
279
+ - if the user does not check it, they will be logged in for the browser session.
280
+
281
+ If you do not include a `remember` checkbox, the user will be logged in for `QuoVadis.session_lifetime`.
282
+
283
+ After authenticating the user will be redirected to the first of these that exists:
284
+
285
+ - the page they tried to view before they were redirected to the login page;
286
+ - a route named `after_login`, if any;
287
+ - your root route.
288
+
289
+
290
+ ### Two-factor authentication (2FA) or Two-step verification (2SV)
291
+
292
+ If you do not want 2FA at all, set `QuoVadis.two_factor_authentication_mandatory false` in your configuration and skip the rest of this section.
293
+
294
+ If you do want 2FA, you can choose whether it is optional or mandatory for your users by setting `QuoVadis.two_factor_authentication_mandatory <true|false>` in your configuration.
295
+
296
+ Use `before_action :require_two_factor_authentication` in your controllers (which supersedes `:require_password_authentication`). This will require the user, after authenticating with their password, to authenticate with 2FA – when 2FA is mandatory, or when it is optional and the user has set up 2FA.
297
+
298
+ Here's the workflow for a user setting up optional 2FA:
299
+
300
+ 1. User visits their 2FA overview page.
301
+ 2. [2FA overview page] User clicks a link to set up 2FA (TOTP for now).
302
+ 3. [TOTP setup page] User scans the QR code with their authenticator and enters the 6-digit one-time password.
303
+ 4. QuoVadis verifies the one-time password, generates 5 backup recovery codes, and redirects the user to the recovery codes page (or back to step 3 if the OTP is invalid).
304
+ 5. [Recovery code page] User views and hopefully saves their 5 recovery codes.
305
+
306
+ When 2FA is mandatory the workflow starts automatically at step 3 after password authentication.
307
+
308
+ In your views, have a link where users can manage their 2FA:
309
+
310
+ ```ruby
311
+ link_to '2FA', quo_vadis.twofa_path
312
+ ```
313
+
314
+ Write the 2FA overview page ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/twofas/show.html.erb)). It must be in `app/views/quo_vadis/twofas/show.html.:format`. This page allows the user to set up 2FA, deactivate or reset it, and generate new recovery codes.
315
+
316
+ Next, write the TOTP setup page ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/totps/new.html.erb)). It must be in `app/views/quo_vadis/totps/new.html.:format`. This page shows the user a QR code (and the key as text) which they scan with their authenticator.
317
+
318
+ Next, write the recovery codes page ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/recovery_codes/index.html.erb)). It must be in `app/views/quo_vadis/recovery_codes/index.html.:format`. This shows the recovery codes immediately after TOTP is setup, and immediately after generating fresh recovery codes, but not otherwise.
319
+
320
+ Next, write the TOTP challenge page where a user inputs their 6-digit TOTP ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/totps/challenge.html.erb)). It must be in `app/views/quo_vadis/totps/challenge.html.:format`. It's a good idea to link to the recovery code page (`challenge_recovery_codes_path`) for any user who has lost their authenticator.
321
+
322
+ Finally, write the recovery code challenge page where a user inputs one of their recovery codes ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/recovery_codes/challenge.html.erb)). It must be in `app/views/quo_vadis/recovery_codes/challenge.html.:format`. A recovery code can only be used once, and using one deactivates TOTP – so the user will have to set it up again next time.
323
+
324
+
325
+ ### Change password
326
+
327
+ To change their password, the user must provide their current one as well as the new one.
328
+
329
+ Write the change-password form ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/passwords/edit.html.erb)). It must be in `app/views/quo_vadis/passwords/edit.html.:format`.
330
+
331
+ After the password has been changed, the user is redirected to the first of:
332
+
333
+ - your route named `:after_password_change`, if any;
334
+ - your root route.
335
+
336
+ A successful password change logs out any other sessions the user has (e.g. on other devices).
337
+
338
+
339
+ ### Reset password
340
+
341
+ The user can reset their password if they lose it. The flow is:
342
+
343
+ 1. [Request password-reset page] User enters their identifier (not their email unless the identifier is email).
344
+ 2. QuoVadis emails the user a link. The link is valid for `QuoVadis.password_reset_token_lifetime`.
345
+ 3. [The email] The user clicks the link.
346
+ 4. [Password-reset confirmation page] The user enters their new password and clicks a button.
347
+ 5. QuoVadis sets the user's password and logs them in.
348
+
349
+ First, write the page where the user requests a password-reset ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/password_resets/new.html.erb)). It must be in `app/views/quo_vadis/password_resets/new.html.:format`. Note it must capture the user's identifier (not email, unless the identifier is email).
350
+
351
+ See the Configuration section below for how to set QuoVadis's emails' from addresses, headers, etc.
352
+
353
+ Now write the page to where the user is redirected while they wait for the email ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/password_resets/index.html.erb)). It must be in `app/views/quo_vadis/password_resets/index.html.:format`.
354
+
355
+ It's a good idea for that page to link to `new_password_reset_path` where the user can request another email if need be.
356
+
357
+ Next, write the page to which the link in the email points ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/password_resets/edit.html.erb)). It must be in `app/views/quo_vadis/password_resets/edit.html.:format`.
358
+
359
+ Finally, write the page where people can put in their identifier (not their email, unless the identifier is email) again to request another password-reset email ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/password_resets/new.html.erb)). It must be in `app/views/quo_vadis/password_resets/new.html.:format`.
360
+
361
+ After the user has reset their password, they will be logged in and redirected to the first of these that exists:
362
+
363
+ - a route named `:after_login`;
364
+ - your root route.
365
+
366
+
367
+ ### Sessions
368
+
369
+ A logged-in session lasts for either the browser session or `QuoVadis.session_lifetime`. As well as having a lifetime, a session will also expire after it has been inactive for `QuoVadis.session_idle_timeout`.
370
+
371
+ A user can view their active sessions and log out of any of them.
372
+
373
+ Write the view showing the sessions ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/sessions/index.html.erb)). It must be in `app/views/quo_vadis/sessions/index.html.:format`.
374
+
375
+
376
+ ### Audit trail
377
+
378
+ An audit trail is kept of authentication events. You can see the full list in the [`Log`](https://github.com/airblade/quo_vadis/blob/master/app/models/quo_vadis/log.rb) class.
379
+
380
+ Write the view showing the events ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/logs/index.html.erb)). It must be in `app/views/quo_vadis/logs/index.html.:format`.
381
+
382
+
383
+ ### Notifications
384
+
385
+ QuoVadis notifies users by email whenever their authentication details are changed or something suspicious happens.
386
+
387
+ Write the corresponding mailer views:
388
+
389
+ - change of email ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/mailer/email_change_notification.text.erb))
390
+ - change of identifier (unless the identifier is email) ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/mailer/identifier_change_notification.text.erb))
391
+ - change of password ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/mailer/password_change_notification.text.erb))
392
+ - reset of password ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/mailer/password_reset_notification.text.erb))
393
+ - TOTP setup ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/mailer/totp_setup_notification.text.erb))
394
+ - TOTP code used a second time ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/mailer/totp_reuse_notification.text.erb))
395
+ - 2FA deactivated ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/mailer/twofa_deactivated_notification.text.erb))
396
+ - recovery codes generated ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/mailer/recovery_codes_generation_notification.text.erb))
397
+
398
+ They must be in `app/views/quo_vadis/mailer/NAME.{text,html}.erb`.
399
+
400
+
401
+ ## Configuration
402
+
403
+ This is QuoVadis' [default configuration](https://github.com/airblade/quo_vadis/blob/master/lib/quo_vadis/defaults.rb):
404
+
405
+ ```ruby
406
+ QuoVadis.configure do
407
+ password_minimum_length 12
408
+ mask_ips false
409
+ cookie_name '__Host-qv'
410
+ session_lifetime :session
411
+ session_lifetime_extend_to_end_of_day false
412
+ session_idle_timeout :lifetime
413
+ password_reset_token_lifetime 10.minutes
414
+ accounts_require_confirmation false
415
+ account_confirmation_token_lifetime 10.minutes
416
+ mail_headers ({ from: 'Example App <support@example.com>' })
417
+ enqueue_transactional_emails true
418
+ app_name Rails.app_class.to_s.deconstantize # for the TOTP QR code
419
+ two_factor_authentication_mandatory true
420
+ mount_point '/'
421
+ end
422
+ ```
423
+
424
+ You can override any of it with a similarly structured file in `config/initializers/quo_vadis.rb`.
425
+
426
+ Here are the options in detail:
427
+
428
+ __`password_minimum_length`__ (integer)
429
+
430
+ The minimum number of characters for a password.
431
+
432
+ __`mask_ips`__ (boolean)
433
+
434
+ Whether to mask the IP address in the sessions list and the audit trail.
435
+
436
+ Masking means setting the last octet (IPv4) or the last 80 bits (IPv6) to 0.
437
+
438
+ __`cookie_name`__ (string)
439
+
440
+ The name of the cookie QuoVadis uses to store the session identifier. The `__Host-` prefix is [recommended](https://developer.mozilla.org/en-US/docs/Web/API/document/cookie).
441
+
442
+ __`session_lifetime`__ (`:session` | `ActiveSupport::Duration` | integer)
443
+
444
+ The lifetime of a logged-in session. Use `:session` for the browser session, or a `Duration` or number of seconds.
445
+
446
+ __`session_lifetime_extend_to_end_of_day`__ (boolean)
447
+
448
+ Whether to extend the session's lifetime to the end of the day it will expire on.
449
+
450
+ Set `true` to reduce the chance of a user being logged out while actively using your application.
451
+
452
+ __`session_idle_timeout`__ (`:lifetime` | `ActiveSupport::Duration` | integer)
453
+
454
+ The logged-in session is expired if the user isn't seen for this `Duration` or number of seconds. Use `:lifetime` to set the idle timeout to the session's lifetime (i.e. to turn off the idle timeout).
154
455
 
155
- It'll take you about 3 minutes to implement this.
456
+ __`password_reset_token_lifetime`__ (`ActiveSupport::Duration` | integer)
156
457
 
157
- Update your user controller's `create` action as above.
458
+ The `Duration` or number of seconds for which a password-reset token is valid.
158
459
 
159
- Write the mailer view, i.e. the email which will be sent to your new users ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/quo_vadis/notifier/invite.text.erb)). The view must:
460
+ __`accounts_require_confirmation`__ (boolean)
160
461
 
161
- * be at `app/views/quo_vadis/notifier/invite.text.erb`
162
- * render `@url` somewhere (this is the link the user clicks to go to the invitation page)
462
+ Whether new users must confirm their account before they can log in.
163
463
 
164
- You can also refer to `@user` in the email view, as well as any other data you pass to `invite_to_activate`. Note that passing `:from` and/or `:subject` in the hash to `invite_to_activate` overrides the default `QuoVadis.from` and/or `I18n.t('quo_vadis.notifier.invite.subject')` respectively.
464
+ __`account_confirmation_token_lifetime`__ (`ActiveSupport::Duration` | integer)
165
465
 
166
- Configure the email's from address in `config/initializers/quo_vadis.rb` (or pass in the data hash to `invite_to_activate`).
466
+ The `Duration` or number of seconds for which an account-confirmation token is valid.
167
467
 
168
- Configure the default host so ActionMailer can generate the URL. In `config/environments/<env>.rb`:
468
+ __`mail_headers`__ (hash)
169
469
 
170
- config.action_mailer.default_url_options = {:host => 'yourdomain.com'}
470
+ Mail headers which QuoVadis' emails should have.
171
471
 
172
- Finally, write the invitation page ([example](https://github.com/airblade/quo_vadis/blob/master/test/dummy/app/views/sessions/invite.html.erb)). The form must:
472
+ __`enqueue_transactional_emails`__ (boolean)
173
473
 
174
- * be in `app/views/sessions/invite.html.:format`
175
- * POST the parameters `:username` and `:password` to `activation_url(params[:token])`
474
+ Set `true` if account-confirmation and password-reset emails should be queued for later delivery (`#deliver_later`) or `false` if they should be sent inline (`#deliver_now`).
176
475
 
177
- If the token expires and you need to generate a new one, re-invite the user with: `invite_to_activate @user`.
476
+ __`app_name`__ (string)
178
477
 
478
+ Used in the provisioning URI for the TOTP QR code.
179
479
 
180
- ## Customisation
480
+ __`two_factor_authentication_mandatory`__ (boolean)
181
481
 
182
- You can customise the flash messages and mailer from/subject in `config/locales/quo_vadis.en.yml`.
482
+ Whether users must set up and use a second authentication factor.
183
483
 
184
- You can customise the sign-in and sign-out redirects in `config/initializers/quo_vadis.rb`; they both default to the root route. You can also hook into the sign-in and sign-out process if you need to run any other code.
484
+ __`mount_point`__ (string)
185
485
 
186
- If you want to add other session management type features, go right ahead: create a `SessionsController` as normal and carry on.
486
+ The path prefix for QuoVadis's routes.
187
487
 
188
- You can skip the validation of authentication attributes (password etc) by overriding `should_authenticate?` in your model. Perhaps only some of the users should be able to sign in, so you don't want to force them to have a password.
488
+ For example, the default login path is at `/login`. If you set `mount_point` to `/auth`, the login path would be `/auth/login`.
189
489
 
490
+ #### Rails configuration
190
491
 
191
- ## See also
492
+ You must also configure the mailer host so URLs are generated correctly in emails:
192
493
 
193
- * Rails 3 edge's [ActiveModel::SecurePassword](https://github.com/rails/rails/blob/master/activemodel/lib/active_model/secure_password.rb). It's `has_secure_password` class method is similar to Quo Vadis's `authenticates` class method.
194
- * [RailsCast 250: Authentication from Scratch](http://railscasts.com/episodes/250-authentication-from-scratch).
494
+ ```ruby
495
+ config.action_mailer.default_url_options: { host: 'example.com }
496
+ ```
195
497
 
498
+ Finally, you can set up your post-authentication and post-password-change routes. If you don't, you must have a root route. For example:
196
499
 
197
- ## What's up with the name?
500
+ ```ruby
501
+ # config/routes.rb
502
+ get '/dashboard', to: 'dashboards#show', as: 'after_login'
503
+ get '/profile', to: 'profiles#show', as: 'after_password_change'
504
+ ```
198
505
 
199
- Roman sentries used to challenge intruders with, "Halt! Who goes there?"; quo vadis is Latin for "Who goes there?". At least that's what my Latin teacher told us, but I was 8 years old then so I may not be remembering this entirely accurately.
506
+ ### I18n
200
507
 
508
+ All QuoVadis' flash messages are set via [i18n](https://github.com/airblade/quo_vadis/blob/master/config/locales/quo_vadis.en.yml).
201
509
 
202
- ## Questions, Problems, Feedback
510
+ You can override any of the messages with your own locale file at `config/locales/quo_vadis.en.yml`.
203
511
 
204
- Please use the GitHub [issue tracker](https://github.com/airblade/quo_vadis/issues) or email me.
512
+ If you don't want a specific flash message at all, give the key an empty value in your locale file.
205
513
 
206
514
 
207
- ## Intellectual property
515
+ ## Intellectual Property
208
516
 
209
- Copyright 2011 Andy Stewart (boss@airbladesoftware.com).
517
+ Copyright 2011-2021 Andrew Stewart (boss@airbladesoftware.com).
210
518
 
211
519
  Released under the MIT licence.