rodauth 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +18 -0
- data/doc/guides/admin_activation.rdoc +46 -0
- data/doc/guides/already_authenticated.rdoc +10 -0
- data/doc/guides/alternative_login.rdoc +46 -0
- data/doc/guides/create_account_programmatically.rdoc +38 -0
- data/doc/guides/delay_password.rdoc +25 -0
- data/doc/guides/email_only.rdoc +16 -0
- data/doc/guides/i18n.rdoc +26 -0
- data/doc/{internals.rdoc → guides/internals.rdoc} +0 -0
- data/doc/guides/links.rdoc +12 -0
- data/doc/guides/login_return.rdoc +37 -0
- data/doc/guides/password_column.rdoc +25 -0
- data/doc/guides/password_confirmation.rdoc +37 -0
- data/doc/guides/password_requirements.rdoc +30 -0
- data/doc/guides/paths.rdoc +36 -0
- data/doc/guides/query_params.rdoc +9 -0
- data/doc/guides/redirects.rdoc +17 -0
- data/doc/guides/registration_field.rdoc +68 -0
- data/doc/guides/require_mfa.rdoc +30 -0
- data/doc/guides/reset_password_autologin.rdoc +21 -0
- data/doc/guides/status_column.rdoc +28 -0
- data/doc/guides/totp_or_recovery.rdoc +16 -0
- data/doc/jwt_refresh.rdoc +6 -0
- data/doc/otp.rdoc +1 -0
- data/doc/release_notes/2.2.0.txt +39 -0
- data/lib/rodauth.rb +4 -4
- data/lib/rodauth/features/active_sessions.rb +5 -7
- data/lib/rodauth/features/audit_logging.rb +2 -0
- data/lib/rodauth/features/email_auth.rb +1 -1
- data/lib/rodauth/features/jwt.rb +5 -6
- data/lib/rodauth/features/jwt_cors.rb +15 -15
- data/lib/rodauth/features/jwt_refresh.rb +29 -6
- data/lib/rodauth/features/otp.rb +5 -1
- data/lib/rodauth/features/password_complexity.rb +4 -2
- data/lib/rodauth/features/single_session.rb +1 -1
- data/lib/rodauth/features/sms_codes.rb +0 -1
- data/lib/rodauth/features/two_factor_base.rb +4 -4
- data/lib/rodauth/features/verify_account.rb +4 -0
- data/lib/rodauth/features/verify_account_grace_period.rb +2 -4
- data/lib/rodauth/features/webauthn.rb +1 -3
- data/lib/rodauth/version.rb +1 -1
- metadata +25 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f672a0312d4a0c6103bb763d339161119551a0e64cc248546a0cb52f4e6784c1
|
4
|
+
data.tar.gz: 530d322cc3ddba655959238aafe155512de11f090afb9465980181dcc729d1b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8018f5e02d077bd8625c17c8c5a1f607b986c1916296132ef68e2651d2af26d65ddbdfdc9b5f1352431c74caa30e94d270cc03840207303f8fa0e6a44910a0b
|
7
|
+
data.tar.gz: 5f24ce4d800d3bbe914dac40dba1cd68b78af4aff5efdaf976b32702d45b5fe7809453d2a650f407437afb1b9d6206ff3d79e6f703fcd85d8d55a0f0658464eb
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
=== 2.2.0 (2020-07-20)
|
2
|
+
|
3
|
+
* Allow removing all jwt_refresh tokens when logging out by providing a value of "all" as the token to remove (jeremyevans)
|
4
|
+
|
5
|
+
* Allow removing specific jwt_refresh token when logging out by providing the token to remove (jeremyevans)
|
6
|
+
|
7
|
+
* Avoid NoMethodError when checking if session is authenticated when using two factor auth, verify_account_grace_period, and email_auth (jeremyevans) (#105)
|
8
|
+
|
9
|
+
* Reduce queries in #authenticated? and #require_authentication when using two factor authentication (janko) (#106)
|
10
|
+
|
11
|
+
* Treat verify_account_email_resend returning false as an error in the verify_account feature (jeremyevans)
|
12
|
+
|
13
|
+
* Fix use of password_dictionary configuration method in password_complexity feature (jeremyevans)
|
14
|
+
|
15
|
+
* Remove unnecessary conditionals (jeremyevans)
|
16
|
+
|
17
|
+
* Add otp_last_use to the otp feature, returning the time of last successful OTP use (jeremyevans) (#103)
|
18
|
+
|
1
19
|
=== 2.1.0 (2020-06-09)
|
2
20
|
|
3
21
|
* Do not check CSRF tokens by default for requests using JWT (janko, jeremyevans) (#99)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
= Require account verification by admin
|
2
|
+
|
3
|
+
There are scenarios in which, instead of allowing the user to verify they have
|
4
|
+
access to the email for the account, you may want to have an admin or moderator
|
5
|
+
approve new accounts manually. One way this can be achieved by sending the
|
6
|
+
account verification email to the admin:
|
7
|
+
|
8
|
+
plugin :rodauth do
|
9
|
+
enable :login, :logout, :verify_account, :reset_password
|
10
|
+
|
11
|
+
# Send account verification email to the admin
|
12
|
+
email_to do
|
13
|
+
if account[account_status_column] == account_unverified_status_value
|
14
|
+
"admin@myapp.com"
|
15
|
+
else
|
16
|
+
super()
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Do not ask for password when creating or verifying account
|
21
|
+
verify_account_set_password? false
|
22
|
+
create_account_set_password? false
|
23
|
+
|
24
|
+
# Adjust the account verification email subject and body
|
25
|
+
verify_account_email_subject "New User Awaiting Admin Approval"
|
26
|
+
verify_account_email_body do
|
27
|
+
"The user #{account[login_column]} has created an account. Click here to approve it: #{verify_account_email_link}."
|
28
|
+
end
|
29
|
+
|
30
|
+
# Display this message to the user after they've created their account
|
31
|
+
verify_account_email_sent_notice_flash "Your account has been created and is awaiting approval"
|
32
|
+
|
33
|
+
# Prevent the admin from being logged in after confirming the account
|
34
|
+
verify_account_autologin? false
|
35
|
+
verify_account_notice_flash "The account has been approved"
|
36
|
+
|
37
|
+
# Send a reset password email after verifying the account.
|
38
|
+
# This allows the user to choose the password for the account,
|
39
|
+
# and also makes sure the user can only log in if they have
|
40
|
+
# access to the email address for the account.
|
41
|
+
after_verify_account do
|
42
|
+
generate_reset_password_key_value
|
43
|
+
create_reset_password_key
|
44
|
+
send_reset_password_email
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
= Skip login page if already authenticated
|
2
|
+
|
3
|
+
In some cases it may be useful to skip login/registration pages when the user
|
4
|
+
is already logged in. This can be achieved as follows. Note that this only
|
5
|
+
matters if the user manually navigates to the login or create account pages.
|
6
|
+
|
7
|
+
plugin :rodauth do
|
8
|
+
# Redirect logged in users to the wherever login redirects to
|
9
|
+
already_logged_in { redirect login_redirect }
|
10
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
= Use a non-email login
|
2
|
+
|
3
|
+
Rodauth's by default uses email addresses for identifying users, since that is
|
4
|
+
the most common form of identifier currently. In some cases, you might want
|
5
|
+
to allow logging in via alternative identifiers, such as a username. In this
|
6
|
+
case, it is best to choose a different column name for the login, such as
|
7
|
+
+:username+. Among other things, this also makes it so that the login field
|
8
|
+
does not expect an email address to be provided.
|
9
|
+
|
10
|
+
plugin :rodauth do
|
11
|
+
enable :login, :logout
|
12
|
+
login_column :username
|
13
|
+
end
|
14
|
+
|
15
|
+
Note that Rodauth features that require sending email need an email address, and
|
16
|
+
that defaults to the value of the login column. If you have both a username and
|
17
|
+
an email for an account, you can have the login column be the user, and use the
|
18
|
+
value of the email colummn for the email address.
|
19
|
+
|
20
|
+
plugin :rodauth do
|
21
|
+
enable :login, :logout, :reset_password
|
22
|
+
|
23
|
+
login_column :username
|
24
|
+
email_to do
|
25
|
+
account[:email]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
An alternative approach would be to accept a login and automatically change it
|
30
|
+
to an email address. If you have a +username+ field on the +accounts+ table,
|
31
|
+
then you can configure Rodauth to allow entering a username instead of email
|
32
|
+
during login. See the {Adding new registration field}[rdoc-ref:doc/guides/registration_field.rdoc]
|
33
|
+
guide for instructions on requiring add an additional field during registration.
|
34
|
+
|
35
|
+
plugin :rodauth do
|
36
|
+
enable :login, :logout
|
37
|
+
|
38
|
+
account_from_login do |login|
|
39
|
+
# handle the case when login parameter is a username
|
40
|
+
unless login.include?("@")
|
41
|
+
login = db[:accounts].where(username: login).get(:email)
|
42
|
+
end
|
43
|
+
|
44
|
+
super(login)
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
= Create an account record programmatically
|
2
|
+
|
3
|
+
In some scenarios you might want to create an account records programmatically,
|
4
|
+
for example in your tests.
|
5
|
+
|
6
|
+
If you're storing passwords in a separate table, you can create an account
|
7
|
+
records as follows:
|
8
|
+
|
9
|
+
account_id = DB[:accounts].insert(
|
10
|
+
email: "name@example.com",
|
11
|
+
status_id: 2, # verified
|
12
|
+
)
|
13
|
+
|
14
|
+
DB[:account_password_hashes].insert(
|
15
|
+
id: account_id,
|
16
|
+
password_hash: BCrypt::Password.create("secret").to_s,
|
17
|
+
)
|
18
|
+
|
19
|
+
If the password is stored in a column in the accounts table:
|
20
|
+
|
21
|
+
account_id = DB[:accounts].insert(
|
22
|
+
email: "name@example.com",
|
23
|
+
password_hash: BCrypt::Password.create("secret").to_s,
|
24
|
+
status_id: 2, # verified
|
25
|
+
)
|
26
|
+
|
27
|
+
If you are creating accounts in your tests, you probably want to use
|
28
|
+
the +:cost+ option, otherwise you will have very slow tests:
|
29
|
+
|
30
|
+
account_id = DB[:accounts].insert(
|
31
|
+
email: "name@example.com",
|
32
|
+
status_id: 2, # verified
|
33
|
+
)
|
34
|
+
|
35
|
+
DB[:account_password_hashes].insert(
|
36
|
+
id: account_id,
|
37
|
+
password_hash: BCrypt::Password.create("secret", cost: BCrypt::Engine::MIN_COST).to_s,
|
38
|
+
)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
= Set password when verifying account
|
2
|
+
|
3
|
+
If you want to request less information from the user on registration, you can
|
4
|
+
ask the user to set their password only when they verify their account:
|
5
|
+
|
6
|
+
plugin :rodauth do
|
7
|
+
enable :login, :logout, :verify_account
|
8
|
+
verify_account_set_password? true
|
9
|
+
end
|
10
|
+
|
11
|
+
Note that this is already the default behaviour when verify account feature is
|
12
|
+
loaded, but it's not when verify account grace period is used, because it would
|
13
|
+
prevent the account from logging in during the grace period. You can work around
|
14
|
+
this by automatically remebering their login during account creation using the
|
15
|
+
remember feature. Be aware that remembering accounts has effects beyond the
|
16
|
+
verification period, and this would only allow automatic logins from the browser
|
17
|
+
that created the account.
|
18
|
+
|
19
|
+
plugin :rodauth do
|
20
|
+
enable :login, :logout, :verify_account_grace_period, :remember
|
21
|
+
verify_account_set_password? true
|
22
|
+
after_create_account do
|
23
|
+
remember_login
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
= Allow only email authentication
|
2
|
+
|
3
|
+
When using the email authentication feature, you can avoid other authentication
|
4
|
+
mechanisms entirely as follows:
|
5
|
+
|
6
|
+
plugin :rodauth do
|
7
|
+
enable :login, :email_auth, :create_account, :verify_account
|
8
|
+
|
9
|
+
create_account_set_password? false
|
10
|
+
verify_account_set_password? false
|
11
|
+
force_email_auth? true
|
12
|
+
end
|
13
|
+
|
14
|
+
With this configuration, users won't be required to enter a password on
|
15
|
+
registration, and on login the email authentication link will automatically be
|
16
|
+
sent after the email address is entered.
|
@@ -0,0 +1,26 @@
|
|
1
|
+
= Translate with i18n gem
|
2
|
+
|
3
|
+
Rodauth allows transforming user-facing text configuration such as flash
|
4
|
+
messages, validation errors, labels etc. via the +translate+ configuration
|
5
|
+
method. This method receives a name of a configuration along with its default
|
6
|
+
value, and is expected to return the result text.
|
7
|
+
|
8
|
+
You can use this to perform translations using the
|
9
|
+
{i18n gem}[https://github.com/ruby-i18n/i18n]:
|
10
|
+
|
11
|
+
plugin :rodauth do
|
12
|
+
enable :login, :logout, :reset_password
|
13
|
+
|
14
|
+
translate do |key, default|
|
15
|
+
I18n.translate("rodauth.#{key}") || default
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Your translation file may then look something like this:
|
20
|
+
|
21
|
+
en:
|
22
|
+
rodauth:
|
23
|
+
login_notice_flash: "You have been signed in"
|
24
|
+
require_login_error_flash: "Login is required for accessing this page"
|
25
|
+
no_matching_login_message: "user with this email address doesn't exist"
|
26
|
+
reset_password_email_subject: "Password Reset Instructions"
|
File without changes
|
@@ -0,0 +1,12 @@
|
|
1
|
+
= Display authentication links
|
2
|
+
|
3
|
+
You can retrieve a relative URL to any Rodauth action by calling the
|
4
|
+
corresponding <tt>*_path</tt> method on the Rodauth instance:
|
5
|
+
|
6
|
+
<a href="<%= rodauth.login_path %>">Sign in</a>
|
7
|
+
<a href="<%= rodauth.create_account_path %>">Sign up</a>
|
8
|
+
|
9
|
+
For absolute URLs instead of paths, you can use the <tt>*_url</tt> methods:
|
10
|
+
|
11
|
+
<a href="<%= rodauth.login_url %>">Sign in</a>
|
12
|
+
<a href="<%= rodauth.create_account_url %>">Sign up</a>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
= Redirect to original page after login
|
2
|
+
|
3
|
+
When the user attempts to open a page that requires authentication, Rodauth
|
4
|
+
redirects them to the login page. It can be useful to redirect them back to
|
5
|
+
the page they originally requested after successful login. Similarly, you
|
6
|
+
can do this for pages requiring multifactor authentication.
|
7
|
+
|
8
|
+
plugin :rodauth do
|
9
|
+
enable :login, :logout, :otp
|
10
|
+
|
11
|
+
# Have successful login redirect back to originally requested page
|
12
|
+
login_return_to_requested_location? true
|
13
|
+
|
14
|
+
# Have successful multifactor authentication redirect back to
|
15
|
+
# originally requested page
|
16
|
+
two_factor_auth_return_to_requested_location? true
|
17
|
+
end
|
18
|
+
|
19
|
+
You can manually set which page to redirect after login or multifactor
|
20
|
+
authentication, though it is questionable whether the user will desire
|
21
|
+
this behavior compared to the default.
|
22
|
+
|
23
|
+
route do |r|
|
24
|
+
r.rodauth
|
25
|
+
|
26
|
+
# Return the last visited path after login
|
27
|
+
if rodauth.logged_in?
|
28
|
+
# Return to the last visited page after multifactor authentication
|
29
|
+
unless rodauth.two_factor_authenticated?
|
30
|
+
session[rodauth.two_factor_auth_redirect_session_key] = request.fullpath
|
31
|
+
end
|
32
|
+
else
|
33
|
+
session[rodauth.login_redirect_session_key] = request.fullpath
|
34
|
+
end
|
35
|
+
|
36
|
+
# rest of routes
|
37
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
= Store password hash in accounts table
|
2
|
+
|
3
|
+
By default, Rodauth stores the password hash in a separate
|
4
|
+
+account_password_hashes+ table. This makes it a lot less likely that the
|
5
|
+
password hashes will be leaked, especially if you use Rodauth's default
|
6
|
+
approach of using database functions for checking the hashes.
|
7
|
+
|
8
|
+
However, if you have reasons for storing the password hashes in +accounts+
|
9
|
+
table that outweigh the security benefits of Rodauth's default approach,
|
10
|
+
Rodauth supports that.
|
11
|
+
|
12
|
+
To do this, add the password hash column to the +accounts+ table:
|
13
|
+
|
14
|
+
alter_table :accounts do
|
15
|
+
add_column :password_hash, String
|
16
|
+
end
|
17
|
+
|
18
|
+
And then tell Rodauth to use it:
|
19
|
+
|
20
|
+
plugin :rodauth do
|
21
|
+
enable :login, :logout
|
22
|
+
|
23
|
+
# Use the password_hash column in the accounts table
|
24
|
+
account_password_hash_column :password_hash
|
25
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
= Require password confirmation for certain actions
|
2
|
+
|
3
|
+
You might want to require the user to enter their password before accessing
|
4
|
+
sensitive sections of the app. This functionality is provided by the confirm
|
5
|
+
password feature, which accompanied with the password grace period feature will
|
6
|
+
remember the entered password for a period of time:
|
7
|
+
|
8
|
+
plugin :rodauth do
|
9
|
+
enable :confirm_password, :password_grace_period
|
10
|
+
|
11
|
+
# Remember the password for 1 hour
|
12
|
+
password_grace_period 60*60
|
13
|
+
end
|
14
|
+
|
15
|
+
route do |r|
|
16
|
+
r.rodauth
|
17
|
+
|
18
|
+
r.is 'some-action' do
|
19
|
+
# Require password authentication if the password has not been
|
20
|
+
# input recently.
|
21
|
+
rodauth.require_password_authentication
|
22
|
+
|
23
|
+
# ...
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
You can also do this for Rodauth actions that normally require a password.
|
28
|
+
Which essentially moves the password confirmation into a separate step, as
|
29
|
+
Rodauth's behavior with the password grace period feature is to ask for the
|
30
|
+
password on the same form.
|
31
|
+
|
32
|
+
plugin :rodauth do
|
33
|
+
enable :confirm_password, :password_grace_period, :change_login, :change_password
|
34
|
+
|
35
|
+
before_change_login_route { require_password_authentication }
|
36
|
+
before_change_password_route { require_password_authentication }
|
37
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
= Customize password requirements
|
2
|
+
|
3
|
+
By default, Rodauth requires passwords to have at least 6 characters. You can
|
4
|
+
modify the minimum length:
|
5
|
+
|
6
|
+
plugin :rodauth do
|
7
|
+
enable :login, :logout, :create_account
|
8
|
+
|
9
|
+
# Require passwords to have at least 8 characters
|
10
|
+
password_minimum_length 8
|
11
|
+
end
|
12
|
+
|
13
|
+
You can use the {disallow common passwords feature}[rdoc-ref:doc/disallow_common_passwords.rdoc]
|
14
|
+
to prevent the usage of common passwords (the most common 10,000 by default).
|
15
|
+
|
16
|
+
You can use additional complexity checks on passwords via the {password
|
17
|
+
complexity feature}[rdoc-ref:doc/password_complexity.rdoc], though most of
|
18
|
+
those complexity checks are no longer considered modern security best
|
19
|
+
practices and are likely to decrease overall security.
|
20
|
+
|
21
|
+
If you want complete control over whether passwords meet requirements, you
|
22
|
+
can use the <tt>password_meets_requirements?</tt> configuration method.
|
23
|
+
|
24
|
+
plugin :rodauth do
|
25
|
+
enable :login, :logout, :create_account
|
26
|
+
|
27
|
+
password_meets_requirements? do |password|
|
28
|
+
#true if password meets requirements, false otherwise
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
= Change route path
|
2
|
+
|
3
|
+
You can change the URL path of any Rodauth route by overriding the
|
4
|
+
corresponding <tt>*_route</tt> method:
|
5
|
+
|
6
|
+
plugin :rodauth do
|
7
|
+
enable :login, :logout, :create_account, :reset_password
|
8
|
+
|
9
|
+
# Change login route to "/signin"
|
10
|
+
login_route "signin"
|
11
|
+
|
12
|
+
# Change create account route to "/register"
|
13
|
+
create_account_route "register"
|
14
|
+
|
15
|
+
# Change password reset request route to "/reset-password/request"
|
16
|
+
reset_password_request_route "reset-password/request"
|
17
|
+
end
|
18
|
+
|
19
|
+
If you want to add a prefix to all Rodauth routes, you should use the +prefix+
|
20
|
+
setting:
|
21
|
+
|
22
|
+
plugin :rodauth do
|
23
|
+
enable :login, :logout
|
24
|
+
|
25
|
+
# Use /auth prefix to each Rodauth route
|
26
|
+
prefix "/auth"
|
27
|
+
end
|
28
|
+
|
29
|
+
route do |r|
|
30
|
+
r.on "auth" do
|
31
|
+
# Serve Rodauth routes under the /auth branch of the routing tree
|
32
|
+
r.rodauth
|
33
|
+
end
|
34
|
+
|
35
|
+
# ...
|
36
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
= Pass query parameters to auth URLs
|
2
|
+
|
3
|
+
The <tt>*_path</tt> and <tt>*_url</tt> methods allow passing additional query parameters:
|
4
|
+
|
5
|
+
rodauth.create_account_path(type: "seller")
|
6
|
+
#=> "/create-account?type=seller"
|
7
|
+
|
8
|
+
rodauth.login_url(type: "operator")
|
9
|
+
#=> "https//example.com/login?type=operator"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
= Change redirect destination
|
2
|
+
|
3
|
+
You can change the redirect destination for any Rodauth action by overriding
|
4
|
+
the corresponding <tt>*_redirect</tt> method:
|
5
|
+
|
6
|
+
plugin :rodauth do
|
7
|
+
enable :login, :logout, :create_account, :reset_password
|
8
|
+
|
9
|
+
# Redirect to "/dashboard" after login
|
10
|
+
login_redirect "/dashboard"
|
11
|
+
|
12
|
+
# Redirect to wherever login redirects to after creating account
|
13
|
+
create_account_redirect { login_redirect }
|
14
|
+
|
15
|
+
# Redirect to login page after password reset
|
16
|
+
reset_password_redirect { login_path }
|
17
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
= Add new field during account creation
|
2
|
+
|
3
|
+
The create account form only handles login and password parameters by
|
4
|
+
default. However, you might want to ask for additional information during
|
5
|
+
account creation, such as requiring the user to also enter their full name
|
6
|
+
or their company's name.
|
7
|
+
|
8
|
+
== A) Accounts table
|
9
|
+
|
10
|
+
Let's assume you wanted to wanted to store the additional field(s) directly on
|
11
|
+
the +accounts+ table:
|
12
|
+
|
13
|
+
atler_table :accounts do
|
14
|
+
add_column :name, String
|
15
|
+
end
|
16
|
+
|
17
|
+
You need to override the <tt>create-account</tt> template, which by default in
|
18
|
+
Rodauth you can do by adding a <tt>create-account.erb</tt> template in your
|
19
|
+
Roda +views+ directory.
|
20
|
+
|
21
|
+
Once you've added the <tt>create-account.erb</tt> template, and had it include
|
22
|
+
a field for the +name+, you can handle the submission of that field in a before
|
23
|
+
create account hook:
|
24
|
+
|
25
|
+
plugin :rodauth do
|
26
|
+
enable :login, :logout, :create_account
|
27
|
+
|
28
|
+
before_create_account do
|
29
|
+
# Validate presence of the name field
|
30
|
+
unless name = param_or_nil("name")
|
31
|
+
throw_error_status(422, "name", "must be present")
|
32
|
+
end
|
33
|
+
|
34
|
+
# Assign the new field to the account record
|
35
|
+
account[:name] = name
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
== B) Separate table
|
40
|
+
|
41
|
+
Alternatively, you can store the additional field(s) in separate table, for
|
42
|
+
example:
|
43
|
+
|
44
|
+
create_table :account_names do
|
45
|
+
foreign_key :account_id, :accounts, primary_key: true, type: :Bignum
|
46
|
+
String :name, null: false
|
47
|
+
end
|
48
|
+
|
49
|
+
You can then handle the new submitted field as follows:
|
50
|
+
|
51
|
+
plugin :rodauth do
|
52
|
+
enable :login, :logout, :create_account
|
53
|
+
|
54
|
+
before_create_account do
|
55
|
+
# Validate presence of the name field
|
56
|
+
throw_error_status(422, "name", "must be present") unless param_or_nil("name")
|
57
|
+
end
|
58
|
+
|
59
|
+
after_create_account do
|
60
|
+
# Create the associated record
|
61
|
+
db[:account_names].insert(account_id: account[:id], name: param("name"))
|
62
|
+
end
|
63
|
+
|
64
|
+
after_close_account do
|
65
|
+
# Delete the associated record
|
66
|
+
db[:account_names].where(account_id: account[:id]).delete
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
= Require multifactor authentication after login
|
2
|
+
|
3
|
+
You may want to require multifactor authentication on login for people
|
4
|
+
that have multifactor authentication set up. The +require_authentication+
|
5
|
+
Rodauth method works for pages that require an authenticated user, but not for
|
6
|
+
pages where authentication is optional.
|
7
|
+
|
8
|
+
You can set this up as follows:
|
9
|
+
|
10
|
+
plugin :rodauth do
|
11
|
+
enable :login, :logout, :otp
|
12
|
+
|
13
|
+
# If you don't want to show an error message when redirecting
|
14
|
+
# to the multifactor authentication page.
|
15
|
+
two_factor_need_authentication_error_flash nil
|
16
|
+
|
17
|
+
# Display the same flash message after multifactor
|
18
|
+
# authentication than is displayed after login
|
19
|
+
two_factor_auth_notice_flash { login_notice_flash }
|
20
|
+
end
|
21
|
+
|
22
|
+
route do |r|
|
23
|
+
r.rodauth
|
24
|
+
|
25
|
+
if rodauth.logged_in? && rodauth.two_factor_authentication_setup?
|
26
|
+
rodauth.require_two_factor_authenticated
|
27
|
+
end
|
28
|
+
|
29
|
+
# ...
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
= Autologin after password reset
|
2
|
+
|
3
|
+
When the user resets their password, by default they are not automatically
|
4
|
+
logged in. You can change this behaviour and login the user automatically
|
5
|
+
after password reset.
|
6
|
+
|
7
|
+
plugin :rodauth do
|
8
|
+
enable :login, :logout, :reset_password
|
9
|
+
|
10
|
+
reset_password_autologin? true
|
11
|
+
end
|
12
|
+
|
13
|
+
Similarly, when the verify login change feature is used, the user is not
|
14
|
+
automatically logged in after verifying the login change. You can configure
|
15
|
+
Rodauth to automatically log the user in in this case:
|
16
|
+
|
17
|
+
plugin :rodauth do
|
18
|
+
enable :login, :logout, :verify_login_change
|
19
|
+
|
20
|
+
verify_login_change_autologin? true
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
= Store account status in a text column
|
2
|
+
|
3
|
+
By default, Rodauth recommends using a separate table for account statuses, and
|
4
|
+
linking them via foreign keys. This is useful as it achieves an enum-like
|
5
|
+
behaviour, where the database ensures a constrained set of status values.
|
6
|
+
|
7
|
+
However, if you use a testing environment that starts with a blank database,
|
8
|
+
and don't want to fix your testing environment to support real foreign keys,
|
9
|
+
you can configure Rodauth to store the account status in a text column.
|
10
|
+
Doing so results in problems if a text value you do not expect gets stored
|
11
|
+
in the column. We can mitigate the problems by using a CHECK constraint
|
12
|
+
on the column.
|
13
|
+
|
14
|
+
create_table :accounts do
|
15
|
+
# ...
|
16
|
+
String :status, null: false, default: "verified",
|
17
|
+
check: {status: %w'unverified verified closed'}
|
18
|
+
end
|
19
|
+
|
20
|
+
Then we can configure Rodauth to support this.
|
21
|
+
|
22
|
+
plugin :rodauth do
|
23
|
+
# ...
|
24
|
+
account_status_column :status
|
25
|
+
account_unverified_status_value "unverified"
|
26
|
+
account_open_status_value "verified"
|
27
|
+
account_closed_status_value "closed"
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
= Allow recovery code on TOTP code field
|
2
|
+
|
3
|
+
If using the otp feature, for convenience you might want to allow
|
4
|
+
the user to enter the recovery code into the TOTP code field, instead
|
5
|
+
of requiring they use the separate recovery codes form. You can
|
6
|
+
implement this using the following configuration:
|
7
|
+
|
8
|
+
plugin :rodauth do
|
9
|
+
enable :login, :logout, :otp, :recovery_codes
|
10
|
+
|
11
|
+
before_otp_auth_route do
|
12
|
+
if recovery_code_match?(param(otp_auth_param))
|
13
|
+
two_factor_authenticate("recovery_code")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/doc/jwt_refresh.rdoc
CHANGED
@@ -13,6 +13,12 @@ older access tokens. Older access tokens remain valid until they expire. You
|
|
13
13
|
can use the active_sessions feature if you want previous access tokens to be invalid
|
14
14
|
as soon as the refresh token is used.
|
15
15
|
|
16
|
+
You can have multiple active refresh tokens active at a time, since each browser session
|
17
|
+
will generally use a separate refresh token. If you would like to revoke a refresh token
|
18
|
+
when logging out, provide the refresh token when submitting the JSON request to logout.
|
19
|
+
If you would like to remove all refresh tokens for the account when logging out, provide
|
20
|
+
a value of <tt>all</tt> as the token value.
|
21
|
+
|
16
22
|
This feature depends on the jwt feature.
|
17
23
|
|
18
24
|
== Auth Value Methods
|
data/doc/otp.rdoc
CHANGED
@@ -73,6 +73,7 @@ otp_auth_view :: The HTML to use for the OTP authentication form.
|
|
73
73
|
otp_disable_view :: The HTML to use for the OTP disable form.
|
74
74
|
otp_exists? :: Whether the current account has setup OTP.
|
75
75
|
otp_key :: The stored OTP secret for the account.
|
76
|
+
otp_last_use :: The last time OTP authentication was successful for the account.
|
76
77
|
otp_locked_out? :: Whether the current account has been locked out of OTP authentication.
|
77
78
|
otp_new_secret :: A new secret to use when setting up OTP.
|
78
79
|
otp_provisioning_name :: The provisioning name to use during OTP setup, defaults to the account's email.
|
@@ -0,0 +1,39 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* When using the jwt_refresh feature, you can remove the current
|
4
|
+
refresh token when logging out by submitting the refresh token
|
5
|
+
in the logout request, the same as when submitting the refresh
|
6
|
+
token to obtain a new refresh token. You can also use a value
|
7
|
+
of "all" instead of the refresh token to remove all refresh
|
8
|
+
tokens when logging out.
|
9
|
+
|
10
|
+
* A rodauth.otp_last_use method has been added to the otp feature,
|
11
|
+
allowing you to determine when the otp was last used.
|
12
|
+
|
13
|
+
= Other Improvements
|
14
|
+
|
15
|
+
* When using multifactor authentication, rodauth.authenticated? and
|
16
|
+
rodauth.require_authentication now cache values in the session and
|
17
|
+
do not perform queries every time they are called.
|
18
|
+
|
19
|
+
* Many guides for common scenarios have been added to the
|
20
|
+
documentation. These augment Rodauth's existing comprehensive
|
21
|
+
feature documentation, which is aimed to be more of a reference
|
22
|
+
and less of a guide.
|
23
|
+
|
24
|
+
* When the verify_account_grace_period and email_auth features are
|
25
|
+
used with a multifactor authentication feature, and the
|
26
|
+
verify_account_set_password? configuration method is set to true,
|
27
|
+
Rodauth no longer raises a NoMethodError when checking if the
|
28
|
+
session was authenticated.
|
29
|
+
|
30
|
+
* In the verify_account feature, if verify_account_email_resend
|
31
|
+
returns false indicating no email was sent, an error message
|
32
|
+
is now used, instead of a success message.
|
33
|
+
|
34
|
+
* In the password_complexity feature, the password_dictionary
|
35
|
+
configuration method was previously ignored if the default
|
36
|
+
password dictionary file existed.
|
37
|
+
|
38
|
+
* Rodauth and all features that ship with it now have 100% branch
|
39
|
+
coverage.
|
data/lib/rodauth.rb
CHANGED
@@ -137,7 +137,9 @@ module Rodauth
|
|
137
137
|
feature.module_eval(&block)
|
138
138
|
configuration.def_configuration_methods(feature)
|
139
139
|
|
140
|
+
# :nocov:
|
140
141
|
if constant
|
142
|
+
# :nocov:
|
141
143
|
Rodauth.const_set(constant, feature)
|
142
144
|
Rodauth::FeatureConfiguration.const_set(constant, configuration)
|
143
145
|
end
|
@@ -336,10 +338,8 @@ module Rodauth
|
|
336
338
|
end
|
337
339
|
|
338
340
|
def freeze
|
339
|
-
|
340
|
-
|
341
|
-
opts[:rodauths].freeze
|
342
|
-
end
|
341
|
+
opts[:rodauths].each_value(&:freeze)
|
342
|
+
opts[:rodauths].freeze
|
343
343
|
super
|
344
344
|
end
|
345
345
|
end
|
@@ -118,14 +118,12 @@ module Rodauth
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def before_logout
|
121
|
-
if
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
remove_current_session
|
126
|
-
end
|
121
|
+
if param_or_nil(global_logout_param)
|
122
|
+
remove_all_active_sessions
|
123
|
+
else
|
124
|
+
remove_current_session
|
127
125
|
end
|
128
|
-
super
|
126
|
+
super
|
129
127
|
end
|
130
128
|
|
131
129
|
def session_inactivity_deadline_condition
|
@@ -82,7 +82,9 @@ module Rodauth
|
|
82
82
|
|
83
83
|
def audit_log_ds
|
84
84
|
ds = db[audit_logging_table]
|
85
|
+
# :nocov:
|
85
86
|
if db.database_type == :postgres
|
87
|
+
# :nocov:
|
86
88
|
# For PostgreSQL, use RETURNING NULL. This allows the feature
|
87
89
|
# to be used with INSERT but not SELECT permissions on the
|
88
90
|
# table, useful for audit logging where the database user
|
data/lib/rodauth/features/jwt.rb
CHANGED
@@ -138,6 +138,11 @@ module Rodauth
|
|
138
138
|
!!(jwt_token && jwt_payload)
|
139
139
|
end
|
140
140
|
|
141
|
+
def view(page, title)
|
142
|
+
return super unless use_jwt?
|
143
|
+
return_json_response
|
144
|
+
end
|
145
|
+
|
141
146
|
private
|
142
147
|
|
143
148
|
def check_csrf?
|
@@ -256,12 +261,6 @@ module Rodauth
|
|
256
261
|
@json_response ||= {}
|
257
262
|
end
|
258
263
|
|
259
|
-
def _view(meth, page)
|
260
|
-
return super unless use_jwt?
|
261
|
-
return super if meth == :render
|
262
|
-
return_json_response
|
263
|
-
end
|
264
|
-
|
265
264
|
def _json_response_body(hash)
|
266
265
|
request.send(:convert_to_json, hash)
|
267
266
|
end
|
@@ -13,27 +13,27 @@ module Rodauth
|
|
13
13
|
auth_methods(:jwt_cors_allow?)
|
14
14
|
|
15
15
|
def jwt_cors_allow?
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
16
|
+
return false unless origin = request.env['HTTP_ORIGIN']
|
17
|
+
|
18
|
+
case allowed = jwt_cors_allow_origin
|
19
|
+
when String
|
20
|
+
timing_safe_eql?(origin, allowed)
|
21
|
+
when Array
|
22
|
+
allowed.any?{|s| timing_safe_eql?(origin, s)}
|
23
|
+
when Regexp
|
24
|
+
allowed =~ origin
|
25
|
+
when true
|
26
|
+
true
|
27
|
+
else
|
28
|
+
false
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
private
|
33
33
|
|
34
34
|
def before_rodauth
|
35
|
-
if
|
36
|
-
response['Access-Control-Allow-Origin'] =
|
35
|
+
if jwt_cors_allow?
|
36
|
+
response['Access-Control-Allow-Origin'] = request.env['HTTP_ORIGIN']
|
37
37
|
|
38
38
|
# Handle CORS preflight request
|
39
39
|
if request.request_method == 'OPTIONS'
|
@@ -80,14 +80,10 @@ module Rodauth
|
|
80
80
|
private
|
81
81
|
|
82
82
|
def _account_from_refresh_token(token)
|
83
|
-
id,
|
84
|
-
return unless id && token
|
85
|
-
|
86
|
-
token_id, key = split_token(token)
|
87
|
-
return unless token_id && key
|
83
|
+
id, token_id, key = _account_refresh_token_split(token)
|
88
84
|
|
85
|
+
return unless key
|
89
86
|
return unless actual = get_active_refresh_token(id, token_id)
|
90
|
-
|
91
87
|
return unless timing_safe_eql?(key, convert_token_key(actual))
|
92
88
|
|
93
89
|
ds = account_ds(id)
|
@@ -95,6 +91,16 @@ module Rodauth
|
|
95
91
|
ds.first
|
96
92
|
end
|
97
93
|
|
94
|
+
def _account_refresh_token_split(token)
|
95
|
+
id, token = split_token(token)
|
96
|
+
return unless id && token
|
97
|
+
|
98
|
+
token_id, key = split_token(token)
|
99
|
+
return unless token_id && key
|
100
|
+
|
101
|
+
[id, token_id, key]
|
102
|
+
end
|
103
|
+
|
98
104
|
def get_active_refresh_token(account_id, token_id)
|
99
105
|
jwt_refresh_token_account_ds(account_id).
|
100
106
|
where(Sequel::CURRENT_TIMESTAMP > jwt_refresh_token_deadline_column).
|
@@ -134,6 +140,23 @@ module Rodauth
|
|
134
140
|
hash
|
135
141
|
end
|
136
142
|
|
143
|
+
def before_logout
|
144
|
+
if token = param_or_nil(jwt_refresh_token_key_param)
|
145
|
+
if token == 'all'
|
146
|
+
jwt_refresh_token_account_ds(session_value).delete
|
147
|
+
else
|
148
|
+
id, token_id, key = _account_refresh_token_split(token)
|
149
|
+
|
150
|
+
if id && token_id && key && (actual = get_active_refresh_token(session_value, token_id)) && timing_safe_eql?(key, convert_token_key(actual))
|
151
|
+
jwt_refresh_token_account_ds(id).
|
152
|
+
where(jwt_refresh_token_id_column=>token_id).
|
153
|
+
delete
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
super if defined?(super)
|
158
|
+
end
|
159
|
+
|
137
160
|
def after_close_account
|
138
161
|
jwt_refresh_token_account_ds(account_id).delete
|
139
162
|
super if defined?(super)
|
data/lib/rodauth/features/otp.rb
CHANGED
@@ -79,6 +79,7 @@ module Rodauth
|
|
79
79
|
:otp,
|
80
80
|
:otp_exists?,
|
81
81
|
:otp_key,
|
82
|
+
:otp_last_use,
|
82
83
|
:otp_locked_out?,
|
83
84
|
:otp_new_secret,
|
84
85
|
:otp_provisioning_name,
|
@@ -255,7 +256,6 @@ module Rodauth
|
|
255
256
|
def otp_remove
|
256
257
|
otp_key_ds.delete
|
257
258
|
@otp_key = nil
|
258
|
-
super if defined?(super)
|
259
259
|
end
|
260
260
|
|
261
261
|
def otp_add_key
|
@@ -269,6 +269,10 @@ module Rodauth
|
|
269
269
|
update(otp_keys_last_use_column=>Sequel::CURRENT_TIMESTAMP) == 1
|
270
270
|
end
|
271
271
|
|
272
|
+
def otp_last_use
|
273
|
+
convert_timestamp(otp_key_ds.get(otp_keys_last_use_column))
|
274
|
+
end
|
275
|
+
|
272
276
|
def otp_record_authentication_failure
|
273
277
|
otp_key_ds.update(otp_keys_failures_column=>Sequel.identifier(otp_keys_failures_column) + 1)
|
274
278
|
end
|
@@ -26,14 +26,16 @@ module Rodauth
|
|
26
26
|
|
27
27
|
def post_configure
|
28
28
|
super
|
29
|
-
return if
|
29
|
+
return if method(:password_dictionary).owner != Rodauth::PasswordComplexity
|
30
30
|
|
31
31
|
case password_dictionary_file
|
32
32
|
when false
|
33
|
-
|
33
|
+
# nothing
|
34
34
|
when nil
|
35
35
|
default_dictionary_file = '/usr/share/dict/words'
|
36
|
+
# :nocov:
|
36
37
|
if File.file?(default_dictionary_file)
|
38
|
+
# :nocov:
|
37
39
|
words = File.read(default_dictionary_file)
|
38
40
|
end
|
39
41
|
else
|
@@ -125,7 +125,7 @@ module Rodauth
|
|
125
125
|
return true if two_factor_authenticated?
|
126
126
|
|
127
127
|
# True if authenticated via single factor and 2nd factor not setup
|
128
|
-
!
|
128
|
+
!uses_two_factor_authentication?
|
129
129
|
end
|
130
130
|
|
131
131
|
def require_authentication
|
@@ -134,7 +134,7 @@ module Rodauth
|
|
134
134
|
# Avoid database query if already authenticated via 2nd factor
|
135
135
|
return if two_factor_authenticated?
|
136
136
|
|
137
|
-
require_two_factor_authenticated if
|
137
|
+
require_two_factor_authenticated if uses_two_factor_authentication?
|
138
138
|
end
|
139
139
|
|
140
140
|
def require_two_factor_setup
|
@@ -208,11 +208,11 @@ module Rodauth
|
|
208
208
|
end
|
209
209
|
|
210
210
|
def _two_factor_setup_links
|
211
|
-
|
211
|
+
[]
|
212
212
|
end
|
213
213
|
|
214
214
|
def _two_factor_remove_links
|
215
|
-
|
215
|
+
[]
|
216
216
|
end
|
217
217
|
|
218
218
|
def _two_factor_remove_all_from_session
|
@@ -70,6 +70,7 @@ module Rodauth
|
|
70
70
|
end
|
71
71
|
|
72
72
|
r.post do
|
73
|
+
verified = false
|
73
74
|
if account_from_login(param(login_param)) && allow_resending_verify_account_email?
|
74
75
|
if verify_account_email_recently_sent?
|
75
76
|
set_redirect_error_flash verify_account_email_recently_sent_error_flash
|
@@ -79,8 +80,11 @@ module Rodauth
|
|
79
80
|
before_verify_account_email_resend
|
80
81
|
if verify_account_email_resend
|
81
82
|
after_verify_account_email_resend
|
83
|
+
verified = true
|
82
84
|
end
|
85
|
+
end
|
83
86
|
|
87
|
+
if verified
|
84
88
|
set_notice_flash verify_account_email_sent_notice_flash
|
85
89
|
else
|
86
90
|
set_redirect_error_status(no_matching_login_error_status)
|
@@ -53,10 +53,7 @@ module Rodauth
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def allow_email_auth?
|
56
|
-
|
57
|
-
return false unless super
|
58
|
-
end
|
59
|
-
!account_in_unverified_grace_period?
|
56
|
+
(defined?(super) ? super : true) && !account_in_unverified_grace_period?
|
60
57
|
end
|
61
58
|
|
62
59
|
def verify_account_check_already_logged_in
|
@@ -75,6 +72,7 @@ module Rodauth
|
|
75
72
|
end
|
76
73
|
|
77
74
|
def account_in_unverified_grace_period?
|
75
|
+
account || account_from_session
|
78
76
|
account[account_status_column] == account_unverified_status_value &&
|
79
77
|
verify_account_grace_period &&
|
80
78
|
!verify_account_ds.where(Sequel.date_add(verification_requested_at_column, :seconds=>verify_account_grace_period) > Sequel::CURRENT_TIMESTAMP).empty?
|
@@ -377,9 +377,7 @@ module Rodauth
|
|
377
377
|
end
|
378
378
|
|
379
379
|
def remove_webauthn_key(webauthn_id)
|
380
|
-
|
381
|
-
super if defined?(super)
|
382
|
-
ret
|
380
|
+
webauthn_keys_ds.where(webauthn_keys_webauthn_id_column=>webauthn_id).delete == 1
|
383
381
|
end
|
384
382
|
|
385
383
|
def remove_all_webauthn_keys_and_user_ids
|
data/lib/rodauth/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rodauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-07-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -247,7 +247,6 @@ extra_rdoc_files:
|
|
247
247
|
- doc/http_basic_auth.rdoc
|
248
248
|
- doc/create_account.rdoc
|
249
249
|
- doc/email_base.rdoc
|
250
|
-
- doc/internals.rdoc
|
251
250
|
- doc/disallow_common_passwords.rdoc
|
252
251
|
- doc/disallow_password_reuse.rdoc
|
253
252
|
- doc/password_complexity.rdoc
|
@@ -304,6 +303,7 @@ extra_rdoc_files:
|
|
304
303
|
- doc/release_notes/1.23.0.txt
|
305
304
|
- doc/release_notes/2.0.0.txt
|
306
305
|
- doc/release_notes/2.1.0.txt
|
306
|
+
- doc/release_notes/2.2.0.txt
|
307
307
|
files:
|
308
308
|
- CHANGELOG
|
309
309
|
- MIT-LICENSE
|
@@ -323,8 +323,28 @@ files:
|
|
323
323
|
- doc/disallow_password_reuse.rdoc
|
324
324
|
- doc/email_auth.rdoc
|
325
325
|
- doc/email_base.rdoc
|
326
|
+
- doc/guides/admin_activation.rdoc
|
327
|
+
- doc/guides/already_authenticated.rdoc
|
328
|
+
- doc/guides/alternative_login.rdoc
|
329
|
+
- doc/guides/create_account_programmatically.rdoc
|
330
|
+
- doc/guides/delay_password.rdoc
|
331
|
+
- doc/guides/email_only.rdoc
|
332
|
+
- doc/guides/i18n.rdoc
|
333
|
+
- doc/guides/internals.rdoc
|
334
|
+
- doc/guides/links.rdoc
|
335
|
+
- doc/guides/login_return.rdoc
|
336
|
+
- doc/guides/password_column.rdoc
|
337
|
+
- doc/guides/password_confirmation.rdoc
|
338
|
+
- doc/guides/password_requirements.rdoc
|
339
|
+
- doc/guides/paths.rdoc
|
340
|
+
- doc/guides/query_params.rdoc
|
341
|
+
- doc/guides/redirects.rdoc
|
342
|
+
- doc/guides/registration_field.rdoc
|
343
|
+
- doc/guides/require_mfa.rdoc
|
344
|
+
- doc/guides/reset_password_autologin.rdoc
|
345
|
+
- doc/guides/status_column.rdoc
|
346
|
+
- doc/guides/totp_or_recovery.rdoc
|
326
347
|
- doc/http_basic_auth.rdoc
|
327
|
-
- doc/internals.rdoc
|
328
348
|
- doc/jwt.rdoc
|
329
349
|
- doc/jwt_cors.rdoc
|
330
350
|
- doc/jwt_refresh.rdoc
|
@@ -363,6 +383,7 @@ files:
|
|
363
383
|
- doc/release_notes/1.9.0.txt
|
364
384
|
- doc/release_notes/2.0.0.txt
|
365
385
|
- doc/release_notes/2.1.0.txt
|
386
|
+
- doc/release_notes/2.2.0.txt
|
366
387
|
- doc/remember.rdoc
|
367
388
|
- doc/reset_password.rdoc
|
368
389
|
- doc/session_expiration.rdoc
|