:nocov:
Methods
Public Class
- configure
- create_database_authentication_functions
- create_database_previous_password_check_functions
- drop_database_authentication_functions
- drop_database_previous_password_check_functions
- lib
- load_dependencies
- new
- version
Public Instance
- _account_from_email_auth_key
- _account_from_login
- _account_from_refresh_token
- _account_from_reset_password_key
- _account_from_session
- _account_from_unlock_key
- _account_from_verify_account_key
- _account_from_verify_login_change_key
- _account_refresh_token_split
- _around_rodauth
- _email_auth_request
- _email_auth_request_and_redirect
- _field_attributes
- _field_error_attributes
- _filter_links
- _formatted_field_error
- _get_remember_cookie
- _json_response_body
- _jwt_decode_opts
- _login
- _login_form_footer
- _login_form_footer_links
- _multi_phase_login_forms
- _new_account
- _otp
- _otp_add_key
- _otp_key
- _otp_tmp_key
- _recovery_codes
- _return_json_response
- _setup_account_lockouts_hash
- _sms
- _template_opts
- _two_factor_auth_links
- _two_factor_remove_all_from_session
- _two_factor_remove_links
- _two_factor_setup_links
- _update_login
- _view
- _view_opts
- account!
- account_activity_ds
- account_ds
- account_expired?
- account_expired_at
- account_from_email_auth_key
- account_from_key
- account_from_login
- account_from_refresh_token
- account_from_remember_cookie
- account_from_reset_password_key
- account_from_session
- account_from_unlock_key
- account_from_verify_account_key
- account_from_verify_login_change_key
- account_id
- account_in_unverified_grace_period?
- account_initial_status_value
- account_lockouts_ds
- account_login_failures_ds
- account_password_hash_column
- account_session_status_filter
- account_table_ds
- account_webauthn_ids
- account_webauthn_usage
- account_webauthn_user_id
- active_remember_key_ds
- active_sessions_ds
- active_sessions_insert_hash
- active_sessions_update_hash
- add_active_session
- add_audit_log
- add_field_error_class
- add_previous_password_hash
- add_recovery_code
- add_recovery_codes
- add_remember_key
- add_webauthn_credential
- after_change_password
- after_close_account
- after_create_account
- after_login
- after_login_entered_during_multi_phase_login
- after_login_failure
- after_logout
- after_otp_disable
- after_refresh_token
- after_reset_password
- after_sms_disable
- after_webauthn_remove
- allow_email_auth?
- allow_resending_verify_account_email?
- already_logged_in
- argon2_hash_algorithm?
- argon2_hash_cost
- argon2_password_hash_match?
- argon2_salt_option
- audit_log_ds
- audit_log_insert_hash
- audit_log_message
- audit_log_message_default
- audit_log_metadata
- auth_class_eval
- authenticated?
- authenticated_by
- authenticated_webauthn_id
- auto_add_missing_recovery_codes
- auto_remove_recovery_codes
- autocomplete_for_field?
- autologin_session
- autologin_type
- base32_encode
- base_url
- before_change_login_route
- before_change_password_route
- before_create_account_route
- before_login_attempt
- before_logout
- before_otp_setup_route
- before_reset_password
- before_reset_password_request
- before_rodauth
- before_two_factor_auth_route
- before_two_factor_manage_route
- before_unlock_account
- before_unlock_account_request
- before_verify_account
- before_verify_account_route
- before_view_recovery_codes
- before_webauthn_auth_route
- before_webauthn_login_route
- before_webauthn_remove_route
- before_webauthn_setup_route
- button
- button_opts
- can_add_recovery_codes?
- catch_error
- change_login
- change_login_notice_flash
- change_login_requires_password?
- change_password_requires_password?
- check_account_expiration
- check_active_session
- check_already_logged_in
- check_csrf
- check_csrf?
- check_password_change_allowed
- check_session_expiration
- check_single_session
- clear_cached_otp
- clear_invalid_login_attempts
- clear_session
- close_account
- close_account_requires_password?
- compute_hmac
- compute_raw_hmac
- confirm_password
- confirm_password_redirect
- convert_email_token_key
- convert_session_key
- convert_timestamp
- convert_token_id
- convert_token_id_to_integer
- convert_token_key
- create_account_autologin?
- create_account_notice_flash
- create_account_set_password?
- create_email
- create_email_auth_key
- create_email_to
- create_reset_password_key
- create_verify_account_key
- create_verify_login_change_email
- create_verify_login_change_key
- csrf_tag
- currently_active_session?
- database_function_password_match?
- db
- delete_account
- delete_account_on_close?
- disable_remember_login
- domain
- email_auth_ds
- email_auth_email_link
- email_auth_email_recently_sent?
- email_auth_key_insert_hash
- email_auth_request_form
- email_from
- email_to
- expire_session
- extend_remember_deadline
- extend_remember_deadline_while_logged_in?
- extract_password_hash_cost
- features
- field_attributes
- field_error
- field_error_attributes
- flash
- forget_login
- formatted_field_error
- function_name
- generate_active_sessions_key
- generate_email_auth_key_value
- generate_refresh_token
- generate_remember_key_value
- generate_reset_password_key_value
- generate_unlock_account_key
- generate_verify_account_key_value
- generate_verify_login_change_key_value
- get_active_refresh_token
- get_activity_timestamp
- get_email_auth_email_last_sent
- get_email_auth_key
- get_password_changed_at
- get_password_hash
- get_password_reset_key
- get_remember_key
- get_reset_password_email_last_sent
- get_unlock_account_email_last_sent
- get_unlock_account_key
- get_verify_account_email_last_sent
- get_verify_account_key
- get_verify_login_change_login_and_key
- handle_duplicate_active_session_id
- handle_webauthn_sign_count_verification_error
- has_password?
- hook_action
- http_basic_auth
- inactive_session_cond
- include_success_messages?
- input_field_string
- inputmode_for_field?
- internal_request?
- internal_request_configuration
- invalid_login_attempted
- invalid_previous_password_message
- json_request?
- json_response
- jwt_cors_allow?
- jwt_payload
- jwt_refresh_token_account_ds
- jwt_refresh_token_account_token_ds
- jwt_refresh_token_ds
- jwt_refresh_token_insert_hash
- jwt_refresh_token_match?
- jwt_secret
- jwt_session_hash
- jwt_token
- last_account_activity_at
- last_account_login_at
- load_memory
- loaded_templates
- locked_out?
- logged_in_via_remember_key?
- login
- login_confirm_label
- login_does_not_meet_requirements_message
- login_failed_reset_password_request_form
- login_form_footer_links
- login_hidden_field
- login_input_type
- login_meets_email_requirements?
- login_meets_length_requirements?
- login_meets_requirements?
- login_required
- login_return_to_requested_location_path
- login_session
- login_too_long_message
- login_too_many_bytes_message
- login_too_short_message
- login_uses_email?
- login_valid_email?
- logout
- logout_additional_form_tags
- modifications_require_password?
- new_account
- new_recovery_code
- new_webauthn_credential
- no_longer_active_session
- normalize_session_or_flash_key
- null_byte_parameter_value
- only_json?
- open_account?
- otp_add_key
- otp_available?
- otp_exists?
- otp_hmac_secret
- otp_issuer
- otp_key_ds
- otp_keys_use_hmac?
- otp_last_use
- otp_locked_out?
- otp_new_secret
- otp_provisioning_name
- otp_provisioning_uri
- otp_qr_code
- otp_record_authentication_failure
- otp_remove
- otp_remove_auth_failures
- otp_tmp_key
- otp_update_last_use
- otp_user_key
- otp_valid_code?
- otp_valid_key?
- over_max_bytesize_param_value
- param
- param_or_nil
- password_confirm_label
- password_does_not_contain_null_byte?
- password_does_not_meet_requirements_message
- password_doesnt_match_previous_password?
- password_expiration_ds
- password_expired?
- password_field_autocomplete_value
- password_has_enough_character_groups?
- password_has_no_invalid_pattern?
- password_hash
- password_hash_cost
- password_hash_ds
- password_hash_match?
- password_hash_using_salt
- password_match?
- password_meets_length_requirements?
- password_meets_requirements?
- password_not_in_dictionary?
- password_not_one_of_the_most_common?
- password_not_too_many_repeating_characters?
- password_one_of_most_common?
- password_recently_entered?
- password_reset_ds
- password_too_long_message
- password_too_many_bytes_message
- password_too_short_message
- possible_authentication_methods
- post_configure
- previous_password_ds
- raises_uniqueness_violation?
- random_key
- raw_param
- recovery_code_match?
- recovery_codes_available?
- recovery_codes_ds
- recovery_codes_primary?
- recovery_codes_remove
- redirect
- redirect_two_factor_authenticated
- remember_key_ds
- remember_login
- remembered_session_id
- remove_active_session
- remove_all_active_sessions
- remove_all_webauthn_keys_and_user_ids
- remove_current_session
- remove_email_auth_key
- remove_inactive_sessions
- remove_jwt_refresh_token_key
- remove_lockout_metadata
- remove_remember_key
- remove_reset_password_key
- remove_session_value
- remove_verify_account_key
- remove_verify_login_change_key
- remove_webauthn_key
- render
- render_multi_phase_login_forms
- request
- require_account
- require_account_session
- require_authentication
- require_current_password
- require_http_basic_auth
- require_login
- require_login_confirmation?
- require_otp_setup
- require_password_authentication
- require_password_authentication?
- require_sms_available
- require_sms_not_setup
- require_sms_setup
- require_two_factor_authenticated
- require_two_factor_not_authenticated
- require_two_factor_setup
- require_webauthn_setup
- rescue_jwt_payload
- reset_password_email_link
- reset_password_email_recently_sent?
- reset_password_key_insert_hash
- reset_single_session_key
- response
- retry_on_uniqueness_violation
- return_json_response
- return_response
- route!
- route_path
- route_url
- save_account
- send_email
- send_verify_login_change_email
- serialize_audit_log_metadata
- session
- session_inactivity_deadline_condition
- session_jwt
- session_lifetime_deadline_condition
- session_value
- set_deadline_value
- set_deadline_values?
- set_email_auth_email_last_sent
- set_error_flash
- set_error_reason
- set_expired
- set_field_error
- set_http_basic_auth_error_response
- set_jwt
- set_jwt_refresh_token_hmac_session_key
- set_jwt_token
- set_last_password_entry
- set_login_requirement_error_message
- set_new_account_password
- set_notice_flash
- set_notice_now_flash
- set_password
- set_password_requirement_error_message
- set_redirect_error_flash
- set_redirect_error_status
- set_remember_cookie
- set_reset_password_email_last_sent
- set_response_error_reason_status
- set_response_error_status
- set_session_value
- set_single_session_key
- set_title
- set_unlock_account_email_last_sent
- set_verify_account_email_last_sent
- setup_account_verification
- show_lockout_page
- single_session_ds
- skip_login_field_on_login?
- skip_password_field_on_login?
- skip_status_checks?
- sms_auth_message
- sms_available?
- sms_code
- sms_code_issued_at
- sms_code_match?
- sms_codes_primary?
- sms_confirm
- sms_confirm_failure
- sms_confirm_message
- sms_confirmation_match?
- sms_current_auth?
- sms_disable
- sms_ds
- sms_failures
- sms_locked_out?
- sms_needs_confirmation?
- sms_new_auth_code
- sms_new_confirm_code
- sms_normalize_phone
- sms_phone
- sms_record_failure
- sms_remove_failures
- sms_send
- sms_send_auth_code
- sms_send_confirm_code
- sms_set_code
- sms_setup
- sms_setup?
- sms_valid_phone?
- split_token
- template_path
- throw_basic_auth_error
- throw_error
- throw_error_reason
- throw_error_status
- timing_safe_eql?
- token_link
- token_param_value
- transaction
- translate
- two_factor_auth_links
- two_factor_authenticate
- two_factor_authenticated?
- two_factor_authentication_setup?
- two_factor_login_type_match?
- two_factor_modifications_require_password?
- two_factor_password_match?
- two_factor_remove
- two_factor_remove_auth_failures
- two_factor_remove_links
- two_factor_remove_session
- two_factor_setup_links
- two_factor_update_session
- unique_constraint_violation_class
- unlock_account
- unlock_account_email_link
- unlock_account_email_recently_sent?
- unverified_grace_period_expired?
- update_account
- update_activity
- update_current_session?
- update_hash_ds
- update_last_activity
- update_last_login
- update_login
- update_password_changed_at
- update_password_hash?
- update_session
- update_single_session_key
- update_sms
- use_database_authentication_functions?
- use_date_arithmetic?
- use_json?
- use_jwt?
- use_multi_phase_login?
- use_request_specific_csrf_tokens?
- uses_two_factor_authentication?
- valid_jwt?
- valid_login_entered?
- valid_new_webauthn_credential?
- valid_webauthn_credential_auth?
- verified_account?
- verify_account
- verify_account_check_already_logged_in
- verify_account_ds
- verify_account_email_link
- verify_account_email_recently_sent?
- verify_account_email_resend
- verify_account_key_insert_hash
- verify_account_set_password?
- verify_account_view
- verify_login_change
- verify_login_change_ds
- verify_login_change_email_body
- verify_login_change_email_link
- verify_login_change_key_insert_hash
- verify_login_change_old_login
- view
- webauthn_account_id
- webauthn_auth_additional_form_tags
- webauthn_auth_credential_from_form_submission
- webauthn_auth_form_path
- webauthn_authenticator_selection
- webauthn_credential_options_for_get
- webauthn_extensions
- webauthn_key_insert_hash
- webauthn_keys_ds
- webauthn_origin
- webauthn_remove_authenticated_session
- webauthn_rp_id
- webauthn_rp_name
- webauthn_setup?
- webauthn_setup_credential_from_form_submission
- webauthn_update_session
- webauthn_user_ids_ds
- webauthn_user_name
Classes and Modules
Constants
FEATURES | = | {} | ||
INVALID_DOMAIN | = | "invalidurl @@.com" | ||
MAJOR | = | 2 |
The major version of |
|
MINOR | = | 29 |
The minor version of |
|
TINY | = | 0 |
The patch version of |
|
VERSION | = | "#{MAJOR}.#{MINOR}.#{TINY}".freeze |
The full version of |
|
VERSION_NUMBER | = | MAJOR*10000 + MINOR*100 + TINY |
Public Instance Aliases
account_session_value | -> | account_id | |
ignore_uniqueness_violation | -> | raises_uniqueness_violation? |
If you just want to ignore uniqueness violations, this alias makes more sense. |
logged_in? | -> | session_value | |
raised_uniqueness_violation | -> | raises_uniqueness_violation? |
If you would like to operate/reraise the exception, this alias makes more sense. |
Public Class methods
# File lib/rodauth.rb 46 def self.configure(app, opts={}, &block) 47 json_opt = app.opts[:rodauth_json] = opts.fetch(:json, app.opts[:rodauth_json]) 48 csrf = app.opts[:rodauth_csrf] = opts.fetch(:csrf, app.opts[:rodauth_csrf]) 49 app.opts[:rodauth_route_csrf] = case csrf 50 when false, :rack_csrf 51 false 52 else 53 json_opt != :only 54 end 55 auth_class = (app.opts[:rodauths] ||= {})[opts[:name]] ||= opts[:auth_class] || Class.new(Auth) 56 if !auth_class.roda_class 57 auth_class.roda_class = app 58 elsif auth_class.roda_class != app 59 auth_class = app.opts[:rodauths][opts[:name]] = Class.new(auth_class) 60 auth_class.roda_class = app 61 end 62 auth_class.class_eval{@configuration_name = opts[:name] unless defined?(@configuration_name)} 63 auth_class.configure(&block) if block 64 auth_class.allocate.post_configure if auth_class.method_defined?(:post_configure) 65 end
# File lib/rodauth/migrations.rb 4 def self.create_database_authentication_functions(db, opts={}) 5 table_name = opts[:table_name] || :account_password_hashes 6 get_salt_name = opts[:get_salt_name] || :rodauth_get_salt 7 valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash 8 argon2 = opts[:argon2] 9 10 case db.database_type 11 when :postgres 12 search_path = opts[:search_path] || 'public, pg_temp' 13 primary_key_type = 14 case db.schema(table_name).find { |row| row.first == :id }[1][:db_type] 15 when 'uuid' then :uuid 16 else :int8 17 end 18 table_name = db.literal(table_name) unless table_name.is_a?(String) 19 20 argon_sql = <<END 21 CASE 22 WHEN password_hash ~ '^\\$argon2id' 23 THEN substring(password_hash from '\\$argon2id\\$v=\\d+\\$m=\\d+,t=\\d+,p=\\d+\\$.+\\$') 24 ELSE substr(password_hash, 0, 30) 25 END INTO salt 26 END 27 db.run <<END 28 CREATE OR REPLACE FUNCTION #{get_salt_name}(acct_id #{primary_key_type}) RETURNS text AS $$ 29 DECLARE salt text; 30 BEGIN 31 SELECT 32 #{argon2 ? argon_sql : "substr(password_hash, 0, 30) INTO salt"} 33 FROM #{table_name} 34 WHERE acct_id = id; 35 RETURN salt; 36 END; 37 $$ LANGUAGE plpgsql 38 SECURITY DEFINER 39 SET search_path = #{search_path}; 40 END 41 42 db.run <<END 43 CREATE OR REPLACE FUNCTION #{valid_hash_name}(acct_id #{primary_key_type}, hash text) RETURNS boolean AS $$ 44 DECLARE valid boolean; 45 BEGIN 46 SELECT password_hash = hash INTO valid 47 FROM #{table_name} 48 WHERE acct_id = id; 49 RETURN valid; 50 END; 51 $$ LANGUAGE plpgsql 52 SECURITY DEFINER 53 SET search_path = #{search_path}; 54 END 55 when :mysql 56 argon_sql = <<END 57 CASE 58 WHEN password_hash REGEXP '^.argon2id' 59 THEN left(password_hash, CHAR_LENGTH(password_hash) - INSTR(REVERSE(password_hash), '$')) 60 ELSE substr(password_hash, 1, 30) 61 END 62 END 63 db.run <<END 64 CREATE FUNCTION #{get_salt_name}(acct_id int8) RETURNS varchar(255) 65 SQL SECURITY DEFINER 66 READS SQL DATA 67 BEGIN 68 RETURN (SELECT 69 #{argon2 ? argon_sql : "substr(password_hash, 1, 30)"} 70 FROM #{table_name} 71 WHERE acct_id = id); 72 END; 73 END 74 75 db.run <<END 76 CREATE FUNCTION #{valid_hash_name}(acct_id int8, hash varchar(255)) RETURNS tinyint(1) 77 SQL SECURITY DEFINER 78 READS SQL DATA 79 BEGIN 80 DECLARE valid tinyint(1); 81 DECLARE csr CURSOR FOR 82 SELECT password_hash = hash 83 FROM #{table_name} 84 WHERE acct_id = id; 85 OPEN csr; 86 FETCH csr INTO valid; 87 CLOSE csr; 88 RETURN valid; 89 END; 90 END 91 when :mssql 92 argon_sql = <<END 93 CASE 94 WHEN password_hash LIKE '[$]argon2id%' 95 THEN left(password_hash, len(password_hash) - charindex('$', reverse(password_hash))) 96 ELSE substring(password_hash, 0, 30) 97 END 98 END 99 db.run <<END 100 CREATE FUNCTION #{get_salt_name}(@account_id bigint) RETURNS nvarchar(255) 101 WITH EXECUTE AS OWNER 102 AS 103 BEGIN 104 DECLARE @salt nvarchar(255); 105 SELECT @salt = 106 #{argon2 ? argon_sql : "substring(password_hash, 0, 30)"} 107 FROM #{table_name} 108 WHERE id = @account_id; 109 RETURN @salt; 110 END; 111 END 112 113 db.run <<END 114 CREATE FUNCTION #{valid_hash_name}(@account_id bigint, @hash nvarchar(255)) RETURNS bit 115 WITH EXECUTE AS OWNER 116 AS 117 BEGIN 118 DECLARE @valid bit; 119 DECLARE @ph nvarchar(255); 120 SELECT @ph = password_hash 121 FROM #{table_name} 122 WHERE id = @account_id; 123 IF(@hash = @ph) 124 SET @valid = 1; 125 ELSE 126 SET @valid = 0 127 RETURN @valid; 128 END; 129 END 130 end 131 end
# File lib/rodauth/migrations.rb 153 def self.create_database_previous_password_check_functions(db, opts={}) 154 create_database_authentication_functions(db, {:table_name=>:account_previous_password_hashes, :get_salt_name=>:rodauth_get_previous_salt, :valid_hash_name=>:rodauth_previous_password_hash_match}.merge(opts)) 155 end
# File lib/rodauth/migrations.rb 133 def self.drop_database_authentication_functions(db, opts={}) 134 table_name = opts[:table_name] || :account_password_hashes 135 get_salt_name = opts[:get_salt_name] || :rodauth_get_salt 136 valid_hash_name = opts[:valid_hash_name] || :rodauth_valid_password_hash 137 138 case db.database_type 139 when :postgres 140 primary_key_type = 141 case db.schema(table_name).find { |row| row.first == :id }[1][:db_type] 142 when 'uuid' then :uuid 143 else :int8 144 end 145 db.run "DROP FUNCTION #{get_salt_name}(#{primary_key_type})" 146 db.run "DROP FUNCTION #{valid_hash_name}(#{primary_key_type}, text)" 147 when :mysql, :mssql 148 db.run "DROP FUNCTION #{get_salt_name}" 149 db.run "DROP FUNCTION #{valid_hash_name}" 150 end 151 end
# File lib/rodauth/migrations.rb 157 def self.drop_database_previous_password_check_functions(db, opts={}) 158 drop_database_authentication_functions(db, {:table_name=>:account_previous_password_hashes, :get_salt_name=>:rodauth_get_previous_salt, :valid_hash_name=>:rodauth_previous_password_hash_match}.merge(opts)) 159 end
# File lib/rodauth.rb 6 def self.lib(opts={}, &block) 7 require 'roda' 8 c = Class.new(Roda) 9 c.plugin(:rodauth, opts) do 10 enable :internal_request 11 instance_exec(&block) 12 end 13 c.freeze 14 c.rodauth 15 end
# File lib/rodauth.rb 17 def self.load_dependencies(app, opts={}) 18 json_opt = opts.fetch(:json, app.opts[:rodauth_json]) 19 if json_opt 20 app.plugin :json 21 app.plugin :json_parser 22 end 23 24 unless json_opt == :only 25 unless opts[:render] == false 26 require 'tilt/string' 27 app.plugin :render 28 end 29 30 case opts.fetch(:csrf, app.opts[:rodauth_csrf]) 31 when false 32 # nothing 33 when :rack_csrf 34 # :nocov: 35 app.plugin :csrf 36 # :nocov: 37 else 38 app.plugin :route_csrf 39 end 40 41 app.plugin :flash unless opts[:flash] == false 42 app.plugin :h 43 end 44 end
# File lib/rodauth/features/base.rb 139 def initialize(scope) 140 @scope = scope 141 end
Public Instance methods
# File lib/rodauth/features/email_auth.rb 243 def _account_from_email_auth_key(token) 244 account_from_key(token, account_open_status_value){|id| get_email_auth_key(id)} 245 end
# File lib/rodauth/features/base.rb 697 def _account_from_login(login) 698 ds = account_table_ds.where(login_column=>login) 699 ds = ds.select(*account_select) if account_select 700 ds = ds.where(account_status_column=>[account_unverified_status_value, account_open_status_value]) unless skip_status_checks? 701 ds.first 702 end
# File lib/rodauth/features/jwt_refresh.rb 111 def _account_from_refresh_token(token) 112 id, token_id, key = _account_refresh_token_split(token) 113 114 unless key && 115 (id.to_s == session_value.to_s) && 116 (actual = get_active_refresh_token(id, token_id)) && 117 timing_safe_eql?(key, convert_token_key(actual)) && 118 jwt_refresh_token_match?(key) 119 return 120 end 121 122 ds = account_ds(id) 123 ds = ds.where(account_session_status_filter) unless skip_status_checks? 124 ds.first 125 end
# File lib/rodauth/features/reset_password.rb 255 def _account_from_reset_password_key(token) 256 account_from_key(token, account_open_status_value){|id| get_password_reset_key(id)} 257 end
# File lib/rodauth/features/base.rb 704 def _account_from_session 705 ds = account_ds(session_value) 706 ds = ds.where(account_session_status_filter) unless skip_status_checks? 707 ds.first 708 end
# File lib/rodauth/features/lockout.rb 299 def _account_from_unlock_key(token) 300 account_from_key(token){|id| account_lockouts_ds(id).get(account_lockouts_key_column)} 301 end
# File lib/rodauth/features/verify_account.rb 309 def _account_from_verify_account_key(token) 310 account_from_key(token, account_unverified_status_value){|id| get_verify_account_key(id)} 311 end
# File lib/rodauth/features/verify_login_change.rb 212 def _account_from_verify_login_change_key(token) 213 account_from_key(token) do |id| 214 @verify_login_change_new_login, key = get_verify_login_change_login_and_key(id) 215 key 216 end 217 end
# File lib/rodauth/features/jwt_refresh.rb 127 def _account_refresh_token_split(token) 128 id, token = split_token(token) 129 id = convert_token_id(id) 130 return unless id && token 131 132 token_id, key = split_token(token) 133 token_id = convert_token_id(token_id) 134 return unless token_id && key 135 136 [id, token_id, key] 137 end
# File lib/rodauth/features/base.rb 510 def _around_rodauth 511 yield 512 end
# File lib/rodauth/features/email_auth.rb 185 def _email_auth_request 186 if email_auth_email_recently_sent? 187 set_redirect_error_flash email_auth_email_recently_sent_error_flash 188 redirect email_auth_email_recently_sent_redirect 189 end 190 191 generate_email_auth_key_value 192 transaction do 193 before_email_auth_request 194 create_email_auth_key 195 send_email_auth_email 196 after_email_auth_request 197 end 198 199 set_notice_flash email_auth_email_sent_notice_flash 200 end
# File lib/rodauth/features/email_auth.rb 180 def _email_auth_request_and_redirect 181 _email_auth_request 182 redirect email_auth_email_sent_redirect 183 end
# File lib/rodauth/features/base.rb 716 def _field_attributes(field) 717 nil 718 end
# File lib/rodauth/features/base.rb 720 def _field_error_attributes(field) 721 " aria-invalid=\"true\" aria-describedby=\"#{field}_error_message\" " 722 end
# File lib/rodauth/features/base.rb 811 def _filter_links(links) 812 links.select!{|_, link| link} 813 links.sort! 814 links 815 end
# File lib/rodauth/features/base.rb 724 def _formatted_field_error(field, error) 725 "<span class=\"#{input_field_error_message_class}\" id=\"#{field}_error_message\">#{error}</span>" 726 end
# File lib/rodauth/features/remember.rb 223 def _get_remember_cookie 224 request.cookies[remember_cookie_key] 225 end
# File lib/rodauth/features/json.rb 184 def _json_response_body(hash) 185 request.send(:convert_to_json, hash) 186 end
# File lib/rodauth/features/jwt.rb 110 def _jwt_decode_opts 111 jwt_decode_opts 112 end
# File lib/rodauth/features/login.rb 154 def _login(auth_type) 155 warn("Deprecated #_login method called, use #login instead.") 156 login(auth_type) 157 end
# File lib/rodauth/features/email_auth.rb 170 def _multi_phase_login_forms 171 forms = super 172 forms << [30, email_auth_request_form, :_email_auth_request_and_redirect] if valid_login_entered? && allow_email_auth? 173 forms 174 end
# File lib/rodauth/features/create_account.rb 121 def _new_account(login) 122 acc = {login_column=>login} 123 unless skip_status_checks? 124 acc[account_status_column] = account_initial_status_value 125 end 126 acc 127 end
# File lib/rodauth/features/otp.rb 414 def _otp 415 otp_class.new(otp_user_key, :issuer=>otp_issuer, :digits=>otp_digits, :interval=>otp_interval) 416 end
# File lib/rodauth/features/otp.rb 403 def _otp_add_key(secret) 404 # Uniqueness errors can't be handled here, as we can't be sure the secret provided 405 # is the same as the current secret. 406 otp_key_ds.insert(otp_keys_id_column=>session_value, otp_keys_column=>secret) 407 end
# File lib/rodauth/features/otp.rb 409 def _otp_key 410 @otp_user_key = nil 411 otp_key_ds.get(otp_keys_column) 412 end
# File lib/rodauth/features/otp.rb 397 def _otp_tmp_key(secret) 398 @otp_tmp_key = true 399 @otp_user_key = nil 400 @otp_key = secret 401 end
# File lib/rodauth/features/recovery_codes.rb 260 def _recovery_codes 261 recovery_codes_ds.select_map(recovery_codes_column) 262 end
# File lib/rodauth/features/json.rb 174 def _return_json_response 175 response.status ||= json_response_error_status if json_response[json_response_error_key] 176 response['Content-Type'] ||= json_response_content_type 177 return_response _json_response_body(json_response) 178 end
# File lib/rodauth/features/lockout.rb 171 def _setup_account_lockouts_hash(account_id, key) 172 hash = {account_lockouts_id_column=>account_id, account_lockouts_key_column=>key} 173 set_deadline_value(hash, account_lockouts_deadline_column, account_lockouts_deadline_interval) 174 hash 175 end
Set the template path only if there isn’t an overridden template in the application. Result should replace existing template opts.
# File lib/rodauth/features/base.rb 854 def _template_opts(opts, page) 855 opts = scope.send(:find_template, scope.send(:parse_template_opts, page, opts)) 856 unless File.file?(scope.send(:template_path, opts)) 857 opts[:path] = template_path(page) 858 end 859 opts 860 end
# File lib/rodauth/features/confirm_password.rb 79 def _two_factor_auth_links 80 links = (super if defined?(super)) || [] 81 if authenticated_by.length == 1 && !authenticated_by.include?('password') && has_password? 82 links << [5, confirm_password_path, confirm_password_link_text] 83 end 84 links 85 end
# File lib/rodauth/features/otp.rb 353 def _two_factor_remove_all_from_session 354 two_factor_remove_session('totp') 355 super 356 end
# File lib/rodauth/features/otp.rb 347 def _two_factor_remove_links 348 links = super 349 links << [20, otp_disable_path, otp_disable_link_text] if otp_exists? 350 links 351 end
# File lib/rodauth/features/otp.rb 341 def _two_factor_setup_links 342 links = super 343 links << [20, otp_setup_path, otp_setup_link_text] unless otp_exists? 344 links 345 end
# File lib/rodauth/features/change_login.rb 83 def _update_login(login) 84 updated = nil 85 raised = raises_uniqueness_violation?{updated = update_account({login_column=>login}, account_ds.exclude(login_column=>login)) == 1} 86 if raised 87 set_login_requirement_error_message(:already_an_account_with_this_login, already_an_account_with_this_login_message) 88 end 89 updated && !raised 90 end
# File lib/rodauth/features/base.rb 862 def _view(meth, page) 863 scope.send(meth, _view_opts(page)) 864 end
# File lib/rodauth/features/base.rb 843 def _view_opts(page) 844 opts = template_opts.dup 845 opts[:locals] = opts[:locals] ? opts[:locals].dup : {} 846 opts[:locals][:rodauth] = self 847 opts[:cache] = cache_templates 848 opts[:cache_key] = :"rodauth_#{page}" 849 _template_opts(opts, page) 850 end
# File lib/rodauth/features/base.rb 358 def account! 359 account || (session_value && account_from_session) 360 end
# File lib/rodauth/features/account_expiration.rb 104 def account_activity_ds(account_id) 105 db[account_activity_table]. 106 where(account_activity_id_column=>account_id) 107 end
# File lib/rodauth/features/base.rb 736 def account_ds(id=account_id) 737 raise ArgumentError, "invalid account id passed to account_ds" unless id 738 ds = account_table_ds.where(account_id_column=>id) 739 ds = ds.select(*account_select) if account_select 740 ds 741 end
# File lib/rodauth/features/account_expiration.rb 54 def account_expired? 55 columns = [account_activity_last_activity_column, account_activity_last_login_column, account_activity_expired_column] 56 last_activity, last_login, expired = account_activity_ds(account_id).get(columns) 57 return true if expired 58 timestamp = convert_timestamp(expire_account_on_last_activity? ? last_activity : last_login) 59 return false unless timestamp 60 timestamp < Time.now - expire_account_after 61 end
# File lib/rodauth/features/account_expiration.rb 35 def account_expired_at 36 get_activity_timestamp(account_id, account_activity_expired_column) 37 end
# File lib/rodauth/features/email_auth.rb 133 def account_from_email_auth_key(key) 134 @account = _account_from_email_auth_key(key) 135 end
# File lib/rodauth/features/email_base.rb 65 def account_from_key(token, status_id=nil) 66 id, key = split_token(token) 67 id = convert_token_id(id) 68 return unless id && key 69 70 return unless actual = yield(id) 71 72 unless timing_safe_eql?(key, convert_email_token_key(actual)) 73 if hmac_secret && allow_raw_email_token? 74 return unless timing_safe_eql?(key, actual) 75 else 76 return 77 end 78 end 79 ds = account_ds(id) 80 ds = ds.where(account_status_column=>status_id) if status_id && !skip_status_checks? 81 ds.first 82 end
# File lib/rodauth/features/base.rb 260 def account_from_login(login) 261 @account = _account_from_login(login) 262 end
# File lib/rodauth/features/jwt_refresh.rb 88 def account_from_refresh_token(token) 89 @account = _account_from_refresh_token(token) 90 end
# File lib/rodauth/features/remember.rb 203 def account_from_remember_cookie 204 unless id = remembered_session_id 205 # Only set expired cookie if there is already a cookie set. 206 forget_login if _get_remember_cookie 207 return 208 end 209 210 set_session_value(session_key, id) 211 account_from_session 212 remove_session_value(session_key) 213 214 unless account 215 remove_remember_key(id) 216 forget_login 217 return 218 end 219 220 account 221 end
# File lib/rodauth/features/password_expiration.rb 45 def account_from_reset_password_key(key) 46 if a = super 47 check_password_change_allowed 48 end 49 a 50 end
# File lib/rodauth/features/base.rb 362 def account_from_session 363 @account = _account_from_session 364 end
# File lib/rodauth/features/lockout.rb 222 def account_from_unlock_key(key) 223 @account = _account_from_unlock_key(key) 224 end
# File lib/rodauth/features/verify_account.rb 203 def account_from_verify_account_key(key) 204 @account = _account_from_verify_account_key(key) 205 end
# File lib/rodauth/features/verify_login_change.rb 122 def account_from_verify_login_change_key(key) 123 @account = _account_from_verify_login_change_key(key) 124 end
# File lib/rodauth/features/base.rb 250 def account_id 251 account[account_id_column] 252 end
# File lib/rodauth/features/verify_account_grace_period.rb 85 def account_in_unverified_grace_period? 86 return false unless account! 87 account[account_status_column] == account_unverified_status_value && 88 verify_account_grace_period && 89 !verify_account_ds.where(Sequel.date_add(verification_requested_at_column, :seconds=>verify_account_grace_period) > Sequel::CURRENT_TIMESTAMP).empty? 90 end
# File lib/rodauth/features/base.rb 354 def account_initial_status_value 355 account_open_status_value 356 end
# File lib/rodauth/features/lockout.rb 295 def account_lockouts_ds(id=account_id) 296 db[account_lockouts_table].where(account_lockouts_id_column=>id) 297 end
# File lib/rodauth/features/lockout.rb 291 def account_login_failures_ds 292 db[account_login_failures_table].where(account_login_failures_id_column=>account_id) 293 end
If the account_password_hash_column
is set, the password hash is verified in ruby, it will not use a database function to do so, it will check the password hash using bcrypt.
# File lib/rodauth/features/base.rb 280 def account_password_hash_column 281 nil 282 end
# File lib/rodauth/features/base.rb 728 def account_session_status_filter 729 {account_status_column=>account_open_status_value} 730 end
# File lib/rodauth/features/base.rb 743 def account_table_ds 744 db[accounts_table] 745 end
# File lib/rodauth/features/webauthn.rb 265 def account_webauthn_ids 266 webauthn_keys_ds.select_map(webauthn_keys_webauthn_id_column) 267 end
# File lib/rodauth/features/webauthn.rb 269 def account_webauthn_usage 270 webauthn_keys_ds.select_hash(webauthn_keys_webauthn_id_column, webauthn_keys_last_use_column) 271 end
# File lib/rodauth/features/webauthn.rb 273 def account_webauthn_user_id 274 unless webauthn_id = webauthn_user_ids_ds.get(webauthn_user_ids_webauthn_id_column) 275 webauthn_id = WebAuthn.generate_user_id 276 if e = raised_uniqueness_violation do 277 webauthn_user_ids_ds.insert( 278 webauthn_user_ids_account_id_column => webauthn_account_id, 279 webauthn_user_ids_webauthn_id_column => webauthn_id 280 ) 281 end 282 # If two requests to create a webauthn user id are sent at the same time and an insert 283 # is attempted for both, one will fail with a unique constraint violation. In that case 284 # it is safe for the second one to use the webauthn user id inserted by the other request. 285 # If there is still no webauthn user id at this point, then we'll just reraise the 286 # exception. 287 # :nocov: 288 raise e unless webauthn_id = webauthn_user_ids_ds.get(webauthn_user_ids_webauthn_id_column) 289 # :nocov: 290 end 291 end 292 293 webauthn_id 294 end
# File lib/rodauth/features/remember.rb 251 def active_remember_key_ds(id=account_id) 252 remember_key_ds(id).where(Sequel.expr(remember_deadline_column) > Sequel::CURRENT_TIMESTAMP) 253 end
# File lib/rodauth/features/active_sessions.rb 179 def active_sessions_ds 180 db[active_sessions_table]. 181 where(active_sessions_account_id_column=>session_value) 182 end
# File lib/rodauth/features/active_sessions.rb 148 def active_sessions_insert_hash 149 {active_sessions_account_id_column => session_value, active_sessions_session_id_column => compute_hmac(active_sessions_key)} 150 end
# File lib/rodauth/features/active_sessions.rb 152 def active_sessions_update_hash 153 {active_sessions_last_use_column => Sequel::CURRENT_TIMESTAMP} 154 end
# File lib/rodauth/features/active_sessions.rb 66 def add_active_session 67 key = generate_active_sessions_key 68 set_session_value(session_id_session_key, key) 69 if e = raises_uniqueness_violation?{active_sessions_ds.insert(active_sessions_insert_hash)} 70 handle_duplicate_active_session_id(e) 71 end 72 nil 73 end
# File lib/rodauth/features/audit_logging.rb 39 def add_audit_log(account_id, action) 40 if hash = audit_log_insert_hash(account_id, action) 41 audit_log_ds.insert(hash) 42 end 43 end
# File lib/rodauth/features/base.rb 180 def add_field_error_class(field) 181 if field_error(field) 182 " #{input_field_error_class}" 183 end 184 end
# File lib/rodauth/features/disallow_password_reuse.rb 25 def add_previous_password_hash(hash) 26 ds = previous_password_ds 27 28 unless @dont_check_previous_password 29 keep_before = ds.reverse(previous_password_id_column). 30 limit(nil, previous_passwords_to_check). 31 get(previous_password_id_column) 32 33 if keep_before 34 ds.where(Sequel.expr(previous_password_id_column) <= keep_before). 35 delete 36 end 37 end 38 39 # This should never raise uniqueness violations, as it uses a serial primary key 40 ds.insert(previous_password_account_id_column=>account_id, previous_password_hash_column=>hash) 41 end
# File lib/rodauth/features/recovery_codes.rb 187 def add_recovery_code 188 # This should never raise uniqueness violations unless the recovery code is the same, and the odds of that 189 # are 1/256**32 assuming a good random number generator. Still, attempt to handle that case by retrying 190 # on such a uniqueness violation. 191 retry_on_uniqueness_violation do 192 recovery_codes_ds.insert(recovery_codes_id_column=>session_value, recovery_codes_column=>new_recovery_code) 193 end 194 end
# File lib/rodauth/features/recovery_codes.rb 177 def add_recovery_codes(number) 178 return if number <= 0 179 transaction do 180 number.times do 181 add_recovery_code 182 end 183 end 184 remove_instance_variable(:@recovery_codes) 185 end
# File lib/rodauth/features/remember.rb 155 def add_remember_key 156 hash = {remember_id_column=>account_id, remember_key_column=>remember_key_value} 157 set_deadline_value(hash, remember_deadline_column, remember_deadline_interval) 158 159 if e = raised_uniqueness_violation{remember_key_ds.insert(hash)} 160 # If inserting into the remember key table causes a violation, we can pull the 161 # existing row from the table. If there is no invalid row, we can then reraise. 162 raise e unless @remember_key_value = active_remember_key_ds.get(remember_key_column) 163 end 164 end
# File lib/rodauth/features/recovery_codes.rb 151 def add_webauthn_credential(_) 152 super if defined?(super) 153 auto_add_missing_recovery_codes 154 end
# File lib/rodauth/features/change_password_notify.rb 11 def after_change_password 12 super 13 send_password_changed_email 14 end
# File lib/rodauth/features/account_expiration.rb 99 def after_close_account 100 super if defined?(super) 101 account_activity_ds(account_id).delete 102 end
# File lib/rodauth/features/disallow_password_reuse.rb 87 def after_create_account 88 if account_password_hash_column && !(respond_to?(:verify_account_set_password?) && verify_account_set_password?) 89 add_previous_password_hash(password_hash(param(password_param))) 90 end 91 super if defined?(super) 92 end
# File lib/rodauth/features/email_auth.rb 208 def after_login 209 # Remove the email auth key after any login, even if 210 # it is a password login. This is done to invalidate 211 # the email login when a user has a password and requests 212 # email authentication, but then remembers their password 213 # and doesn't need the link. At that point, the link 214 # that allows login access to the account becomes a 215 # security liability, and it is best to remove it. 216 remove_email_auth_key 217 super 218 end
# File lib/rodauth/features/email_auth.rb 151 def after_login_entered_during_multi_phase_login 152 # If forcing email auth, just send the email link. 153 _email_auth_request_and_redirect if force_email_auth? 154 155 super 156 end
# File lib/rodauth/features/lockout.rb 258 def after_login_failure 259 invalid_login_attempted 260 super 261 end
# File lib/rodauth/features/remember.rb 227 def after_logout 228 forget_login 229 super if defined?(super) 230 end
# File lib/rodauth/features/recovery_codes.rb 225 def after_otp_disable 226 super if defined?(super) 227 auto_remove_recovery_codes 228 end
# File lib/rodauth/features/active_sessions.rb 116 def after_refresh_token 117 super if defined?(super) 118 if prev_key = session[session_id_session_key] 119 key = generate_active_sessions_key 120 set_session_value(session_id_session_key, key) 121 active_sessions_ds. 122 where(active_sessions_session_id_column => compute_hmac(prev_key)). 123 update(active_sessions_session_id_column => compute_hmac(key)) 124 end 125 end
# File lib/rodauth/features/password_grace_period.rb 39 def after_reset_password 40 super if defined?(super) 41 @last_password_entry = Time.now.to_i 42 end
# File lib/rodauth/features/recovery_codes.rb 230 def after_sms_disable 231 super if defined?(super) 232 auto_remove_recovery_codes 233 end
# File lib/rodauth/features/recovery_codes.rb 235 def after_webauthn_remove 236 super if defined?(super) 237 auto_remove_recovery_codes 238 end
# File lib/rodauth/features/email_auth.rb 204 def allow_email_auth? 205 defined?(super) ? super : true 206 end
# File lib/rodauth/features/verify_account.rb 170 def allow_resending_verify_account_email? 171 account[account_status_column] == account_unverified_status_value 172 end
# File lib/rodauth/features/base.rb 288 def already_logged_in 289 nil 290 end
:nocov:
# File lib/rodauth/features/argon2.rb 82 def argon2_hash_algorithm?(hash) 83 hash.start_with?('$argon2id$') 84 end
# File lib/rodauth/features/argon2.rb 71 def argon2_hash_cost 72 {t_cost: 1, m_cost: 3} 73 end
# File lib/rodauth/features/argon2.rb 86 def argon2_password_hash_match?(hash, password) 87 ::Argon2::Password.verify_password(password, hash, argon2_secret) 88 end
# File lib/rodauth/features/argon2.rb 33 def argon2_salt_option 34 :salt_do_not_supply 35 end
# File lib/rodauth/features/audit_logging.rb 83 def audit_log_ds 84 ds = db[audit_logging_table] 85 # :nocov: 86 if db.database_type == :postgres 87 # :nocov: 88 # For PostgreSQL, use RETURNING NULL. This allows the feature 89 # to be used with INSERT but not SELECT permissions on the 90 # table, useful for audit logging where the database user 91 # the application is running as should not need to read the 92 # logs. 93 ds = ds.returning(nil) 94 end 95 ds 96 end
# File lib/rodauth/features/audit_logging.rb 45 def audit_log_insert_hash(account_id, action) 46 if message = audit_log_message(action) 47 { 48 audit_logging_account_id_column => account_id, 49 audit_logging_message_column => message, 50 audit_logging_metadata_column => serialize_audit_log_metadata(audit_log_metadata(action)) 51 } 52 end 53 end
# File lib/rodauth/features/audit_logging.rb 63 def audit_log_message(action) 64 meth = :"audit_log_message_for_#{action}" 65 if respond_to?(meth, true) 66 send(meth) 67 else 68 audit_log_message_default(action) 69 end 70 end
# File lib/rodauth/features/audit_logging.rb 59 def audit_log_message_default(action) 60 action.to_s 61 end
# File lib/rodauth/features/audit_logging.rb 72 def audit_log_metadata(action) 73 meth = :"audit_log_metadata_for_#{action}" 74 if respond_to?(meth, true) 75 send(meth) 76 else 77 audit_log_metadata_default 78 end 79 end
# File lib/rodauth/features/base.rb 131 def auth_class_eval(&block) 132 auth.class_eval(&block) 133 end
# File lib/rodauth/features/base.rb 341 def authenticated? 342 logged_in? 343 end
# File lib/rodauth/features/base.rb 440 def authenticated_by 441 session[authenticated_by_session_key] 442 end
# File lib/rodauth/features/webauthn.rb 245 def authenticated_webauthn_id 246 session[authenticated_webauthn_id_session_key] 247 end
# File lib/rodauth/features/recovery_codes.rb 248 def auto_add_missing_recovery_codes 249 if auto_add_recovery_codes? 250 add_recovery_codes(recovery_codes_limit - recovery_codes.length) 251 end 252 end
# File lib/rodauth/features/recovery_codes.rb 254 def auto_remove_recovery_codes 255 if auto_remove_recovery_codes? && (%w'totp webauthn sms_code' & possible_authentication_methods).empty? 256 recovery_codes_remove 257 end 258 end
# File lib/rodauth/features/base.rb 210 def autocomplete_for_field?(_param) 211 mark_input_fields_with_autocomplete? 212 end
# File lib/rodauth/features/base.rb 453 def autologin_session(autologin_type) 454 login_session('autologin') 455 set_session_value(autologin_type_session_key, autologin_type) 456 end
# File lib/rodauth/features/base.rb 449 def autologin_type 450 session[autologin_type_session_key] 451 end
# File lib/rodauth/features/otp.rb 392 def base32_encode(data, length) 393 chars = 'abcdefghijklmnopqrstuvwxyz234567' 394 length.times.map{|i|chars[data[i].ord % 32]}.join 395 end
# File lib/rodauth/features/base.rb 490 def base_url 491 url = String.new("#{request.scheme}://#{domain}") 492 url << ":#{request.port}" if request.port != Rack::Request::DEFAULT_PORTS[request.scheme] 493 url 494 end
# File lib/rodauth/features/verify_account_grace_period.rb 58 def before_change_login_route 59 unless verified_account? 60 set_redirect_error_flash unverified_change_login_error_flash 61 redirect unverified_change_login_redirect 62 end 63 super if defined?(super) 64 end
# File lib/rodauth/features/password_expiration.rb 90 def before_change_password_route 91 check_password_change_allowed 92 super 93 end
# File lib/rodauth/features/disallow_password_reuse.rb 77 def before_create_account_route 78 super if defined?(super) 79 @dont_check_previous_password = true 80 end
# File lib/rodauth/features/lockout.rb 246 def before_login_attempt 247 if locked_out? 248 show_lockout_page 249 end 250 super 251 end
# File lib/rodauth/features/active_sessions.rb 132 def before_logout 133 if param_or_nil(global_logout_param) 134 remove_remember_key(session_value) if respond_to?(:remove_remember_key) 135 remove_all_active_sessions 136 else 137 remove_current_session 138 end 139 super 140 end
# File lib/rodauth/features/json.rb 134 def before_otp_setup_route 135 super if defined?(super) 136 if use_json? && otp_keys_use_hmac? && !param_or_nil(otp_setup_raw_param) 137 _otp_tmp_key(otp_new_secret) 138 json_response[otp_setup_param] = otp_user_key 139 json_response[otp_setup_raw_param] = otp_key 140 end 141 end
# File lib/rodauth/features/account_expiration.rb 79 def before_reset_password 80 check_account_expiration 81 super if defined?(super) 82 end
# File lib/rodauth/features/account_expiration.rb 84 def before_reset_password_request 85 check_account_expiration 86 super if defined?(super) 87 end
# File lib/rodauth/features/json.rb 143 def before_rodauth 144 if json_request? 145 if json_check_accept? && (accept = request.env['HTTP_ACCEPT']) && accept !~ json_accept_regexp 146 response.status = 406 147 json_response[json_response_error_key] = json_not_accepted_error_message 148 _return_json_response 149 end 150 151 unless request.post? 152 response.status = 405 153 response.headers['Allow'] = 'POST' 154 json_response[json_response_error_key] = json_non_post_error_message 155 return_json_response 156 end 157 elsif only_json? 158 response.status = json_response_error_status 159 return_response non_json_request_error_message 160 end 161 162 super 163 end
# File lib/rodauth/features/json.rb 80 def before_two_factor_auth_route 81 super if defined?(super) 82 if use_json? 83 json_response[:auth_links] = two_factor_auth_links.sort.map{|_,link| link} 84 json_response[json_response_success_key] ||= "" if include_success_messages? 85 return_json_response 86 end 87 end
# File lib/rodauth/features/json.rb 70 def before_two_factor_manage_route 71 super if defined?(super) 72 if use_json? 73 json_response[:setup_links] = two_factor_setup_links.sort.map{|_,link| link} 74 json_response[:remove_links] = two_factor_remove_links.sort.map{|_,link| link} 75 json_response[json_response_success_key] ||= "" if include_success_messages? 76 return_json_response 77 end 78 end
# File lib/rodauth/features/account_expiration.rb 89 def before_unlock_account 90 check_account_expiration 91 super if defined?(super) 92 end
# File lib/rodauth/features/account_expiration.rb 94 def before_unlock_account_request 95 check_account_expiration 96 super if defined?(super) 97 end
# File lib/rodauth/features/webauthn_verify_account.rb 30 def before_verify_account 31 super 32 if features.include?(:json) && use_json? && !param_or_nil(webauthn_setup_param) 33 cred = new_webauthn_credential 34 json_response[webauthn_setup_param] = cred.as_json 35 json_response[webauthn_setup_challenge_param] = cred.challenge 36 json_response[webauthn_setup_challenge_hmac_param] = compute_hmac(cred.challenge) 37 end 38 @webauthn_credential = webauthn_setup_credential_from_form_submission 39 add_webauthn_credential(@webauthn_credential) 40 end
# File lib/rodauth/features/disallow_password_reuse.rb 82 def before_verify_account_route 83 super if defined?(super) 84 @dont_check_previous_password = true 85 end
# File lib/rodauth/features/json.rb 89 def before_view_recovery_codes 90 super if defined?(super) 91 if use_json? 92 json_response[:codes] = recovery_codes 93 json_response[json_response_success_key] ||= "" if include_success_messages? 94 end 95 end
# File lib/rodauth/features/json.rb 107 def before_webauthn_auth_route 108 super if defined?(super) 109 if use_json? && !param_or_nil(webauthn_auth_param) 110 cred = webauthn_credential_options_for_get 111 json_response[webauthn_auth_param] = cred.as_json 112 json_response[webauthn_auth_challenge_param] = cred.challenge 113 json_response[webauthn_auth_challenge_hmac_param] = compute_hmac(cred.challenge) 114 end 115 end
# File lib/rodauth/features/json.rb 117 def before_webauthn_login_route 118 super if defined?(super) 119 if use_json? && !param_or_nil(webauthn_auth_param) && account_from_login(param(login_param)) 120 cred = webauthn_credential_options_for_get 121 json_response[webauthn_auth_param] = cred.as_json 122 json_response[webauthn_auth_challenge_param] = cred.challenge 123 json_response[webauthn_auth_challenge_hmac_param] = compute_hmac(cred.challenge) 124 end 125 end
# File lib/rodauth/features/json.rb 127 def before_webauthn_remove_route 128 super if defined?(super) 129 if use_json? && !param_or_nil(webauthn_remove_param) 130 json_response[webauthn_remove_param] = account_webauthn_usage 131 end 132 end
# File lib/rodauth/features/json.rb 97 def before_webauthn_setup_route 98 super if defined?(super) 99 if use_json? && !param_or_nil(webauthn_setup_param) 100 cred = new_webauthn_credential 101 json_response[webauthn_setup_param] = cred.as_json 102 json_response[webauthn_setup_challenge_param] = cred.challenge 103 json_response[webauthn_setup_challenge_hmac_param] = compute_hmac(cred.challenge) 104 end 105 end
# File lib/rodauth/features/base.rb 390 def button(value, opts={}) 391 scope.render(button_opts(value, opts)) 392 end
# File lib/rodauth/features/base.rb 382 def button_opts(value, opts) 383 opts = Hash[template_opts].merge!(opts) 384 opts[:locals] = {:value=>value, :opts=>opts} 385 opts[:cache] = cache_templates 386 opts[:cache_key] = :rodauth_button 387 _template_opts(opts, 'button') 388 end
# File lib/rodauth/features/recovery_codes.rb 173 def can_add_recovery_codes? 174 recovery_codes.length < recovery_codes_limit 175 end
# File lib/rodauth/features/base.rb 601 def catch_error(&block) 602 catch(:rodauth_error, &block) 603 end
# File lib/rodauth/features/change_login.rb 68 def change_login(login) 69 if account_ds.get(login_column).downcase == login.downcase 70 set_login_requirement_error_message(:same_as_current_login, same_as_current_login_message) 71 return false 72 end 73 74 update_login(login) 75 end
# File lib/rodauth/features/verify_login_change.rb 138 def change_login_notice_flash 139 change_login_needs_verification_notice_flash 140 end
# File lib/rodauth/features/change_login.rb 64 def change_login_requires_password? 65 modifications_require_password? 66 end
# File lib/rodauth/features/change_password.rb 68 def change_password_requires_password? 69 modifications_require_password? 70 end
# File lib/rodauth/features/account_expiration.rb 63 def check_account_expiration 64 if account_expired? 65 set_expired unless account_expired_at 66 set_redirect_error_flash account_expiration_error_flash 67 redirect account_expiration_redirect 68 end 69 update_last_login 70 end
# File lib/rodauth/features/active_sessions.rb 52 def check_active_session 53 if logged_in? && !currently_active_session? 54 no_longer_active_session 55 end 56 end
# File lib/rodauth/features/base.rb 284 def check_already_logged_in 285 already_logged_in if logged_in? 286 end
# File lib/rodauth/features/base.rb 366 def check_csrf 367 scope.check_csrf!(check_csrf_opts, &check_csrf_block) 368 end
# File lib/rodauth/features/base.rb 659 def check_csrf? 660 scope.opts[:rodauth_route_csrf] 661 end
# File lib/rodauth/features/password_expiration.rb 30 def check_password_change_allowed 31 if password_changed_at = get_password_changed_at 32 if password_changed_at > Time.now - allow_password_change_after 33 set_redirect_error_flash password_not_changeable_yet_error_flash 34 redirect password_not_changeable_yet_redirect 35 end 36 end 37 end
# File lib/rodauth/features/session_expiration.rb 15 def check_session_expiration 16 return unless logged_in? 17 18 unless session.has_key?(session_last_activity_session_key) && session.has_key?(session_created_session_key) 19 if session_expiration_default 20 expire_session 21 end 22 23 return 24 end 25 26 time = Time.now.to_i 27 28 if session[session_last_activity_session_key] + session_inactivity_timeout < time 29 expire_session 30 end 31 set_session_value(session_last_activity_session_key, time) 32 33 if session[session_created_session_key] + max_session_lifetime < time 34 expire_session 35 end 36 end
# File lib/rodauth/features/single_session.rb 51 def check_single_session 52 if logged_in? && !currently_active_session? 53 no_longer_active_session 54 end 55 end
# File lib/rodauth/features/otp.rb 358 def clear_cached_otp 359 remove_instance_variable(:@otp) if defined?(@otp) 360 end
# File lib/rodauth/features/lockout.rb 167 def clear_invalid_login_attempts 168 unlock_account 169 end
# File lib/rodauth/features/base.rb 300 def clear_session 301 if scope.respond_to?(:clear_session) 302 scope.clear_session 303 else 304 session.clear 305 end 306 end
# File lib/rodauth/features/close_account.rb 66 def close_account 67 unless skip_status_checks? 68 update_account(account_status_column=>account_closed_status_value) 69 end 70 71 unless account_password_hash_column 72 password_hash_ds.delete 73 end 74 end
# File lib/rodauth/features/close_account.rb 62 def close_account_requires_password? 63 modifications_require_password? 64 end
Return urlsafe base64 HMAC for data, assumes hmac_secret is set.
# File lib/rodauth/features/base.rb 244 def compute_hmac(data) 245 s = [compute_raw_hmac(data)].pack('m').chomp!("=\n") 246 s.tr!('+/', '-_') 247 s 248 end
# File lib/rodauth/features/base.rb 710 def compute_raw_hmac(data) 711 raise ArgumentError, "hmac_secret not set" unless hmac_secret 712 713 OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, hmac_secret, data) 714 end
# File lib/rodauth/features/confirm_password.rb 63 def confirm_password 64 authenticated_by.delete('autologin') 65 authenticated_by.delete('remember') 66 authenticated_by.delete('email_auth') 67 authenticated_by.delete('password') 68 authenticated_by.unshift("password") 69 remove_session_value(autologin_type_session_key) 70 nil 71 end
# File lib/rodauth/features/confirm_password.rb 73 def confirm_password_redirect 74 remove_session_value(confirm_password_redirect_session_key) || default_redirect 75 end
# File lib/rodauth/features/email_base.rb 61 def convert_email_token_key(key) 62 convert_token_key(key) 63 end
# File lib/rodauth/features/base.rb 580 def convert_session_key(key) 581 key = :"#{session_key_prefix}#{key}" if session_key_prefix 582 normalize_session_or_flash_key(key) 583 end
This is needed for jdbc/sqlite, which returns timestamp columns as strings
# File lib/rodauth/features/base.rb 752 def convert_timestamp(timestamp) 753 timestamp = db.to_application_timestamp(timestamp) if timestamp.is_a?(String) 754 timestamp 755 end
# File lib/rodauth/features/base.rb 534 def convert_token_id(id) 535 if convert_token_id_to_integer? 536 convert_token_id_to_integer(id) 537 else 538 id 539 end 540 end
# File lib/rodauth/features/base.rb 542 def convert_token_id_to_integer(id) 543 if id = (Integer(id, 10) rescue nil) 544 if id > 9223372036854775807 || id < -9223372036854775808 545 # Only allow 64-bit signed integer range to avoid problems on PostgreSQL 546 id = nil 547 end 548 end 549 550 id 551 end
# File lib/rodauth/features/base.rb 522 def convert_token_key(key) 523 if key && hmac_secret 524 compute_hmac(key) 525 else 526 key 527 end 528 end
# File lib/rodauth/features/verify_account.rb 223 def create_account_autologin? 224 false 225 end
# File lib/rodauth/features/verify_account.rb 190 def create_account_notice_flash 191 verify_account_email_sent_notice_flash 192 end
# File lib/rodauth/features/verify_account.rb 227 def create_account_set_password? 228 return false if verify_account_set_password? 229 super 230 end
# File lib/rodauth/features/email_base.rb 40 def create_email(subject, body) 41 create_email_to(email_to, subject, body) 42 end
# File lib/rodauth/features/email_auth.rb 104 def create_email_auth_key 105 transaction do 106 if email_auth_key_value = get_email_auth_key(account_id) 107 set_email_auth_email_last_sent 108 @email_auth_key_value = email_auth_key_value 109 elsif e = raised_uniqueness_violation{email_auth_ds.insert(email_auth_key_insert_hash)} 110 # If inserting into the email auth table causes a violation, we can pull the 111 # existing email auth key from the table, or reraise. 112 raise e unless @email_auth_key_value = get_email_auth_key(account_id) 113 end 114 end 115 end
# File lib/rodauth/features/email_base.rb 44 def create_email_to(to, subject, body) 45 m = Mail.new 46 m.from = email_from 47 m.to = to 48 m.subject = "#{email_subject_prefix}#{subject}" 49 m.body = body 50 m 51 end
# File lib/rodauth/features/reset_password.rb 166 def create_reset_password_key 167 transaction do 168 if reset_password_key_value = get_password_reset_key(account_id) 169 set_reset_password_email_last_sent 170 @reset_password_key_value = reset_password_key_value 171 elsif e = raised_uniqueness_violation{password_reset_ds.insert(reset_password_key_insert_hash)} 172 # If inserting into the reset password table causes a violation, we can pull the 173 # existing reset password key from the table, or reraise. 174 raise e unless @reset_password_key_value = get_password_reset_key(account_id) 175 end 176 end 177 end
# File lib/rodauth/features/verify_account.rb 288 def create_verify_account_key 289 ds = verify_account_ds 290 transaction do 291 if ds.empty? 292 if e = raised_uniqueness_violation{ds.insert(verify_account_key_insert_hash)} 293 # If inserting into the verify account table causes a violation, we can pull the 294 # key from the verify account table, or reraise. 295 raise e unless @verify_account_key_value = get_verify_account_key(account_id) 296 end 297 end 298 end 299 end
# File lib/rodauth/features/verify_login_change.rb 200 def create_verify_login_change_email(login) 201 create_email_to(login, verify_login_change_email_subject, verify_login_change_email_body) 202 end
# File lib/rodauth/features/verify_login_change.rb 178 def create_verify_login_change_key(login) 179 ds = verify_login_change_ds 180 transaction do 181 ds.where((Sequel::CURRENT_TIMESTAMP > verify_login_change_deadline_column) | ~Sequel.expr(verify_login_change_login_column=>login)).delete 182 if e = raised_uniqueness_violation{ds.insert(verify_login_change_key_insert_hash(login))} 183 old_login, key = get_verify_login_change_login_and_key(account_id) 184 # If inserting into the verify login change table causes a violation, we can pull the 185 # key from the verify login change table if the logins match, or reraise. 186 @verify_login_change_key_value = if old_login.downcase == login.downcase 187 key 188 end 189 raise e unless @verify_login_change_key_value 190 end 191 end 192 end
# File lib/rodauth/features/base.rb 370 def csrf_tag(path=request.path) 371 return unless scope.respond_to?(:csrf_tag) 372 373 if use_request_specific_csrf_tokens? 374 scope.csrf_tag(path) 375 else 376 # :nocov: 377 scope.csrf_tag 378 # :nocov: 379 end 380 end
# File lib/rodauth/features/active_sessions.rb 38 def currently_active_session? 39 return false unless session_id = session[session_id_session_key] 40 41 remove_inactive_sessions 42 ds = active_sessions_ds. 43 where(active_sessions_session_id_column => compute_hmac(session_id)) 44 45 if update_current_session? 46 ds.update(active_sessions_update_hash) == 1 47 else 48 ds.count == 1 49 end 50 end
# File lib/rodauth/features/base.rb 514 def database_function_password_match?(name, hash_id, password, salt) 515 db.get(Sequel.function(function_name(name), hash_id, password_hash_using_salt(password, salt))) 516 end
# File lib/rodauth/features/base.rb 268 def db 269 Sequel::DATABASES.first or raise "Sequel database connection is missing" 270 end
# File lib/rodauth/features/close_account.rb 76 def delete_account 77 account_ds.delete 78 end
# File lib/rodauth/features/close_account.rb 80 def delete_account_on_close? 81 skip_status_checks? 82 end
# File lib/rodauth/features/remember.rb 151 def disable_remember_login 152 remove_remember_key 153 end
# File lib/rodauth/features/email_auth.rb 239 def email_auth_ds(id=account_id) 240 db[email_auth_table].where(email_auth_id_column=>id) 241 end
# File lib/rodauth/features/email_auth.rb 137 def email_auth_email_link 138 token_link(email_auth_route, email_auth_key_param, email_auth_key_value) 139 end
# File lib/rodauth/features/email_auth.rb 176 def email_auth_email_recently_sent? 177 (email_last_sent = get_email_auth_email_last_sent) && (Time.now - email_last_sent < email_auth_skip_resend_email_within) 178 end
# File lib/rodauth/features/email_auth.rb 233 def email_auth_key_insert_hash 234 hash = {email_auth_id_column=>account_id, email_auth_key_column=>email_auth_key_value} 235 set_deadline_value(hash, email_auth_deadline_column, email_auth_deadline_interval) 236 hash 237 end
# File lib/rodauth/features/email_auth.rb 147 def email_auth_request_form 148 render('email-auth-request-form') 149 end
# File lib/rodauth/features/email_base.rb 26 def email_from 27 "webmaster@#{domain}" 28 end
# File lib/rodauth/features/email_base.rb 30 def email_to 31 account[login_column] 32 end
# File lib/rodauth/features/session_expiration.rb 38 def expire_session 39 clear_session 40 set_redirect_error_status session_expiration_error_status 41 set_error_reason :session_expired 42 set_redirect_error_flash session_expiration_error_flash 43 redirect session_expiration_redirect 44 end
# File lib/rodauth/features/remember.rb 198 def extend_remember_deadline 199 active_remember_key_ds.update(remember_deadline_column=>Sequel.date_add(Sequel::CURRENT_TIMESTAMP, remember_period)) 200 remember_login 201 end
# File lib/rodauth/features/remember.rb 186 def extend_remember_deadline_while_logged_in? 187 return false unless extend_remember_deadline? 188 189 if extended_at = session[remember_deadline_extended_session_key] 190 extended_at + extend_remember_deadline_period < Time.now.to_i 191 elsif logged_in_via_remember_key? 192 # Handle existing sessions before the change to extend remember deadline 193 # while logged in. 194 true 195 end 196 end
# File lib/rodauth/features/argon2.rb 63 def extract_password_hash_cost(hash) 64 return super unless argon2_hash_algorithm?(hash ) 65 66 /\A\$argon2id\$v=\d+\$m=(\d+),t=(\d+)/ =~ hash 67 { t_cost: $2.to_i, m_cost: Math.log2($1.to_i).to_i } 68 end
# File lib/rodauth/features/base.rb 143 def features 144 self.class.features 145 end
# File lib/rodauth/features/base.rb 218 def field_attributes(field) 219 _field_attributes(field) || default_field_attributes 220 end
# File lib/rodauth/features/base.rb 175 def field_error(field) 176 return nil unless @field_errors 177 @field_errors[field] 178 end
# File lib/rodauth/features/base.rb 222 def field_error_attributes(field) 223 if field_error(field) 224 _field_error_attributes(field) 225 end 226 end
# File lib/rodauth/features/remember.rb 134 def forget_login 135 opts = Hash[remember_cookie_options] 136 opts[:path] = "/" unless opts.key?(:path) 137 ::Rack::Utils.delete_cookie_header!(response.headers, remember_cookie_key, opts) 138 end
# File lib/rodauth/features/base.rb 228 def formatted_field_error(field) 229 if error = field_error(field) 230 _formatted_field_error(field, error) 231 end 232 end
# File lib/rodauth/features/base.rb 663 def function_name(name) 664 if db.database_type == :mssql 665 # :nocov: 666 "dbo.#{name}" 667 # :nocov: 668 else 669 name 670 end 671 end
# File lib/rodauth/features/active_sessions.rb 144 def generate_active_sessions_key 145 @active_sessions_key = random_key 146 end
# File lib/rodauth/features/email_auth.rb 225 def generate_email_auth_key_value 226 @email_auth_key_value = random_key 227 end
# File lib/rodauth/features/jwt_refresh.rb 183 def generate_refresh_token 184 hash = jwt_refresh_token_insert_hash 185 [account_id, jwt_refresh_token_ds.insert(hash), convert_token_key(hash[jwt_refresh_token_key_column])].join(token_separator) 186 end
# File lib/rodauth/features/remember.rb 239 def generate_remember_key_value 240 @remember_key_value = random_key 241 end
# File lib/rodauth/features/reset_password.rb 233 def generate_reset_password_key_value 234 @reset_password_key_value = random_key 235 end
# File lib/rodauth/features/lockout.rb 268 def generate_unlock_account_key 269 random_key 270 end
# File lib/rodauth/features/verify_account.rb 284 def generate_verify_account_key_value 285 @verify_account_key_value = random_key 286 end
# File lib/rodauth/features/verify_login_change.rb 174 def generate_verify_login_change_key_value 175 @verify_login_change_key_value = random_key 176 end
# File lib/rodauth/features/jwt_refresh.rb 156 def get_active_refresh_token(account_id, token_id) 157 jwt_refresh_token_account_ds(account_id). 158 where(Sequel::CURRENT_TIMESTAMP > jwt_refresh_token_deadline_column). 159 delete 160 161 jwt_refresh_token_account_token_ds(account_id, token_id). 162 get(jwt_refresh_token_key_column) 163 end
# File lib/rodauth/features/account_expiration.rb 109 def get_activity_timestamp(account_id, column) 110 convert_timestamp(account_activity_ds(account_id).get(column)) 111 end
# File lib/rodauth/features/email_auth.rb 121 def get_email_auth_email_last_sent 122 if column = email_auth_email_last_sent_column 123 if ts = email_auth_ds.get(column) 124 convert_timestamp(ts) 125 end 126 end 127 end
# File lib/rodauth/features/email_auth.rb 141 def get_email_auth_key(id) 142 ds = email_auth_ds(id) 143 ds.where(Sequel::CURRENT_TIMESTAMP > email_auth_deadline_column).delete 144 ds.get(email_auth_key_column) 145 end
# File lib/rodauth/features/password_expiration.rb 26 def get_password_changed_at 27 convert_timestamp(password_expiration_ds.get(password_expiration_changed_at_column)) 28 end
Get the password hash for the user. When using database authentication functions, note that only the salt is returned.
# File lib/rodauth/features/base.rb 685 def get_password_hash 686 if account_password_hash_column 687 account![account_password_hash_column] 688 elsif use_database_authentication_functions? 689 db.get(Sequel.function(function_name(:rodauth_get_salt), account ? account_id : session_value)) 690 else 691 # :nocov: 692 password_hash_ds.get(password_hash_column) 693 # :nocov: 694 end 695 end
# File lib/rodauth/features/reset_password.rb 191 def get_password_reset_key(id) 192 ds = password_reset_ds(id) 193 ds.where(Sequel::CURRENT_TIMESTAMP > reset_password_deadline_column).delete 194 ds.get(reset_password_key_column) 195 end
# File lib/rodauth/features/remember.rb 140 def get_remember_key 141 unless @remember_key_value = active_remember_key_ds.get(remember_key_column) 142 generate_remember_key_value 143 transaction do 144 remove_remember_key 145 add_remember_key 146 end 147 end 148 nil 149 end
# File lib/rodauth/features/reset_password.rb 201 def get_reset_password_email_last_sent 202 if column = reset_password_email_last_sent_column 203 if ts = password_reset_ds.get(column) 204 convert_timestamp(ts) 205 end 206 end 207 end
# File lib/rodauth/features/lockout.rb 230 def get_unlock_account_email_last_sent 231 if column = account_lockouts_email_last_sent_column 232 if ts = account_lockouts_ds.get(column) 233 convert_timestamp(ts) 234 end 235 end 236 end
# File lib/rodauth/features/lockout.rb 218 def get_unlock_account_key 219 account_lockouts_ds.get(account_lockouts_key_column) 220 end
# File lib/rodauth/features/verify_account.rb 236 def get_verify_account_email_last_sent 237 if column = verify_account_email_last_sent_column 238 if ts = verify_account_ds.get(column) 239 convert_timestamp(ts) 240 end 241 end 242 end
# File lib/rodauth/features/verify_account.rb 215 def get_verify_account_key(id) 216 verify_account_ds(id).get(verify_account_key_column) 217 end
# File lib/rodauth/features/verify_login_change.rb 134 def get_verify_login_change_login_and_key(id) 135 verify_login_change_ds(id).get([verify_login_change_login_column, verify_login_change_key_column]) 136 end
# File lib/rodauth/features/active_sessions.rb 75 def handle_duplicate_active_session_id(_e) 76 # Do nothing by default as session is already tracked. This will result in 77 # the current session and the existing session with the same id 78 # being tracked together, so that a logout of one will logout 79 # the other, and updating the last use on one will update the other, 80 # but this should be acceptable. However, this can be overridden if different 81 # behavior is desired. 82 end
# File lib/rodauth/features/webauthn.rb 347 def handle_webauthn_sign_count_verification_error 348 throw_error_reason(:invalid_webauthn_sign_count, invalid_field_error_status, webauthn_auth_param, webauthn_invalid_sign_count_message) 349 end
# File lib/rodauth/features/base.rb 673 def has_password? 674 return @has_password if defined?(@has_password) 675 return false unless account || session_value 676 @has_password = !!get_password_hash 677 end
# File lib/rodauth/features/audit_logging.rb 31 def hook_action(hook_type, action) 32 super 33 # In after_logout, session is already cleared, so use before_logout in that case 34 if (hook_type == :after || action == :logout) && (id = account ? account_id : session_value) 35 add_audit_log(id, action) 36 end 37 end
# File lib/rodauth/features/http_basic_auth.rb 34 def http_basic_auth 35 return @checked_http_basic_auth if defined?(@checked_http_basic_auth) 36 37 @checked_http_basic_auth = nil 38 return unless token = ((v = request.env['HTTP_AUTHORIZATION']) && v[/\A *Basic (.*)\Z/, 1]) 39 40 username, password = token.unpack("m*").first.split(/:/, 2) 41 return unless username && password 42 43 catch_error do 44 unless account_from_login(username) 45 throw_basic_auth_error(login_param, no_matching_login_message) 46 end 47 48 before_login_attempt 49 50 unless open_account? 51 throw_basic_auth_error(login_param, no_matching_login_message) 52 end 53 54 unless password_match?(password) 55 after_login_failure 56 throw_basic_auth_error(password_param, invalid_password_message) 57 end 58 59 transaction do 60 before_login 61 login_session('password') 62 after_login 63 end 64 65 @checked_http_basic_auth = true 66 return true 67 end 68 69 nil 70 end
# File lib/rodauth/features/active_sessions.rb 168 def inactive_session_cond 169 cond = session_inactivity_deadline_condition 170 cond2 = session_lifetime_deadline_condition 171 return false unless cond || cond2 172 Sequel.|(*[cond, cond2].compact) 173 end
# File lib/rodauth/features/json.rb 180 def include_success_messages? 181 !json_response_success_key.nil? 182 end
# File lib/rodauth/features/base.rb 186 def input_field_string(param, id, opts={}) 187 type = opts.fetch(:type, "text") 188 189 unless type == "password" 190 value = opts.fetch(:value){scope.h param(param)} 191 end 192 193 field_class = opts.fetch(:class, "form-control") 194 195 if autocomplete_for_field?(param) && opts[:autocomplete] 196 autocomplete = "autocomplete=\"#{opts[:autocomplete]}\"" 197 end 198 199 if inputmode_for_field?(param) && opts[:inputmode] 200 inputmode = "inputmode=\"#{opts[:inputmode]}\"" 201 end 202 203 if mark_input_fields_as_required? && opts[:required] != false 204 required = "required=\"required\"" 205 end 206 207 "<input #{opts[:attr]} #{autocomplete} #{inputmode} #{required} #{field_attributes(param)} #{field_error_attributes(param)} type=\"#{type}\" class=\"#{field_class}#{add_field_error_class(param)}\" name=\"#{param}\" id=\"#{id}\" value=\"#{value}\"/> #{formatted_field_error(param) unless opts[:skip_error_message]}" 208 end
# File lib/rodauth/features/base.rb 214 def inputmode_for_field?(_param) 215 mark_input_fields_with_inputmode? 216 end
# File lib/rodauth/features/base.rb 817 def internal_request? 818 false 819 end
# File lib/rodauth/features/internal_request.rb 329 def internal_request_configuration(&block) 330 @auth.instance_exec do 331 (@internal_request_configuration_blocks ||= []) << block 332 end 333 end
# File lib/rodauth/features/lockout.rb 177 def invalid_login_attempted 178 ds = account_login_failures_ds. 179 where(account_login_failures_id_column=>account_id) 180 181 number = if db.database_type == :postgres 182 ds.returning(account_login_failures_number_column). 183 with_sql(:update_sql, account_login_failures_number_column=>Sequel.expr(account_login_failures_number_column)+1). 184 single_value 185 else 186 # :nocov: 187 if ds.update(account_login_failures_number_column=>Sequel.expr(account_login_failures_number_column)+1) > 0 188 ds.get(account_login_failures_number_column) 189 end 190 # :nocov: 191 end 192 193 unless number 194 # Ignoring the violation is safe here. It may allow slightly more than max_invalid_logins invalid logins before 195 # lockout, but allowing a few extra is OK if the race is lost. 196 ignore_uniqueness_violation{account_login_failures_ds.insert(account_login_failures_id_column=>account_id)} 197 number = 1 198 end 199 200 if number >= max_invalid_logins 201 @unlock_account_key_value = generate_unlock_account_key 202 hash = _setup_account_lockouts_hash(account_id, unlock_account_key_value) 203 204 if e = raised_uniqueness_violation{account_lockouts_ds.insert(hash)} 205 # If inserting into the lockout table raises a violation, we should just be able to pull the already inserted 206 # key out of it. If that doesn't return a valid key, we should reraise the error. 207 raise e unless @unlock_account_key_value = account_lockouts_ds.get(account_lockouts_key_column) 208 209 after_account_lockout 210 show_lockout_page 211 else 212 after_account_lockout 213 e 214 end 215 end 216 end
# File lib/rodauth/features/change_password.rb 72 def invalid_previous_password_message 73 invalid_password_message 74 end
# File lib/rodauth/features/json.rb 54 def json_request? 55 return @json_request if defined?(@json_request) 56 @json_request = request.content_type =~ json_request_content_type_regexp 57 end
# File lib/rodauth/features/json.rb 188 def json_response 189 @json_response ||= {} 190 end
# File lib/rodauth/features/jwt_cors.rb 15 def jwt_cors_allow? 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 end 30 end
# File lib/rodauth/features/jwt.rb 114 def jwt_payload 115 return @jwt_payload if defined?(@jwt_payload) 116 @jwt_payload = JWT.decode(jwt_token, jwt_secret, true, _jwt_decode_opts.merge(:algorithm=>jwt_algorithm))[0] 117 rescue JWT::DecodeError => e 118 rescue_jwt_payload(e) 119 end
# File lib/rodauth/features/jwt_refresh.rb 165 def jwt_refresh_token_account_ds(account_id) 166 jwt_refresh_token_ds.where(jwt_refresh_token_account_id_column => account_id) 167 end
# File lib/rodauth/features/jwt_refresh.rb 169 def jwt_refresh_token_account_token_ds(account_id, token_id) 170 jwt_refresh_token_account_ds(account_id). 171 where(jwt_refresh_token_id_column=>token_id) 172 end
# File lib/rodauth/features/jwt_refresh.rb 174 def jwt_refresh_token_ds 175 db[jwt_refresh_token_table] 176 end
# File lib/rodauth/features/jwt_refresh.rb 188 def jwt_refresh_token_insert_hash 189 hash = {jwt_refresh_token_account_id_column => account_id, jwt_refresh_token_key_column => random_key} 190 set_deadline_value(hash, jwt_refresh_token_deadline_column, jwt_refresh_token_deadline_interval) 191 hash 192 end
# File lib/rodauth/features/jwt_refresh.rb 147 def jwt_refresh_token_match?(key) 148 # We don't need to match tokens if we are requiring a valid current access token 149 return true unless allow_refresh_with_expired_jwt_access_token? 150 151 # If allowing with expired jwt access token, check the expired session contains 152 # hmac matching submitted and active refresh token. 153 timing_safe_eql?(compute_hmac(session[jwt_refresh_token_data_session_key].to_s + key), session[jwt_refresh_token_hmac_session_key].to_s) 154 end
# File lib/rodauth/features/jwt.rb 67 def jwt_secret 68 raise ArgumentError, "jwt_secret not set" 69 end
# File lib/rodauth/features/jwt.rb 71 def jwt_session_hash 72 jwt_session_key ? {jwt_session_key=>session} : session 73 end
# File lib/rodauth/features/jwt.rb 79 def jwt_token 80 return @jwt_token if defined?(@jwt_token) 81 82 if (v = request.env['HTTP_AUTHORIZATION']) && v !~ jwt_authorization_ignore 83 @jwt_token = v.sub(jwt_authorization_remove, '') 84 end 85 end
# File lib/rodauth/features/account_expiration.rb 27 def last_account_activity_at 28 get_activity_timestamp(session_value, account_activity_last_activity_column) 29 end
# File lib/rodauth/features/account_expiration.rb 31 def last_account_login_at 32 get_activity_timestamp(session_value, account_activity_last_login_column) 33 end
# File lib/rodauth/features/remember.rb 114 def load_memory 115 if logged_in? 116 if extend_remember_deadline_while_logged_in? 117 account_from_session 118 extend_remember_deadline 119 end 120 elsif account_from_remember_cookie 121 before_load_memory 122 login_session('remember') 123 extend_remember_deadline if extend_remember_deadline? 124 after_load_memory 125 end 126 end
# File lib/rodauth/features/base.rb 757 def loaded_templates 758 [] 759 end
# File lib/rodauth/features/lockout.rb 148 def locked_out? 149 if t = convert_timestamp(account_lockouts_ds.get(account_lockouts_deadline_column)) 150 if Time.now < t 151 true 152 else 153 unlock_account 154 false 155 end 156 else 157 false 158 end 159 end
# File lib/rodauth/features/remember.rb 170 def logged_in_via_remember_key? 171 authenticated_by.include?('remember') 172 end
# File lib/rodauth/features/login.rb 81 def login(auth_type) 82 saved_login_redirect = remove_session_value(login_redirect_session_key) 83 transaction do 84 before_login 85 login_session(auth_type) 86 yield if block_given? 87 after_login 88 end 89 set_notice_flash login_notice_flash 90 redirect(saved_login_redirect || login_redirect) 91 end
# File lib/rodauth/features/login_password_requirements_base.rb 46 def login_confirm_label 47 "Confirm #{login_label}" 48 end
# File lib/rodauth/features/login_password_requirements_base.rb 108 def login_does_not_meet_requirements_message 109 "invalid login#{", #{login_requirement_message}" if login_requirement_message}" 110 end
# File lib/rodauth/features/reset_password.rb 237 def login_failed_reset_password_request_form 238 render("reset-password-request") 239 end
# File lib/rodauth/features/base.rb 292 def login_input_type 293 login_uses_email? ? 'email' : 'text' 294 end
# File lib/rodauth/features/login_password_requirements_base.rb 144 def login_meets_email_requirements?(login) 145 return true unless require_email_address_logins? 146 return true if login_valid_email?(login) 147 set_login_requirement_error_message(:login_not_valid_email, login_not_valid_email_message) 148 return false 149 end
# File lib/rodauth/features/login_password_requirements_base.rb 129 def login_meets_length_requirements?(login) 130 if login_minimum_length > login.length 131 set_login_requirement_error_message(:login_too_short, login_too_short_message) 132 false 133 elsif login_maximum_length < login.length 134 set_login_requirement_error_message(:login_too_long, login_too_long_message) 135 false 136 elsif login_maximum_bytes < login.bytesize 137 set_login_requirement_error_message(:login_too_many_bytes, login_too_many_bytes_message) 138 false 139 else 140 true 141 end 142 end
# File lib/rodauth/features/login_password_requirements_base.rb 54 def login_meets_requirements?(login) 55 login_meets_length_requirements?(login) && \ 56 login_meets_email_requirements?(login) 57 end
# File lib/rodauth/features/base.rb 308 def login_required 309 set_redirect_error_status(login_required_error_status) 310 set_error_reason :login_required 311 set_redirect_error_flash require_login_error_flash 312 redirect require_login_redirect 313 end
# File lib/rodauth/features/login.rb 100 def login_return_to_requested_location_path 101 request.fullpath if request.get? 102 end
# File lib/rodauth/features/base.rb 444 def login_session(auth_type) 445 update_session 446 set_session_value(authenticated_by_session_key, [auth_type]) 447 end
# File lib/rodauth/features/login_password_requirements_base.rb 112 def login_too_long_message 113 "maximum #{login_maximum_length} characters" 114 end
# File lib/rodauth/features/login_password_requirements_base.rb 116 def login_too_many_bytes_message 117 "maximum #{login_maximum_bytes} bytes" 118 end
# File lib/rodauth/features/login_password_requirements_base.rb 120 def login_too_short_message 121 "minimum #{login_minimum_length} characters" 122 end
# File lib/rodauth/features/base.rb 296 def login_uses_email? 297 login_column == :email 298 end
# File lib/rodauth/features/login_password_requirements_base.rb 151 def login_valid_email?(login) 152 login =~ login_email_regexp 153 end
# File lib/rodauth/features/active_sessions.rb 104 def logout_additional_form_tags 105 super.to_s + render('global-logout-field') 106 end
# File lib/rodauth/features/base.rb 500 def modifications_require_password? 501 has_password? 502 end
# File lib/rodauth/features/create_account.rb 96 def new_account(login) 97 @account = _new_account(login) 98 end
# File lib/rodauth/features/recovery_codes.rb 240 def new_recovery_code 241 random_key 242 end
# File lib/rodauth/features/webauthn.rb 296 def new_webauthn_credential 297 WebAuthn::Credential.options_for_create( 298 :timeout => webauthn_setup_timeout, 299 :rp => {:name=>webauthn_rp_name, :id=>webauthn_rp_id}, 300 :user => {:id=>account_webauthn_user_id, :name=>webauthn_user_name}, 301 :authenticator_selection => webauthn_authenticator_selection, 302 :attestation => webauthn_attestation, 303 :extensions => webauthn_extensions, 304 :exclude => account_webauthn_ids, 305 ) 306 end
# File lib/rodauth/features/active_sessions.rb 58 def no_longer_active_session 59 clear_session 60 set_redirect_error_status inactive_session_error_status 61 set_error_reason :inactive_session 62 set_redirect_error_flash active_sessions_error_flash 63 redirect active_sessions_redirect 64 end
# File lib/rodauth/features/base.rb 585 def normalize_session_or_flash_key(key) 586 scope.opts[:sessions_convert_symbols] ? key.to_s : key 587 end
Return nil by default for values with null bytes
# File lib/rodauth/features/base.rb 482 def null_byte_parameter_value(key, value) 483 nil 484 end
# File lib/rodauth/features/base.rb 403 def only_json? 404 scope.class.opts[:rodauth_json] == :only 405 end
# File lib/rodauth/features/base.rb 264 def open_account? 265 skip_status_checks? || account[account_status_column] == account_open_status_value 266 end
# File lib/rodauth/features/otp.rb 271 def otp_add_key 272 _otp_add_key(otp_key) 273 super if defined?(super) 274 end
# File lib/rodauth/features/otp.rb 242 def otp_available? 243 otp_exists? && !otp_locked_out? 244 end
# File lib/rodauth/features/otp.rb 246 def otp_exists? 247 !otp_key.nil? 248 end
# File lib/rodauth/features/otp.rb 367 def otp_hmac_secret(key) 368 base32_encode(compute_raw_hmac(ROTP::Base32.decode(key)), key.bytesize) 369 end
# File lib/rodauth/features/otp.rb 418 def otp_key_ds 419 db[otp_keys_table].where(otp_keys_id_column=>session_value) 420 end
# File lib/rodauth/features/otp.rb 323 def otp_keys_use_hmac? 324 !!hmac_secret 325 end
# File lib/rodauth/features/otp.rb 282 def otp_last_use 283 convert_timestamp(otp_key_ds.get(otp_keys_last_use_column)) 284 end
# File lib/rodauth/features/otp.rb 294 def otp_locked_out? 295 otp_key_ds.get(otp_keys_failures_column) >= otp_auth_failures_limit 296 end
# File lib/rodauth/features/otp.rb 381 def otp_new_secret 382 ROTP::Base32.random_base32.downcase 383 end
# File lib/rodauth/features/otp.rb 306 def otp_provisioning_name 307 account[login_column] 308 end
# File lib/rodauth/features/otp.rb 298 def otp_provisioning_uri 299 otp.provisioning_uri(otp_provisioning_name) 300 end
# File lib/rodauth/features/otp.rb 310 def otp_qr_code 311 svg = RQRCode::QRCode.new(otp_provisioning_uri).as_svg(:module_size=>8, :viewbox=>true, :use_path=>true, :fill=>"fff") 312 svg.sub(/\A<\?xml version="1\.0" standalone="yes"\?>/, '') 313 end
# File lib/rodauth/features/otp.rb 286 def otp_record_authentication_failure 287 otp_key_ds.update(otp_keys_failures_column=>Sequel.identifier(otp_keys_failures_column) + 1) 288 end
# File lib/rodauth/features/otp.rb 266 def otp_remove 267 otp_key_ds.delete 268 @otp_key = nil 269 end
# File lib/rodauth/features/otp.rb 290 def otp_remove_auth_failures 291 otp_key_ds.update(otp_keys_failures_column=>0) 292 end
# File lib/rodauth/features/otp.rb 362 def otp_tmp_key(secret) 363 _otp_tmp_key(secret) 364 clear_cached_otp 365 end
# File lib/rodauth/features/otp.rb 276 def otp_update_last_use 277 otp_key_ds. 278 where(Sequel.date_add(otp_keys_last_use_column, :seconds=>(otp_interval||30)) < Sequel::CURRENT_TIMESTAMP). 279 update(otp_keys_last_use_column=>Sequel::CURRENT_TIMESTAMP) == 1 280 end
# File lib/rodauth/features/otp.rb 315 def otp_user_key 316 @otp_user_key ||= if otp_keys_use_hmac? 317 otp_hmac_secret(otp_key) 318 else 319 otp_key 320 end 321 end
# File lib/rodauth/features/otp.rb 250 def otp_valid_code?(ot_pass) 251 return false unless otp_exists? 252 ot_pass = ot_pass.gsub(/\s+/, '') 253 if drift = otp_drift 254 if otp.respond_to?(:verify_with_drift) 255 # :nocov: 256 otp.verify_with_drift(ot_pass, drift) 257 # :nocov: 258 else 259 otp.verify(ot_pass, :drift_behind=>drift, :drift_ahead=>drift) 260 end 261 else 262 otp.verify(ot_pass) 263 end 264 end
# File lib/rodauth/features/otp.rb 371 def otp_valid_key?(secret) 372 return false unless secret =~ /\A([a-z2-7]{16}|[a-z2-7]{32})\z/ 373 if otp_keys_use_hmac? 374 timing_safe_eql?(otp_hmac_secret(param(otp_setup_raw_param)), secret) 375 else 376 true 377 end 378 end
Return nil by default for values over maximum bytesize.
# File lib/rodauth/features/base.rb 477 def over_max_bytesize_param_value(key, value) 478 nil 479 end
Return a string for the parameter name. This will be an empty string if the parameter doesn’t exist.
# File lib/rodauth/features/base.rb 460 def param(key) 461 param_or_nil(key).to_s 462 end
Return a string for the parameter name, or nil if there is no parameter with that name.
# File lib/rodauth/features/base.rb 466 def param_or_nil(key) 467 value = raw_param(key) 468 unless value.nil? 469 value = value.to_s 470 value = over_max_bytesize_param_value(key, value) if max_param_bytesize && value.bytesize > max_param_bytesize 471 value = null_byte_parameter_value(key, value) if value && value.include?("\0") 472 end 473 value 474 end
# File lib/rodauth/features/login_password_requirements_base.rb 50 def password_confirm_label 51 "Confirm #{password_label}" 52 end
# File lib/rodauth/features/login_password_requirements_base.rb 170 def password_does_not_contain_null_byte?(password) 171 return true unless password.include?("\0") 172 set_password_requirement_error_message(:password_contains_null_byte, contains_null_byte_message) 173 false 174 end
# File lib/rodauth/features/login_password_requirements_base.rb 87 def password_does_not_meet_requirements_message 88 "invalid password, does not meet requirements#{" (#{password_requirement_message})" if password_requirement_message}" 89 end
# File lib/rodauth/features/disallow_password_reuse.rb 50 def password_doesnt_match_previous_password?(password) 51 match = if use_database_authentication_functions? 52 salts = previous_password_ds. 53 select_map([previous_password_id_column, Sequel.function(function_name(:rodauth_get_previous_salt), previous_password_id_column).as(:salt)]) 54 return true if salts.empty? 55 56 salts.any? do |hash_id, salt| 57 database_function_password_match?(:rodauth_previous_password_hash_match, hash_id, password, salt) 58 end 59 else 60 # :nocov: 61 previous_password_ds.select_map(previous_password_hash_column).any? do |hash| 62 password_hash_match?(hash, password) 63 end 64 # :nocov: 65 end 66 67 return true unless match 68 set_password_requirement_error_message(:password_same_as_previous_password, password_same_as_previous_password_message) 69 false 70 end
# File lib/rodauth/features/password_expiration.rb 107 def password_expiration_ds 108 db[password_expiration_table].where(password_expiration_id_column=>account_id) 109 end
# File lib/rodauth/features/password_expiration.rb 68 def password_expired? 69 if password_changed_at = session[password_changed_at_session_key] 70 return password_changed_at + require_password_change_after < Time.now.to_i 71 end 72 73 account_from_session 74 if password_changed_at = get_password_changed_at 75 set_session_value(password_changed_at_session_key, password_changed_at.to_i) 76 password_changed_at + require_password_change_after < Time.now 77 else 78 set_session_value(password_changed_at_session_key, password_expiration_default ? 0 : 2147483647) 79 password_expiration_default 80 end 81 end
# File lib/rodauth/features/base.rb 272 def password_field_autocomplete_value 273 @password_field_autocomplete_value || 'current-password' 274 end
# File lib/rodauth/features/password_complexity.rb 54 def password_has_enough_character_groups?(password) 55 return true if password.length > password_max_length_for_groups_check 56 return true if password_character_groups.select{|re| password =~ re}.length >= password_min_groups 57 set_password_requirement_error_message(:not_enough_character_groups_in_password, password_not_enough_character_groups_message) 58 false 59 end
# File lib/rodauth/features/password_complexity.rb 61 def password_has_no_invalid_pattern?(password) 62 return true unless password_invalid_pattern 63 return true if password !~ password_invalid_pattern 64 set_password_requirement_error_message(:invalid_password_pattern, password_invalid_pattern_message) 65 false 66 end
# File lib/rodauth/features/argon2.rb 18 def password_hash(password) 19 return super unless use_argon2? 20 21 if secret = argon2_secret 22 argon2_params = Hash[password_hash_cost] 23 argon2_params[:secret] = secret 24 else 25 argon2_params = password_hash_cost 26 end 27 ::Argon2::Password.new(argon2_params).create(password) 28 end
# File lib/rodauth/features/argon2.rb 44 def password_hash_cost 45 return super unless use_argon2? 46 argon2_hash_cost 47 end
# File lib/rodauth/features/base.rb 747 def password_hash_ds 748 db[password_hash_table].where(password_hash_id_column=>account ? account_id : session_value) 749 end
# File lib/rodauth/features/argon2.rb 49 def password_hash_match?(hash, password) 50 return super unless argon2_hash_algorithm?(hash) 51 argon2_password_hash_match?(hash, password) 52 end
# File lib/rodauth/features/argon2.rb 54 def password_hash_using_salt(password, salt) 55 return super unless argon2_hash_algorithm?(salt) 56 57 argon2_params = Hash[extract_password_hash_cost(salt)] 58 argon2_params[argon2_salt_option] = Base64.decode64(salt.split('$').last) 59 argon2_params[:secret] = argon2_secret 60 ::Argon2::Password.new(argon2_params).create(password) 61 end
# File lib/rodauth/features/base.rb 425 def password_match?(password) 426 if hash = get_password_hash 427 if account_password_hash_column || !use_database_authentication_functions? 428 password_hash_match?(hash, password) 429 else 430 database_function_password_match?(:rodauth_valid_password_hash, account_id, password, hash) 431 end 432 end 433 end
# File lib/rodauth/features/login_password_requirements_base.rb 155 def password_meets_length_requirements?(password) 156 if password_minimum_length > password.length 157 set_password_requirement_error_message(:password_too_short, password_too_short_message) 158 false 159 elsif password_maximum_length && password_maximum_length < password.length 160 set_password_requirement_error_message(:password_too_long, password_too_long_message) 161 false 162 elsif password_maximum_bytes && password_maximum_bytes < password.bytesize 163 set_password_requirement_error_message(:password_too_many_bytes, password_too_many_bytes_message) 164 false 165 else 166 true 167 end 168 end
# File lib/rodauth/features/disallow_common_passwords.rb 13 def password_meets_requirements?(password) 14 super && password_not_one_of_the_most_common?(password) 15 end
# File lib/rodauth/features/password_complexity.rb 75 def password_not_in_dictionary?(password) 76 return true unless dict = password_dictionary 77 return true unless password =~ /\A(?:\d*)([A-Za-z!@$+|][A-Za-z!@$+|0134578]+[A-Za-z!@$+|])(?:\d*)\z/ 78 word = $1.downcase.tr('!@$+|0134578', 'iastloleastb') 79 return true if !dict.include?(word) 80 set_password_requirement_error_message(:password_in_dictionary, password_in_dictionary_message) 81 false 82 end
# File lib/rodauth/features/disallow_common_passwords.rb 33 def password_not_one_of_the_most_common?(password) 34 return true unless password_one_of_most_common?(password) 35 set_password_requirement_error_message(:password_is_one_of_the_most_common, password_is_one_of_the_most_common_message) 36 false 37 end
# File lib/rodauth/features/password_complexity.rb 68 def password_not_too_many_repeating_characters?(password) 69 return true if password_max_repeating_characters < 2 70 return true if password !~ /(.)(\1){#{password_max_repeating_characters-1}}/ 71 set_password_requirement_error_message(:too_many_repeating_characters_in_password, password_too_many_repeating_characters_message) 72 false 73 end
# File lib/rodauth/features/disallow_common_passwords.rb 27 def password_one_of_most_common?(password) 28 most_common_passwords.include?(password) 29 end
# File lib/rodauth/features/password_grace_period.rb 22 def password_recently_entered? 23 return false unless last_password_entry = session[last_password_entry_session_key] 24 last_password_entry + password_grace_period > Time.now.to_i 25 end
# File lib/rodauth/features/reset_password.rb 251 def password_reset_ds(id=account_id) 252 db[reset_password_table].where(reset_password_id_column=>id) 253 end
# File lib/rodauth/features/login_password_requirements_base.rb 91 def password_too_long_message 92 "maximum #{password_maximum_length} characters" 93 end
# File lib/rodauth/features/login_password_requirements_base.rb 95 def password_too_many_bytes_message 96 "maximum #{password_maximum_bytes} bytes" 97 end
# File lib/rodauth/features/login_password_requirements_base.rb 99 def password_too_short_message 100 "minimum #{password_minimum_length} characters" 101 end
# File lib/rodauth/features/base.rb 504 def possible_authentication_methods 505 has_password? ? ['password'] : [] 506 end
# File lib/rodauth/features/base.rb 407 def post_configure 408 require 'bcrypt' if require_bcrypt? 409 db.extension :date_arithmetic if use_date_arithmetic? 410 411 if convert_token_id_to_integer?.nil? && (db rescue false) && db.table_exists?(accounts_table) && db.schema(accounts_table).find{|col, v| break v[:type] == :integer if col == account_id_column} 412 self.class.send(:define_method, :convert_token_id_to_integer?){true} 413 end 414 415 route_hash= {} 416 self.class.routes.each do |meth| 417 route_meth = "#{meth.to_s.sub(/\Ahandle_/, '')}_route" 418 if route = send(route_meth) 419 route_hash["/#{route}"] = meth 420 end 421 end 422 self.class.route_hash = route_hash.freeze 423 end
# File lib/rodauth/features/disallow_password_reuse.rb 94 def previous_password_ds 95 db[previous_password_hash_table].where(previous_password_account_id_column=>account_id) 96 end
In cases where retrying on uniqueness violations cannot work, this will detect whether a uniqueness violation is raised by the block and return the exception if so. This method should be used if you don’t care about the exception itself.
# File lib/rodauth/features/base.rb 776 def raises_uniqueness_violation?(&block) 777 transaction(:savepoint=>:only, &block) 778 false 779 rescue unique_constraint_violation_class => e 780 e 781 end
# File lib/rodauth/features/base.rb 576 def random_key 577 SecureRandom.urlsafe_base64(32) 578 end
# File lib/rodauth/features/base.rb 486 def raw_param(key) 487 request.params[key] 488 end
# File lib/rodauth/features/recovery_codes.rb 160 def recovery_code_match?(code) 161 recovery_codes.each do |s| 162 if timing_safe_eql?(code, s) 163 recovery_codes_ds.where(recovery_codes_column=>code).delete 164 if recovery_codes_primary? 165 add_recovery_code 166 end 167 return true 168 end 169 end 170 false 171 end
# File lib/rodauth/features/recovery_codes.rb 196 def recovery_codes_available? 197 !recovery_codes_ds.empty? 198 end
# File lib/rodauth/features/recovery_codes.rb 264 def recovery_codes_ds 265 db[recovery_codes_table].where(recovery_codes_id_column=>session_value) 266 end
# File lib/rodauth/features/recovery_codes.rb 244 def recovery_codes_primary? 245 (features & [:otp, :sms_codes, :webauthn]).empty? 246 end
# File lib/rodauth/features/recovery_codes.rb 156 def recovery_codes_remove 157 recovery_codes_ds.delete 158 end
# File lib/rodauth/features/base.rb 553 def redirect(path) 554 request.redirect(path) 555 end
# File lib/rodauth/features/two_factor_base.rb 254 def redirect_two_factor_authenticated 255 saved_two_factor_auth_redirect = remove_session_value(two_factor_auth_redirect_session_key) 256 redirect saved_two_factor_auth_redirect || two_factor_auth_redirect 257 end
# File lib/rodauth/features/remember.rb 247 def remember_key_ds(id=account_id) 248 db[remember_table].where(remember_id_column=>id) 249 end
# File lib/rodauth/features/remember.rb 128 def remember_login 129 get_remember_key 130 set_remember_cookie 131 set_session_value(remember_deadline_extended_session_key, Time.now.to_i) if extend_remember_deadline? 132 end
# File lib/rodauth/features/remember.rb 91 def remembered_session_id 92 return unless cookie = _get_remember_cookie 93 id, key = cookie.split('_', 2) 94 return unless id && key 95 96 actual, deadline = active_remember_key_ds(id).get([remember_key_column, remember_deadline_column]) 97 return unless actual 98 99 if hmac_secret 100 unless valid = timing_safe_eql?(key, compute_hmac(actual)) 101 unless raw_remember_token_deadline && raw_remember_token_deadline > convert_timestamp(deadline) 102 return 103 end 104 end 105 end 106 107 unless valid || timing_safe_eql?(key, actual) 108 return 109 end 110 111 id 112 end
# File lib/rodauth/features/active_sessions.rb 90 def remove_active_session(session_id) 91 active_sessions_ds.where(active_sessions_session_id_column=>session_id).delete 92 end
# File lib/rodauth/features/active_sessions.rb 94 def remove_all_active_sessions 95 active_sessions_ds.delete 96 end
# File lib/rodauth/features/webauthn.rb 381 def remove_all_webauthn_keys_and_user_ids 382 webauthn_user_ids_ds.delete 383 webauthn_keys_ds.delete 384 end
# File lib/rodauth/features/active_sessions.rb 84 def remove_current_session 85 if session_id = session[session_id_session_key] 86 remove_active_session(compute_hmac(session_id)) 87 end 88 end
# File lib/rodauth/features/email_auth.rb 129 def remove_email_auth_key 130 email_auth_ds.delete 131 end
# File lib/rodauth/features/active_sessions.rb 98 def remove_inactive_sessions 99 if cond = inactive_session_cond 100 active_sessions_ds.where(cond).delete 101 end 102 end
# File lib/rodauth/features/jwt_refresh.rb 178 def remove_jwt_refresh_token_key(token) 179 account_id, token_id, _ = _account_refresh_token_split(token) 180 jwt_refresh_token_account_token_ds(account_id, token_id).delete 181 end
# File lib/rodauth/features/lockout.rb 272 def remove_lockout_metadata 273 account_login_failures_ds.delete 274 account_lockouts_ds.delete 275 end
# File lib/rodauth/features/remember.rb 166 def remove_remember_key(id=account_id) 167 remember_key_ds(id).delete 168 end
# File lib/rodauth/features/reset_password.rb 179 def remove_reset_password_key 180 password_reset_ds.delete 181 end
# File lib/rodauth/features/base.rb 825 def remove_session_value(key) 826 session.delete(key) 827 end
# File lib/rodauth/features/verify_account.rb 174 def remove_verify_account_key 175 verify_account_ds.delete 176 end
# File lib/rodauth/features/verify_login_change.rb 110 def remove_verify_login_change_key 111 verify_login_change_ds.delete 112 end
# File lib/rodauth/features/webauthn.rb 377 def remove_webauthn_key(webauthn_id) 378 webauthn_keys_ds.where(webauthn_keys_webauthn_id_column=>webauthn_id).delete == 1 379 end
# File lib/rodauth/features/base.rb 399 def render(page) 400 _view(:render, page) 401 end
# File lib/rodauth/features/login.rb 133 def render_multi_phase_login_forms 134 multi_phase_login_forms.sort.map{|_, form, _| form}.join("\n") 135 end
# File lib/rodauth/features/base.rb 147 def request 148 scope.request 149 end
# File lib/rodauth/features/base.rb 349 def require_account 350 require_authentication 351 require_account_session 352 end
# File lib/rodauth/features/base.rb 594 def require_account_session 595 unless account_from_session 596 clear_session 597 login_required 598 end 599 end
# File lib/rodauth/features/base.rb 345 def require_authentication 346 require_login 347 end
# File lib/rodauth/features/password_expiration.rb 61 def require_current_password 62 if authenticated? && password_expired? && password_change_needed_redirect != request.path_info 63 set_redirect_error_flash password_expiration_error_flash 64 redirect password_change_needed_redirect 65 end 66 end
# File lib/rodauth/features/http_basic_auth.rb 27 def require_http_basic_auth 28 unless http_basic_auth 29 set_http_basic_auth_error_response 30 return_response 31 end 32 end
# File lib/rodauth/features/base.rb 337 def require_login 338 login_required unless logged_in? 339 end
# File lib/rodauth/features/verify_account.rb 166 def require_login_confirmation? 167 false 168 end
# File lib/rodauth/features/otp.rb 233 def require_otp_setup 234 unless otp_exists? 235 set_redirect_error_status(two_factor_not_setup_error_status) 236 set_error_reason :two_factor_not_setup 237 set_redirect_error_flash two_factor_not_setup_error_flash 238 redirect two_factor_need_setup_redirect 239 end 240 end
# File lib/rodauth/features/confirm_password.rb 51 def require_password_authentication 52 require_login 53 54 if require_password_authentication? && has_password? 55 set_redirect_error_status(password_authentication_required_error_status) 56 set_error_reason :password_authentication_required 57 set_redirect_error_flash password_authentication_required_error_flash 58 set_session_value(confirm_password_redirect_session_key, request.fullpath) 59 redirect password_authentication_required_redirect 60 end 61 end
# File lib/rodauth/features/confirm_password.rb 87 def require_password_authentication? 88 return true if defined?(super) && super 89 !authenticated_by.include?('password') 90 end
# File lib/rodauth/features/sms_codes.rb 329 def require_sms_available 330 require_sms_setup 331 332 if sms_locked_out? 333 set_redirect_error_status(lockout_error_status) 334 set_error_reason :sms_locked_out 335 set_redirect_error_flash sms_lockout_error_flash 336 redirect sms_lockout_redirect 337 end 338 end
# File lib/rodauth/features/sms_codes.rb 320 def require_sms_not_setup 321 if sms_setup? 322 set_redirect_error_status(sms_already_setup_error_status) 323 set_error_reason :sms_already_setup 324 set_redirect_error_flash sms_already_setup_error_flash 325 redirect sms_already_setup_redirect 326 end 327 end
# File lib/rodauth/features/sms_codes.rb 311 def require_sms_setup 312 unless sms_setup? 313 set_redirect_error_status(two_factor_not_setup_error_status) 314 set_error_reason :sms_not_setup 315 set_redirect_error_flash sms_not_setup_error_flash 316 redirect sms_needs_setup_redirect 317 end 318 end
# File lib/rodauth/features/two_factor_base.rb 165 def require_two_factor_authenticated 166 unless two_factor_authenticated? 167 if two_factor_auth_return_to_requested_location? 168 set_session_value(two_factor_auth_redirect_session_key, request.fullpath) 169 end 170 set_redirect_error_status(two_factor_need_authentication_error_status) 171 set_error_reason :two_factor_need_authentication 172 set_redirect_error_flash two_factor_need_authentication_error_flash 173 redirect two_factor_auth_required_redirect 174 end 175 end
# File lib/rodauth/features/two_factor_base.rb 156 def require_two_factor_not_authenticated(auth_type = nil) 157 if two_factor_authenticated? || (auth_type && two_factor_login_type_match?(auth_type)) 158 set_redirect_error_status(two_factor_already_authenticated_error_status) 159 set_error_reason :two_factor_already_authenticated 160 set_redirect_error_flash two_factor_already_authenticated_error_flash 161 redirect two_factor_already_authenticated_redirect 162 end 163 end
# File lib/rodauth/features/two_factor_base.rb 144 def require_two_factor_setup 145 # Avoid database query if already authenticated via 2nd factor 146 return if two_factor_authenticated? 147 148 return if uses_two_factor_authentication? 149 150 set_redirect_error_status(two_factor_not_setup_error_status) 151 set_error_reason :two_factor_not_setup 152 set_redirect_error_flash two_factor_not_setup_error_flash 153 redirect two_factor_need_setup_redirect 154 end
# File lib/rodauth/features/webauthn.rb 390 def require_webauthn_setup 391 unless webauthn_setup? 392 set_redirect_error_status(webauthn_not_setup_error_status) 393 set_error_reason :webauthn_not_setup 394 set_redirect_error_flash webauthn_not_setup_error_flash 395 redirect two_factor_need_setup_redirect 396 end 397 end
# File lib/rodauth/features/jwt.rb 121 def rescue_jwt_payload(_) 122 @jwt_payload = false 123 end
# File lib/rodauth/features/reset_password.rb 187 def reset_password_email_link 188 token_link(reset_password_route, reset_password_key_param, reset_password_key_value) 189 end
# File lib/rodauth/features/reset_password.rb 215 def reset_password_email_recently_sent? 216 (email_last_sent = get_reset_password_email_last_sent) && (Time.now - email_last_sent < reset_password_skip_resend_email_within) 217 end
# File lib/rodauth/features/reset_password.rb 245 def reset_password_key_insert_hash 246 hash = {reset_password_id_column=>account_id, reset_password_key_column=>reset_password_key_value} 247 set_deadline_value(hash, reset_password_deadline_column, reset_password_deadline_interval) 248 hash 249 end
# File lib/rodauth/features/single_session.rb 22 def reset_single_session_key 23 if logged_in? 24 single_session_ds.update(single_session_key_column=>random_key) 25 end 26 end
# File lib/rodauth/features/base.rb 151 def response 152 scope.response 153 end
This is used to avoid race conditions when using the pattern of inserting when an update affects no rows. In such cases, if a row is inserted between the update and the insert, the insert will fail with a uniqueness error, but retrying will work. It is possible for it to fail again, but only if the row is deleted before the update and readded before the insert, which is very unlikely to happen. In such cases, raising an exception is acceptable.
# File lib/rodauth/features/base.rb 767 def retry_on_uniqueness_violation(&block) 768 if raises_uniqueness_violation?(&block) 769 yield 770 end 771 end
# File lib/rodauth/features/json.rb 170 def return_json_response 171 _return_json_response 172 end
# File lib/rodauth/features/base.rb 557 def return_response(body=nil) 558 response.write(body) if body 559 request.halt 560 end
# File lib/rodauth/features/base.rb 163 def route! 164 if meth = self.class.route_hash[request.remaining_path] 165 send(meth) 166 end 167 168 nil 169 end
# File lib/rodauth/features/base.rb 562 def route_path(route, opts={}) 563 path = "#{prefix}/#{route}" 564 path += "?#{Rack::Utils.build_nested_query(opts)}" unless opts.empty? 565 path 566 end
# File lib/rodauth/features/base.rb 568 def route_url(route, opts={}) 569 "#{base_url}#{route_path(route, opts)}" 570 end
# File lib/rodauth/features/create_account.rb 100 def save_account 101 id = nil 102 raised = raises_uniqueness_violation?{id = db[accounts_table].insert(account)} 103 104 if raised 105 set_login_requirement_error_message(:already_an_account_with_this_login, already_an_account_with_this_login_message) 106 end 107 108 if id 109 account[account_id_column] = id 110 end 111 112 id && !raised 113 end
# File lib/rodauth/features/email_base.rb 36 def send_email(email) 37 email.deliver! 38 end
# File lib/rodauth/features/verify_login_change.rb 126 def send_verify_login_change_email(login) 127 send_email(create_verify_login_change_email(login)) 128 end
# File lib/rodauth/features/audit_logging.rb 55 def serialize_audit_log_metadata(metadata) 56 metadata.to_json unless metadata.nil? 57 end
# File lib/rodauth/features/base.rb 155 def session 156 scope.session 157 end
# File lib/rodauth/features/active_sessions.rb 156 def session_inactivity_deadline_condition 157 if deadline = session_inactivity_deadline 158 Sequel[active_sessions_last_use_column] < Sequel.date_sub(Sequel::CURRENT_TIMESTAMP, seconds: deadline) 159 end 160 end
# File lib/rodauth/features/jwt.rb 75 def session_jwt 76 JWT.encode(jwt_session_hash, jwt_secret, jwt_algorithm) 77 end
# File lib/rodauth/features/active_sessions.rb 162 def session_lifetime_deadline_condition 163 if deadline = session_lifetime_deadline 164 Sequel[active_sessions_created_at_column] < Sequel.date_sub(Sequel::CURRENT_TIMESTAMP, seconds: deadline) 165 end 166 end
# File lib/rodauth/features/base.rb 255 def session_value 256 session[session_key] 257 end
This is needed on MySQL, which doesn’t support non constant defaults other than CURRENT_TIMESTAMP.
# File lib/rodauth/features/base.rb 803 def set_deadline_value(hash, column, interval) 804 if set_deadline_values? 805 # :nocov: 806 hash[column] = Sequel.date_add(Sequel::CURRENT_TIMESTAMP, interval) 807 # :nocov: 808 end 809 end
# File lib/rodauth/features/base.rb 640 def set_deadline_values? 641 db.database_type == :mysql 642 end
# File lib/rodauth/features/email_auth.rb 117 def set_email_auth_email_last_sent 118 email_auth_ds.update(email_auth_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if email_auth_email_last_sent_column 119 end
# File lib/rodauth/features/base.rb 321 def set_error_flash(message) 322 flash.now[flash_error_key] = message 323 end
# File lib/rodauth/features/base.rb 628 def set_error_reason(reason) 629 end
# File lib/rodauth/features/account_expiration.rb 49 def set_expired 50 update_activity(account_id, account_activity_expired_column) 51 after_account_expiration 52 end
# File lib/rodauth/features/base.rb 171 def set_field_error(field, error) 172 (@field_errors ||= {})[field] = error 173 end
# File lib/rodauth/features/http_basic_auth.rb 74 def set_http_basic_auth_error_response 75 response.status = 401 76 response.headers["WWW-Authenticate"] = "Basic realm=\"#{http_basic_auth_realm}\"" 77 end
# File lib/rodauth/features/jwt.rb 142 def set_jwt 143 set_jwt_token(session_jwt) 144 end
# File lib/rodauth/features/jwt_refresh.rb 194 def set_jwt_refresh_token_hmac_session_key(token) 195 if allow_refresh_with_expired_jwt_access_token? 196 key = _account_refresh_token_split(token).last 197 data = random_key 198 set_session_value(jwt_refresh_token_data_session_key, data) 199 set_session_value(jwt_refresh_token_hmac_session_key, compute_hmac(data + key)) 200 end 201 end
# File lib/rodauth/features/jwt.rb 87 def set_jwt_token(token) 88 response.headers['Authorization'] = token 89 end
# File lib/rodauth/features/password_grace_period.rb 44 def set_last_password_entry 45 set_session_value(last_password_entry_session_key, Time.now.to_i) 46 end
# File lib/rodauth/features/login_password_requirements_base.rb 124 def set_login_requirement_error_message(reason, message) 125 set_error_reason(reason) 126 @login_requirement_message = message 127 end
# File lib/rodauth/features/create_account.rb 92 def set_new_account_password(password) 93 account[account_password_hash_column] = password_hash(password) 94 end
# File lib/rodauth/features/base.rb 329 def set_notice_flash(message) 330 flash[flash_notice_key] = message 331 end
# File lib/rodauth/features/base.rb 333 def set_notice_now_flash(message) 334 flash.now[flash_notice_key] = message 335 end
# File lib/rodauth/features/disallow_password_reuse.rb 19 def set_password(password) 20 hash = super 21 add_previous_password_hash(hash) 22 hash 23 end
# File lib/rodauth/features/login_password_requirements_base.rb 103 def set_password_requirement_error_message(reason, message) 104 set_error_reason(reason) 105 @password_requirement_message = message 106 end
# File lib/rodauth/features/base.rb 325 def set_redirect_error_flash(message) 326 flash[flash_error_key] = message 327 end
Don’t set an error status when redirecting in an error case, as a redirect status is needed.
# File lib/rodauth/features/base.rb 606 def set_redirect_error_status(status) 607 end
# File lib/rodauth/features/remember.rb 176 def set_remember_cookie 177 opts = Hash[remember_cookie_options] 178 opts[:value] = "#{account_id}_#{convert_token_key(remember_key_value)}" 179 opts[:expires] = convert_timestamp(active_remember_key_ds.get(remember_deadline_column)) 180 opts[:path] = "/" unless opts.key?(:path) 181 opts[:httponly] = true unless opts.key?(:httponly) || opts.key?(:http_only) 182 opts[:secure] = true unless opts.key?(:secure) || !request.ssl? 183 ::Rack::Utils.set_cookie_header!(response.headers, remember_cookie_key, opts) 184 end
# File lib/rodauth/features/reset_password.rb 197 def set_reset_password_email_last_sent 198 password_reset_ds.update(reset_password_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if reset_password_email_last_sent_column 199 end
# File lib/rodauth/features/base.rb 613 def set_response_error_reason_status(reason, status) 614 set_error_reason(reason) 615 set_response_error_status(status) 616 end
# File lib/rodauth/features/base.rb 609 def set_response_error_status(status) 610 response.status = status 611 end
# File lib/rodauth/features/base.rb 821 def set_session_value(key, value) 822 session[key] = value 823 end
# File lib/rodauth/features/single_session.rb 93 def set_single_session_key(data) 94 data = compute_hmac(data) if hmac_secret 95 set_session_value(single_session_session_key, data) 96 end
# File lib/rodauth/features/base.rb 315 def set_title(title) 316 if title_instance_variable 317 scope.instance_variable_set(title_instance_variable, title) 318 end 319 end
# File lib/rodauth/features/lockout.rb 238 def set_unlock_account_email_last_sent 239 account_lockouts_ds.update(account_lockouts_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if account_lockouts_email_last_sent_column 240 end
# File lib/rodauth/features/verify_account.rb 232 def set_verify_account_email_last_sent 233 verify_account_ds.update(verify_account_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if verify_account_email_last_sent_column 234 end
# File lib/rodauth/features/verify_account.rb 244 def setup_account_verification 245 generate_verify_account_key_value 246 create_verify_account_key 247 send_verify_account_email 248 end
# File lib/rodauth/features/lockout.rb 277 def show_lockout_page 278 set_response_error_reason_status(:account_locked_out, lockout_error_status) 279 set_error_flash login_lockout_error_flash 280 return_response unlock_account_request_view 281 end
# File lib/rodauth/features/single_session.rb 98 def single_session_ds 99 db[single_session_table]. 100 where(single_session_id_column=>session_value) 101 end
# File lib/rodauth/features/login.rb 111 def skip_login_field_on_login? 112 return false unless use_multi_phase_login? 113 valid_login_entered? 114 end
# File lib/rodauth/features/login.rb 116 def skip_password_field_on_login? 117 return false unless use_multi_phase_login? 118 !valid_login_entered? 119 end
# File lib/rodauth/features/close_account.rb 84 def skip_status_checks? 85 false 86 end
# File lib/rodauth/features/sms_codes.rb 390 def sms_auth_message(code) 391 "SMS authentication code for #{domain} is #{code}" 392 end
# File lib/rodauth/features/sms_codes.rb 432 def sms_available? 433 sms_setup? && !sms_locked_out? 434 end
# File lib/rodauth/features/sms_codes.rb 411 def sms_code 412 sms[sms_code_column] 413 end
# File lib/rodauth/features/sms_codes.rb 415 def sms_code_issued_at 416 convert_timestamp(sms[sms_issued_at_column]) 417 end
# File lib/rodauth/features/sms_codes.rb 340 def sms_code_match?(code) 341 return false unless sms_current_auth? 342 timing_safe_eql?(code, sms_code) 343 end
# File lib/rodauth/features/sms_codes.rb 475 def sms_codes_primary? 476 (features & [:otp, :webauthn]).empty? 477 end
# File lib/rodauth/features/recovery_codes.rb 146 def sms_confirm 147 super if defined?(super) 148 auto_add_missing_recovery_codes 149 end
# File lib/rodauth/features/sms_codes.rb 354 def sms_confirm_failure 355 sms_ds.delete 356 end
# File lib/rodauth/features/sms_codes.rb 394 def sms_confirm_message(code) 395 "SMS confirmation code for #{domain} is #{code}" 396 end
# File lib/rodauth/features/sms_codes.rb 345 def sms_confirmation_match?(code) 346 sms_needs_confirmation? && sms_code_match?(code) 347 end
# File lib/rodauth/features/sms_codes.rb 440 def sms_current_auth? 441 sms_code && sms_code_issued_at + sms_code_allowed_seconds > Time.now 442 end
# File lib/rodauth/features/sms_codes.rb 349 def sms_disable 350 sms_ds.delete 351 @sms = nil 352 end
# File lib/rodauth/features/sms_codes.rb 503 def sms_ds 504 db[sms_codes_table].where(sms_id_column=>session_value) 505 end
# File lib/rodauth/features/sms_codes.rb 419 def sms_failures 420 sms[sms_failures_column] 421 end
# File lib/rodauth/features/sms_codes.rb 436 def sms_locked_out? 437 sms_failures >= sms_failure_limit 438 end
# File lib/rodauth/features/sms_codes.rb 428 def sms_needs_confirmation? 429 sms && sms_failures.nil? 430 end
# File lib/rodauth/features/sms_codes.rb 483 def sms_new_auth_code 484 SecureRandom.random_number(10**sms_auth_code_length).to_s.rjust(sms_auth_code_length, "0") 485 end
# File lib/rodauth/features/sms_codes.rb 487 def sms_new_confirm_code 488 SecureRandom.random_number(10**sms_confirm_code_length).to_s.rjust(sms_confirm_code_length, "0") 489 end
# File lib/rodauth/features/sms_codes.rb 479 def sms_normalize_phone(phone) 480 phone.to_s.gsub(/\D+/, '') 481 end
# File lib/rodauth/features/sms_codes.rb 407 def sms_phone 408 sms[sms_phone_column] 409 end
# File lib/rodauth/features/sms_codes.rb 402 def sms_record_failure 403 update_sms(sms_failures_column=>Sequel.expr(sms_failures_column)+1) 404 sms[sms_failures_column] = sms_ds.get(sms_failures_column) 405 end
# File lib/rodauth/features/sms_codes.rb 365 def sms_remove_failures 366 update_sms(sms_failures_column => 0, sms_code_column => nil) 367 end
# File lib/rodauth/features/sms_codes.rb 491 def sms_send(phone, message) 492 raise NotImplementedError, "sms_send needs to be defined in the Rodauth configuration for SMS sending to work" 493 end
# File lib/rodauth/features/sms_codes.rb 374 def sms_send_auth_code 375 code = sms_new_auth_code 376 sms_set_code(code) 377 sms_send(sms_phone, sms_auth_message(code)) 378 end
# File lib/rodauth/features/sms_codes.rb 380 def sms_send_confirm_code 381 code = sms_new_confirm_code 382 sms_set_code(code) 383 sms_send(sms_phone, sms_confirm_message(code)) 384 end
# File lib/rodauth/features/sms_codes.rb 398 def sms_set_code(code) 399 update_sms(sms_code_column=>code, sms_issued_at_column=>Sequel::CURRENT_TIMESTAMP) 400 end
# File lib/rodauth/features/sms_codes.rb 358 def sms_setup(phone_number) 359 # Cannot handle uniqueness violation here, as the phone number given may not match the 360 # one in the table. 361 sms_ds.insert(sms_id_column=>session_value, sms_phone_column=>phone_number) 362 remove_instance_variable(:@sms) if instance_variable_defined?(:@sms) 363 end
# File lib/rodauth/features/sms_codes.rb 423 def sms_setup? 424 return false unless sms 425 !sms_needs_confirmation? 426 end
# File lib/rodauth/features/sms_codes.rb 386 def sms_valid_phone?(phone) 387 phone.length >= sms_phone_min_length 388 end
# File lib/rodauth/features/base.rb 530 def split_token(token) 531 token.split(token_separator, 2) 532 end
# File lib/rodauth/features/base.rb 732 def template_path(page) 733 File.join(File.dirname(__FILE__), '../../../templates', "#{page}.str") 734 end
# File lib/rodauth/features/http_basic_auth.rb 79 def throw_basic_auth_error(*args) 80 set_http_basic_auth_error_response 81 throw_error(*args) 82 end
# File lib/rodauth/features/base.rb 618 def throw_error(field, error) 619 set_field_error(field, error) 620 throw :rodauth_error 621 end
# File lib/rodauth/features/base.rb 631 def throw_error_reason(reason, status, field, message) 632 set_error_reason(reason) 633 throw_error_status(status, field, message) 634 end
# File lib/rodauth/features/base.rb 623 def throw_error_status(status, field, error) 624 set_response_error_status(status) 625 throw_error(field, error) 626 end
# File lib/rodauth/features/base.rb 589 def timing_safe_eql?(provided, actual) 590 provided = provided.to_s 591 Rack::Utils.secure_compare(provided.ljust(actual.length), actual) && provided.length == actual.length 592 end
# File lib/rodauth/features/email_base.rb 53 def token_link(route, param, key) 54 route_url(route, param => token_param_value(key)) 55 end
# File lib/rodauth/features/email_base.rb 57 def token_param_value(key) 58 "#{account_id}#{token_separator}#{convert_email_token_key(key)}" 59 end
# File lib/rodauth/features/base.rb 572 def transaction(opts={}, &block) 573 db.transaction(opts, &block) 574 end
# File lib/rodauth/features/base.rb 238 def translate(_key, default) 239 # do not attempt to translate by default 240 default 241 end
# File lib/rodauth/features/two_factor_base.rb 211 def two_factor_auth_links 212 @two_factor_auth_links ||= _filter_links(_two_factor_auth_links) 213 end
# File lib/rodauth/features/two_factor_base.rb 246 def two_factor_authenticate(type) 247 two_factor_update_session(type) 248 two_factor_remove_auth_failures 249 after_two_factor_authentication 250 set_notice_flash two_factor_auth_notice_flash 251 redirect_two_factor_authenticated 252 end
# File lib/rodauth/features/two_factor_base.rb 189 def two_factor_authenticated? 190 authenticated_by && authenticated_by.length >= 2 191 end
# File lib/rodauth/features/two_factor_base.rb 193 def two_factor_authentication_setup? 194 possible_authentication_methods.length >= 2 195 end
# File lib/rodauth/features/two_factor_base.rb 203 def two_factor_login_type_match?(type) 204 authenticated_by && authenticated_by.include?(type) 205 end
# File lib/rodauth/features/two_factor_base.rb 120 def two_factor_modifications_require_password? 121 modifications_require_password? 122 end
# File lib/rodauth/features/two_factor_base.rb 181 def two_factor_password_match?(password) 182 if two_factor_modifications_require_password? 183 password_match?(password) 184 else 185 true 186 end 187 end
# File lib/rodauth/features/otp.rb 223 def two_factor_remove 224 super 225 otp_remove 226 end
# File lib/rodauth/features/otp.rb 228 def two_factor_remove_auth_failures 229 super 230 otp_remove_auth_failures 231 end
# File lib/rodauth/features/two_factor_base.rb 219 def two_factor_remove_links 220 @two_factor_remove_links ||= _filter_links(_two_factor_remove_links) 221 end
# File lib/rodauth/features/two_factor_base.rb 259 def two_factor_remove_session(type) 260 authenticated_by.delete(type) 261 remove_session_value(two_factor_setup_session_key) 262 if authenticated_by.empty? 263 clear_session 264 end 265 end
# File lib/rodauth/features/two_factor_base.rb 215 def two_factor_setup_links 216 @two_factor_setup_links ||= _filter_links(_two_factor_setup_links) 217 end
# File lib/rodauth/features/two_factor_base.rb 267 def two_factor_update_session(auth_type) 268 authenticated_by << auth_type 269 set_session_value(two_factor_setup_session_key, true) 270 end
Work around jdbc/sqlite issue where it only raises ConstraintViolation and not UniqueConstraintViolation.
# File lib/rodauth/features/base.rb 785 def unique_constraint_violation_class 786 if db.adapter_scheme == :jdbc && db.database_type == :sqlite 787 # :nocov: 788 Sequel::ConstraintViolation 789 # :nocov: 790 else 791 Sequel::UniqueConstraintViolation 792 end 793 end
# File lib/rodauth/features/lockout.rb 161 def unlock_account 162 transaction do 163 remove_lockout_metadata 164 end 165 end
# File lib/rodauth/features/lockout.rb 226 def unlock_account_email_link 227 token_link(unlock_account_route, unlock_account_key_param, unlock_account_key_value) 228 end
# File lib/rodauth/features/lockout.rb 283 def unlock_account_email_recently_sent? 284 (email_last_sent = get_unlock_account_email_last_sent) && (Time.now - email_last_sent < unlock_account_skip_resend_email_within) 285 end
# File lib/rodauth/features/verify_account_grace_period.rb 92 def unverified_grace_period_expired? 93 return false unless expires_at = session[unverified_account_session_key] 94 expires_at.is_a?(Integer) && Time.now.to_i > expires_at 95 end
# File lib/rodauth/features/base.rb 839 def update_account(values, ds=account_ds) 840 update_hash_ds(account, ds, values) 841 end
# File lib/rodauth/features/account_expiration.rb 113 def update_activity(account_id, *columns) 114 ds = account_activity_ds(account_id) 115 hash = {} 116 columns.each do |c| 117 hash[c] = Sequel::CURRENT_TIMESTAMP 118 end 119 if ds.update(hash) == 0 120 hash[account_activity_id_column] = account_id 121 hash[account_activity_last_activity_column] ||= Sequel::CURRENT_TIMESTAMP 122 hash[account_activity_last_login_column] ||= Sequel::CURRENT_TIMESTAMP 123 # It is safe to ignore uniqueness violations here, as a concurrent insert would also use current timestamps. 124 ignore_uniqueness_violation{ds.insert(hash)} 125 end 126 end
# File lib/rodauth/features/active_sessions.rb 175 def update_current_session? 176 !!session_inactivity_deadline 177 end
# File lib/rodauth/features/base.rb 829 def update_hash_ds(hash, ds, values) 830 num = ds.update(values) 831 if num == 1 832 values.each do |k, v| 833 hash[k] = Sequel::CURRENT_TIMESTAMP == v ? Time.now : v 834 end 835 end 836 num 837 end
# File lib/rodauth/features/account_expiration.rb 43 def update_last_activity 44 if session_value 45 update_activity(session_value, account_activity_last_activity_column) 46 end 47 end
# File lib/rodauth/features/account_expiration.rb 39 def update_last_login 40 update_activity(account_id, account_activity_last_login_column, account_activity_last_activity_column) 41 end
# File lib/rodauth/features/change_login.rb 79 def update_login(login) 80 _update_login(login) 81 end
# File lib/rodauth/features/password_expiration.rb 52 def update_password_changed_at 53 ds = password_expiration_ds 54 if ds.update(password_expiration_changed_at_column=>Sequel::CURRENT_TIMESTAMP) == 0 55 # Ignoring the violation is safe here, since a concurrent insert would also set it to the 56 # current timestamp. 57 ignore_uniqueness_violation{ds.insert(password_expiration_id_column=>account_id)} 58 end 59 end
# File lib/rodauth/features/update_password_hash.rb 17 def update_password_hash? 18 password_hash_cost != @current_password_hash_cost 19 end
# File lib/rodauth/features/account_expiration.rb 72 def update_session 73 check_account_expiration 74 super 75 end
# File lib/rodauth/features/single_session.rb 65 def update_single_session_key 66 key = random_key 67 set_single_session_key(key) 68 if single_session_ds.update(single_session_key_column=>key) == 0 69 # Don't handle uniqueness violations here. While we could get the stored key from the 70 # database, it could lead to two sessions sharing the same key, which this feature is 71 # designed to prevent. 72 single_session_ds.insert(single_session_id_column=>session_value, single_session_key_column=>key) 73 end 74 end
# File lib/rodauth/features/sms_codes.rb 495 def update_sms(values) 496 update_hash_ds(sms, sms_ds, values) 497 end
# File lib/rodauth/features/base.rb 644 def use_database_authentication_functions? 645 case db.database_type 646 when :postgres, :mysql, :mssql 647 true 648 else 649 # :nocov: 650 false 651 # :nocov: 652 end 653 end
# File lib/rodauth/features/active_sessions.rb 184 def use_date_arithmetic? 185 true 186 end
# File lib/rodauth/features/json.rb 59 def use_json? 60 json_request? || only_json? 61 end
# File lib/rodauth/features/email_auth.rb 158 def use_multi_phase_login? 159 true 160 end
# File lib/rodauth/features/base.rb 655 def use_request_specific_csrf_tokens? 656 scope.opts[:rodauth_route_csrf] && scope.use_request_specific_csrf_tokens? 657 end
# File lib/rodauth/features/two_factor_base.rb 197 def uses_two_factor_authentication? 198 return false unless logged_in? 199 set_session_value(two_factor_setup_session_key, two_factor_authentication_setup?) unless session.has_key?(two_factor_setup_session_key) 200 session[two_factor_setup_session_key] 201 end
# File lib/rodauth/features/jwt.rb 99 def valid_jwt? 100 !!(jwt_token && jwt_payload) 101 end
# File lib/rodauth/features/login.rb 121 def valid_login_entered? 122 @valid_login_entered 123 end
# File lib/rodauth/features/webauthn.rb 308 def valid_new_webauthn_credential?(webauthn_credential) 309 # Hack around inability to override expected_origin 310 origin = webauthn_origin 311 webauthn_credential.response.define_singleton_method(:verify) do |expected_challenge, expected_origin = nil, **kw| 312 super(expected_challenge, expected_origin || origin, **kw) 313 end 314 315 (challenge = param_or_nil(webauthn_setup_challenge_param)) && 316 (hmac = param_or_nil(webauthn_setup_challenge_hmac_param)) && 317 timing_safe_eql?(compute_hmac(challenge), hmac) && 318 webauthn_credential.verify(challenge) 319 end
# File lib/rodauth/features/webauthn.rb 357 def valid_webauthn_credential_auth?(webauthn_credential) 358 ds = webauthn_keys_ds.where(webauthn_keys_webauthn_id_column => webauthn_credential.id) 359 pub_key, sign_count = ds.get([webauthn_keys_public_key_column, webauthn_keys_sign_count_column]) 360 361 # Hack around inability to override expected_origin 362 origin = webauthn_origin 363 webauthn_credential.response.define_singleton_method(:verify) do |expected_challenge, expected_origin = nil, **kw| 364 super(expected_challenge, expected_origin || origin, **kw) 365 end 366 367 (challenge = param_or_nil(webauthn_auth_challenge_param)) && 368 (hmac = param_or_nil(webauthn_auth_challenge_hmac_param)) && 369 timing_safe_eql?(compute_hmac(challenge), hmac) && 370 webauthn_credential.verify(challenge, public_key: pub_key, sign_count: sign_count) && 371 ds.update( 372 webauthn_keys_sign_count_column => Integer(webauthn_credential.sign_count), 373 webauthn_keys_last_use_column => Sequel::CURRENT_TIMESTAMP 374 ) == 1 375 end
# File lib/rodauth/features/verify_account_grace_period.rb 17 def verified_account? 18 logged_in? && !session[unverified_account_session_key] 19 end
# File lib/rodauth/features/verify_account.rb 178 def verify_account 179 update_account(account_status_column=>account_open_status_value) == 1 180 end
# File lib/rodauth/features/verify_account.rb 280 def verify_account_check_already_logged_in 281 check_already_logged_in 282 end
# File lib/rodauth/features/verify_account.rb 305 def verify_account_ds(id=account_id) 306 db[verify_account_table].where(verify_account_id_column=>id) 307 end
# File lib/rodauth/features/verify_account.rb 211 def verify_account_email_link 212 token_link(verify_account_route, verify_account_key_param, verify_account_key_value) 213 end
# File lib/rodauth/features/verify_account.rb 260 def verify_account_email_recently_sent? 261 (email_last_sent = get_verify_account_email_last_sent) && (Time.now - email_last_sent < verify_account_skip_resend_email_within) 262 end
# File lib/rodauth/features/verify_account.rb 182 def verify_account_email_resend 183 if @verify_account_key_value = get_verify_account_key(account_id) 184 set_verify_account_email_last_sent 185 send_verify_account_email 186 true 187 end 188 end
# File lib/rodauth/features/verify_account.rb 301 def verify_account_key_insert_hash 302 {verify_account_id_column=>account_id, verify_account_key_column=>verify_account_key_value} 303 end
# File lib/rodauth/features/verify_account_grace_period.rb 29 def verify_account_set_password? 30 false 31 end
# File lib/rodauth/features/webauthn_verify_account.rb 7 def verify_account_view 8 webauthn_setup_view 9 end
# File lib/rodauth/features/verify_login_change.rb 114 def verify_login_change 115 unless res = _update_login(verify_login_change_new_login) 116 remove_verify_login_change_key 117 end 118 119 res 120 end
# File lib/rodauth/features/verify_login_change.rb 208 def verify_login_change_ds(id=account_id) 209 db[verify_login_change_table].where(verify_login_change_id_column=>id) 210 end
# File lib/rodauth/features/verify_login_change.rb 204 def verify_login_change_email_body 205 render('verify-login-change-email') 206 end
# File lib/rodauth/features/verify_login_change.rb 130 def verify_login_change_email_link 131 token_link(verify_login_change_route, verify_login_change_key_param, verify_login_change_key_value) 132 end
# File lib/rodauth/features/verify_login_change.rb 194 def verify_login_change_key_insert_hash(login) 195 hash = {verify_login_change_id_column=>account_id, verify_login_change_key_column=>verify_login_change_key_value, verify_login_change_login_column=>login} 196 set_deadline_value(hash, verify_login_change_deadline_column, verify_login_change_deadline_interval) 197 hash 198 end
# File lib/rodauth/features/verify_login_change.rb 142 def verify_login_change_old_login 143 account_ds.get(login_column) 144 end
# File lib/rodauth/features/base.rb 394 def view(page, title) 395 set_title(title) 396 _view(:view, page) 397 end
# File lib/rodauth/features/webauthn.rb 443 def webauthn_account_id 444 session_value 445 end
# File lib/rodauth/features/webauthn_login.rb 35 def webauthn_auth_additional_form_tags 36 if @webauthn_login 37 super.to_s + login_hidden_field 38 else 39 super 40 end 41 end
# File lib/rodauth/features/webauthn.rb 455 def webauthn_auth_credential_from_form_submission 456 case auth_data = raw_param(webauthn_auth_param) 457 when String 458 begin 459 auth_data = JSON.parse(auth_data) 460 rescue 461 throw_error_reason(:invalid_webauthn_auth_param, invalid_field_error_status, webauthn_auth_param, webauthn_invalid_auth_param_message) 462 end 463 when Hash 464 # nothing 465 else 466 throw_error_reason(:invalid_webauthn_auth_param, invalid_field_error_status, webauthn_auth_param, webauthn_invalid_auth_param_message) 467 end 468 469 begin 470 webauthn_credential = WebAuthn::Credential.from_get(auth_data) 471 unless valid_webauthn_credential_auth?(webauthn_credential) 472 throw_error_reason(:invalid_webauthn_auth_param, invalid_key_error_status, webauthn_auth_param, webauthn_invalid_auth_param_message) 473 end 474 rescue WebAuthn::SignCountVerificationError 475 handle_webauthn_sign_count_verification_error 476 rescue WebAuthn::Error, RuntimeError, NoMethodError 477 throw_error_reason(:invalid_webauthn_auth_param, invalid_field_error_status, webauthn_auth_param, webauthn_invalid_auth_param_message) 478 end 479 480 webauthn_credential 481 end
# File lib/rodauth/features/webauthn.rb 241 def webauthn_auth_form_path 242 webauthn_auth_path 243 end
# File lib/rodauth/features/webauthn.rb 257 def webauthn_authenticator_selection 258 {'requireResidentKey' => false, 'userVerification' => webauthn_user_verification} 259 end
# File lib/rodauth/features/webauthn.rb 321 def webauthn_credential_options_for_get 322 WebAuthn::Credential.options_for_get( 323 :allow => account_webauthn_ids, 324 :timeout => webauthn_auth_timeout, 325 :rp_id => webauthn_rp_id, 326 :user_verification => webauthn_user_verification, 327 :extensions => webauthn_extensions, 328 ) 329 end
# File lib/rodauth/features/webauthn.rb 261 def webauthn_extensions 262 {} 263 end
# File lib/rodauth/features/webauthn.rb 434 def webauthn_key_insert_hash(webauthn_credential) 435 { 436 webauthn_keys_account_id_column => webauthn_account_id, 437 webauthn_keys_webauthn_id_column => webauthn_credential.id, 438 webauthn_keys_public_key_column => webauthn_credential.public_key, 439 webauthn_keys_sign_count_column => Integer(webauthn_credential.sign_count) 440 } 441 end
# File lib/rodauth/features/webauthn.rb 451 def webauthn_keys_ds 452 db[webauthn_keys_table].where(webauthn_keys_account_id_column => webauthn_account_id) 453 end
# File lib/rodauth/features/webauthn.rb 335 def webauthn_origin 336 base_url 337 end
# File lib/rodauth/features/webauthn.rb 249 def webauthn_remove_authenticated_session 250 remove_session_value(authenticated_webauthn_id_session_key) 251 end
# File lib/rodauth/features/webauthn.rb 339 def webauthn_rp_id 340 webauthn_origin.sub(/\Ahttps?:\/\//, '').sub(/:\d+\z/, '') 341 end
# File lib/rodauth/features/webauthn.rb 343 def webauthn_rp_name 344 webauthn_rp_id 345 end
# File lib/rodauth/features/webauthn.rb 386 def webauthn_setup? 387 !webauthn_keys_ds.empty? 388 end
# File lib/rodauth/features/webauthn.rb 483 def webauthn_setup_credential_from_form_submission 484 case setup_data = raw_param(webauthn_setup_param) 485 when String 486 begin 487 setup_data = JSON.parse(setup_data) 488 rescue 489 throw_error_reason(:invalid_webauthn_setup_param, invalid_field_error_status, webauthn_setup_param, webauthn_invalid_setup_param_message) 490 end 491 when Hash 492 # nothing 493 else 494 throw_error_reason(:invalid_webauthn_setup_param, invalid_field_error_status, webauthn_setup_param, webauthn_invalid_setup_param_message) 495 end 496 497 unless two_factor_password_match?(param(password_param)) 498 throw_error_reason(:invalid_password, invalid_password_error_status, password_param, invalid_password_message) 499 end 500 501 begin 502 webauthn_credential = WebAuthn::Credential.from_create(setup_data) 503 unless valid_new_webauthn_credential?(webauthn_credential) 504 throw_error_reason(:invalid_webauthn_setup_param, invalid_field_error_status, webauthn_setup_param, webauthn_invalid_setup_param_message) 505 end 506 rescue WebAuthn::Error, RuntimeError, NoMethodError 507 throw_error_reason(:invalid_webauthn_setup_param, invalid_field_error_status, webauthn_setup_param, webauthn_invalid_setup_param_message) 508 end 509 510 webauthn_credential 511 end
# File lib/rodauth/features/webauthn.rb 253 def webauthn_update_session(webauthn_id) 254 set_session_value(authenticated_webauthn_id_session_key, webauthn_id) 255 end
# File lib/rodauth/features/webauthn.rb 447 def webauthn_user_ids_ds 448 db[webauthn_user_ids_table].where(webauthn_user_ids_account_id_column => webauthn_account_id) 449 end
# File lib/rodauth/features/webauthn.rb 331 def webauthn_user_name 332 account![login_column] 333 end