:nocov:
: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_id
- _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
- _field_attributes
- _field_error_attributes
- _filter_links
- _formatted_field_error
- _get_remember_cookie
- _initialize_instance_variables
- _json_response_body
- _jwt_decode_opts
- _jwt_decode_secrets
- _login
- _login_form_footer
- _login_form_footer_links
- _login_response
- _merge_fixed_locals_opts
- _multi_phase_login_forms
- _new_account
- _otp
- _otp_add_key
- _otp_for_key
- _otp_interval
- _otp_key
- _otp_tmp_key
- _otp_valid_code?
- _otp_valid_code_for_old_secret
- _override_webauthn_credential_response_verify
- _process_raw_hmac
- _recovery_codes
- _return_json_response
- _set_otp_unlock_info
- _set_remember_cookie
- _setup_account_lockouts_hash
- _sms_request_response
- _template_opts
- _two_factor_auth_links
- _two_factor_auth_response
- _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_id
- 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_from_webauthn_login
- 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_authentication_failure
- after_otp_disable
- after_otp_setup
- after_otp_unlock_auth_failure
- after_otp_unlock_auth_success
- after_otp_unlock_not_yet_available
- after_refresh_token
- after_reset_password
- after_sms_confirm
- after_sms_disable
- after_webauthn_remove
- after_webauthn_setup
- allow_email_auth?
- allow_resending_verify_account_email?
- already_logged_in
- argon2_hash_algorithm?
- argon2_hash_cost
- argon2_password_hash_match?
- argon2_password_hash_using_salt_and_secret
- 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
- before_otp_setup_route
- before_reset_password
- before_reset_password_request
- before_rodauth
- before_sms_confirm
- 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
- before_webauthn_setup_route
- button
- button_fixed_locals
- 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
- clear_tokens
- close_account
- close_account_requires_password?
- compute_hmac
- compute_hmacs
- compute_old_hmac
- compute_raw_hmac
- compute_raw_hmac_with_secret
- confirm_password
- confirm_password_redirect
- convert_email_token_key
- convert_response_header_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_reset_password_key
- 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?
- hmac_secret_rotation?
- hook_action
- http_basic_auth
- inactive_session_cond
- include_success_messages?
- input_field_string
- inputmode_for_field?
- instance_variables_to_inspect
- internal_request?
- internal_request_configuration
- invalid_login_attempted
- invalid_previous_password_message
- json_request?
- json_response
- json_response_error?
- 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_confirmation_matches?
- login_does_not_meet_requirements_message
- login_failed_reset_password_request_form
- login_field_autocomplete_value
- login_form_footer_links
- login_hidden_field
- login_input_type
- login_meets_email_requirements?
- login_meets_length_requirements?
- login_meets_requirements?
- login_param_value
- 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_login
- normalize_session_or_flash_key
- null_byte_parameter_value
- only_json?
- open_account?
- otp_add_key
- otp_available?
- otp_exists?
- otp_hmac_old_secret
- otp_hmac_secret
- otp_issuer
- otp_key_ds
- otp_keys_use_hmac?
- otp_last_use
- otp_locked_out?
- otp_lockout_redirect
- 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_unlock_auth_failure
- otp_unlock_auth_success
- otp_unlock_available?
- otp_unlock_data
- otp_unlock_deadline
- otp_unlock_deadline_passed?
- otp_unlock_ds
- otp_unlock_next_auth_attempt_after
- otp_unlock_not_available_set_refresh_header
- otp_unlock_num_successes
- otp_unlock_refresh_tag
- otp_unlock_reset
- otp_unlock_success_cooldown_seconds
- 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
- remember_key_ds
- remember_login
- remembered_session_id
- remove_active_session
- remove_all_active_sessions
- remove_all_active_sessions_except_current
- remove_all_active_sessions_except_for
- 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_login_redirect
- require_otp_setup
- require_password_authentication
- require_password_authentication?
- require_response
- 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_account_status_value
- reset_password_email_link
- reset_password_email_recently_sent?
- reset_password_key_insert_hash
- reset_password_request_for_unverified_account
- 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_response_header
- 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
- show_otp_auth_link?
- single_session_ds
- skip_login_field_on_login?
- skip_password_field_on_login?
- skip_status_checks?
- sms
- 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_needs_confirmation_notice_flash
- sms_new_auth_code
- sms_new_confirm_code
- sms_normalize_phone
- sms_phone
- sms_record_failure
- sms_remove_expired_confirm_code
- 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
- throw_rodauth_error
- 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_partially_authenticated?
- 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?
- unlock_account_key
- unlock_account_key_value
- 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?
- use_scope_clear_session?
- uses_instance_variables
- 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_allow
- webauthn_auth_additional_form_tags
- webauthn_auth_credential_from_form_submission
- webauthn_auth_data
- webauthn_auth_form_path
- webauthn_authenticator_selection
- webauthn_create_relying_party_opts
- webauthn_credential_options_for_get
- webauthn_extensions
- webauthn_form_submission_call
- webauthn_key_insert_hash
- webauthn_keys_ds
- webauthn_login_options?
- webauthn_login_verification_factor?
- webauthn_origin
- webauthn_relying_party
- webauthn_remove_authenticated_session
- webauthn_rp_id
- webauthn_rp_name
- webauthn_setup?
- webauthn_setup_credential_from_form_submission
- webauthn_setup_data
- webauthn_update_session
- webauthn_user_ids_ds
- webauthn_user_name
- webauthn_user_verification
Classes and Modules
Constants
| FEATURES | = | {} | ||
| INVALID_DOMAIN | = | "invalidurl @@.com" | ||
| MAJOR | = | 2 |
The major version of |
|
| MINOR | = | 45 |
The minor version of |
|
| OPTS | = | {}.freeze | ||
| SCOPE_INSTANCE_VARIABLES | = | [:@_rodauths, :@_rodauth].freeze | ||
| 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. |
| webauthn_get_relying_party_opts | -> | webauthn_create_relying_party_opts |
Public Class methods
# File lib/rodauth.rb 51 def self.configure(app, opts=OPTS, &block) 52 json_opt = app.opts[:rodauth_json] = opts.fetch(:json, app.opts[:rodauth_json]) 53 csrf = app.opts[:rodauth_csrf] = opts.fetch(:csrf, app.opts[:rodauth_csrf]) 54 app.opts[:rodauth_route_csrf] = case csrf 55 when false, :rack_csrf 56 false 57 else 58 json_opt != :only 59 end 60 auth_class = (app.opts[:rodauths] ||= {})[opts[:name]] ||= opts[:auth_class] || Class.new(Auth) 61 if !auth_class.roda_class 62 auth_class.roda_class = app 63 elsif auth_class.roda_class != app 64 auth_class = app.opts[:rodauths][opts[:name]] = Class.new(auth_class) 65 auth_class.roda_class = app 66 end 67 auth_class.class_eval{@configuration_name = opts[:name] unless defined?(@configuration_name)} 68 auth_class.configure(&block) if block 69 auth_class.send(:make_shape_friendly) 70 auth_class.allocate.post_configure if auth_class.method_defined?(:post_configure) 71 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 11 def self.lib(opts=OPTS, &block) 12 require 'roda' 13 c = Class.new(Roda) 14 c.plugin(:rodauth, opts) do 15 enable :internal_request 16 instance_exec(&block) 17 end 18 c.freeze 19 c.rodauth 20 end
# File lib/rodauth.rb 22 def self.load_dependencies(app, opts=OPTS, &_) 23 json_opt = opts.fetch(:json, app.opts[:rodauth_json]) 24 if json_opt 25 app.plugin :json 26 app.plugin :json_parser 27 end 28 29 unless json_opt == :only 30 unless opts[:render] == false 31 require 'tilt/string' 32 app.plugin :render 33 end 34 35 case opts.fetch(:csrf, app.opts[:rodauth_csrf]) 36 when false 37 # nothing 38 when :rack_csrf 39 # :nocov: 40 app.plugin :csrf 41 # :nocov: 42 else 43 app.plugin :route_csrf 44 end 45 46 app.plugin :flash unless opts[:flash] == false 47 app.plugin :h 48 end 49 end
# File lib/rodauth/features/base.rb 174 def initialize(scope) 175 @scope = scope 176 _initialize_instance_variables 177 end
Public Instance methods
# File lib/rodauth/features/email_auth.rb 238 def _account_from_email_auth_key(token) 239 account_from_key(token, account_open_status_value){|id| get_email_auth_key(id)} 240 end
# File lib/rodauth/features/base.rb 847 def _account_from_id(id, status_id=nil) 848 ds = account_ds(id) 849 ds = ds.where(account_status_column=>status_id) if status_id && !skip_status_checks? 850 ds.first 851 end
# File lib/rodauth/features/base.rb 834 def _account_from_login(login) 835 ds = account_table_ds.where(login_column=>login) 836 ds = ds.select(*account_select) if account_select 837 ds = ds.where(account_status_column=>[account_unverified_status_value, account_open_status_value]) unless skip_status_checks? 838 ds.first 839 end
# File lib/rodauth/features/jwt_refresh.rb 118 def _account_from_refresh_token(token) 119 id, token_id, key = _account_refresh_token_split(token) 120 121 unless key && 122 (id.to_s == session_value.to_s) && 123 (actual = get_active_refresh_token(id, token_id)) && 124 (timing_safe_eql?(key, convert_token_key(actual)) || (hmac_secret_rotation? && timing_safe_eql?(key, compute_old_hmac(actual)))) && 125 jwt_refresh_token_match?(key) 126 return 127 end 128 129 ds = account_ds(id) 130 ds = ds.where(account_session_status_filter) unless skip_status_checks? 131 ds.first 132 end
# File lib/rodauth/features/reset_password.rb 263 def _account_from_reset_password_key(token) 264 account_from_key(token, reset_password_account_status_value){|id| get_password_reset_key(id)} 265 end
# File lib/rodauth/features/base.rb 841 def _account_from_session 842 ds = account_ds(session_value) 843 ds = ds.where(account_session_status_filter) unless skip_status_checks? 844 ds.first 845 end
# File lib/rodauth/features/lockout.rb 312 def _account_from_unlock_key(token) 313 account_from_key(token){|id| account_lockouts_ds(id).get(account_lockouts_key_column)} 314 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 134 def _account_refresh_token_split(token) 135 id, token = split_token(token) 136 id = convert_token_id(id) 137 return unless id && token 138 139 token_id, key = split_token(token) 140 token_id = convert_token_id(token_id) 141 return unless token_id && key 142 143 [id, token_id, key] 144 end
# File lib/rodauth/features/base.rb 591 def _around_rodauth 592 yield 593 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 email_auth_email_sent_response 200 end
# File lib/rodauth/features/base.rb 866 def _field_attributes(field) 867 nil 868 end
# File lib/rodauth/features/base.rb 870 def _field_error_attributes(field) 871 " aria-invalid=\"true\" aria-describedby=\"#{field}_error_message\" " 872 end
# File lib/rodauth/features/base.rb 961 def _filter_links(links) 962 links.select!{|_, link| link} 963 links.sort! 964 links 965 end
# File lib/rodauth/features/base.rb 874 def _formatted_field_error(field, error) 875 "<span class=\"#{input_field_error_message_class}\" id=\"#{field}_error_message\">#{error}</span>" 876 end
# File lib/rodauth/features/remember.rb 239 def _get_remember_cookie 240 request.cookies[remember_cookie_key] 241 end
# File lib/rodauth/features/base.rb 588 def _initialize_instance_variables 589 end
# File lib/rodauth/features/json.rb 224 def _json_response_body(hash) 225 request.send(:convert_to_json, hash) 226 end
# File lib/rodauth/features/jwt.rb 106 def _jwt_decode_opts 107 jwt_decode_opts 108 end
# File lib/rodauth/features/jwt.rb 111 def _jwt_decode_secrets 112 secrets = [jwt_secret, jwt_old_secret] 113 secrets.compact! 114 secrets 115 end
# File lib/rodauth/features/login.rb 178 def _login(auth_type) 179 warn("Deprecated #_login method called, use #login instead.") 180 login(auth_type) 181 end
# File lib/rodauth/features/login.rb 158 def _login_response 159 set_notice_flash login_notice_flash 160 redirect(saved_login_redirect || login_redirect) 161 end
# File lib/rodauth/features/base.rb 1020 def _merge_fixed_locals_opts(opts, fixed_locals) 1021 if use_template_fixed_locals? && !opts[:locals] 1022 fixed_locals_opts = {default_fixed_locals: fixed_locals} 1023 fixed_locals_opts.merge!(opts[:template_opts]) if opts[:template_opts] 1024 opts[:template_opts] = fixed_locals_opts 1025 end 1026 end
# File lib/rodauth/features/email_auth.rb 179 def _multi_phase_login_forms 180 forms = super 181 forms << [30, email_auth_request_form, :_email_auth_request] if valid_login_entered? && allow_email_auth? 182 forms 183 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 448 def _otp 449 _otp_for_key(otp_user_key) 450 end
# File lib/rodauth/features/otp.rb 433 def _otp_add_key(secret) 434 # Uniqueness errors can't be handled here, as we can't be sure the secret provided 435 # is the same as the current secret. 436 otp_key_ds.insert(otp_keys_id_column=>session_value, otp_keys_column=>secret) 437 end
# File lib/rodauth/features/otp.rb 444 def _otp_for_key(key) 445 otp_class.new(key, :issuer=>otp_issuer, :digits=>otp_digits, :interval=>otp_interval) 446 end
# File lib/rodauth/features/otp.rb 425 def _otp_interval 426 otp_interval || 30 427 end
# File lib/rodauth/features/otp.rb 439 def _otp_key 440 @otp_user_key = nil 441 otp_key_ds.get(otp_keys_column) || false 442 end
# File lib/rodauth/features/otp.rb 419 def _otp_tmp_key(secret) 420 @otp_tmp_key = true 421 @otp_user_key = nil 422 @otp_key = secret 423 end
# File lib/rodauth/features/otp.rb 264 def _otp_valid_code?(ot_pass, otp) 265 return false unless otp_exists? 266 ot_pass = ot_pass.gsub(/\s+/, '') 267 if drift = otp_drift 268 if otp.respond_to?(:verify_with_drift) 269 # :nocov: 270 otp.verify_with_drift(ot_pass, drift) 271 # :nocov: 272 else 273 otp.verify(ot_pass, :drift_behind=>drift, :drift_ahead=>drift) 274 end 275 else 276 otp.verify(ot_pass) 277 end 278 end
Called for valid OTP codes for old secrets
# File lib/rodauth/features/otp.rb 430 def _otp_valid_code_for_old_secret 431 end
# File lib/rodauth/features/webauthn.rb 443 def _override_webauthn_credential_response_verify(webauthn_credential) 444 # no need to override 445 end
# File lib/rodauth/features/base.rb 595 def _process_raw_hmac(hmac) 596 s = [hmac].pack('m') 597 s.chomp!("=\n") 598 s.tr!('+/', '-_') 599 s 600 end
# File lib/rodauth/features/recovery_codes.rb 262 def _recovery_codes 263 recovery_codes_ds.select_map(recovery_codes_column) 264 end
# File lib/rodauth/features/json.rb 214 def _return_json_response 215 response.status ||= json_response_error_status if json_response_error? 216 response.headers[convert_response_header_key('content-type')] ||= json_response_content_type 217 return_response _json_response_body(json_response) 218 end
# File lib/rodauth/features/json.rb 84 def _set_otp_unlock_info 85 if use_json? 86 json_response[:num_successes] = otp_unlock_num_successes 87 json_response[:required_successes] = otp_unlock_auths_required 88 json_response[:next_attempt_after] = otp_unlock_next_auth_attempt_after.to_i 89 end 90 end
# File lib/rodauth/features/remember.rb 188 def _set_remember_cookie(account_id, remember_key_value, deadline) 189 opts = Hash[remember_cookie_options] 190 opts[:value] = "#{account_id}_#{convert_token_key(remember_key_value)}" 191 opts[:expires] = convert_timestamp(deadline) 192 opts[:path] = "/" unless opts.key?(:path) 193 opts[:httponly] = true unless opts.key?(:httponly) || opts.key?(:http_only) 194 opts[:secure] = true unless opts.key?(:secure) || !request.ssl? 195 ::Rack::Utils.set_cookie_header!(response.headers, remember_cookie_key, opts) 196 end
# File lib/rodauth/features/lockout.rb 172 def _setup_account_lockouts_hash(account_id, key) 173 hash = {account_lockouts_id_column=>account_id, account_lockouts_key_column=>key} 174 set_deadline_value(hash, account_lockouts_deadline_column, account_lockouts_deadline_interval) 175 hash 176 end
# File lib/rodauth/features/sms_codes.rb 473 def _sms_request_response 474 set_notice_flash sms_request_notice_flash 475 redirect sms_auth_redirect 476 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 1030 def _template_opts(opts, page) 1031 opts = scope.send(:find_template, scope.send(:parse_template_opts, page, opts)) 1032 unless File.file?(scope.send(:template_path, opts)) 1033 opts[:path] = template_path(page) 1034 end 1035 opts 1036 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/two_factor_base.rb 250 def _two_factor_auth_response 251 saved_two_factor_auth_redirect = remove_session_value(two_factor_auth_redirect_session_key) 252 set_notice_flash two_factor_auth_notice_flash 253 redirect(saved_two_factor_auth_redirect || two_factor_auth_redirect) 254 end
# File lib/rodauth/features/otp.rb 367 def _two_factor_remove_all_from_session 368 two_factor_remove_session('totp') 369 super 370 end
# File lib/rodauth/features/otp.rb 361 def _two_factor_remove_links 362 links = super 363 links << [20, otp_disable_path, otp_disable_link_text] if otp_exists? 364 links 365 end
# File lib/rodauth/features/otp.rb 355 def _two_factor_setup_links 356 links = super 357 links << [20, otp_setup_path, otp_setup_link_text] unless otp_exists? 358 links 359 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 change_made = updated && !raised 90 clear_tokens(:change_login) if change_made 91 change_made 92 end
# File lib/rodauth/features/base.rb 1038 def _view(meth, page) 1039 unless scope.respond_to?(meth) 1040 raise ConfigurationError, "attempted to render a built-in view/email template (#{page.inspect}), but rendering is disabled" 1041 end 1042 1043 scope.send(meth, _view_opts(page)) 1044 end
# File lib/rodauth/features/base.rb 1010 def _view_opts(page) 1011 opts = template_opts.dup 1012 _merge_fixed_locals_opts(opts, '(rodauth: self.rodauth)') 1013 opts[:locals] = opts[:locals] ? opts[:locals].dup : {} 1014 opts[:locals][:rodauth] = self 1015 opts[:cache] = cache_templates 1016 opts[:cache_key] = :"rodauth_#{page}" 1017 _template_opts(opts, page) 1018 end
# File lib/rodauth/features/base.rb 416 def account! 417 account || (session_value && account_from_session) 418 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 886 def account_ds(id=account_id) 887 raise ArgumentError, "invalid account id passed to account_ds" unless id 888 ds = account_table_ds.where(account_id_column=>id) 889 ds = ds.select(*account_select) if account_select 890 ds 891 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/base.rb 424 def account_from_id(id, status_id=nil) 425 @account = _account_from_id(id, status_id) 426 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 (hmac_secret && timing_safe_eql?(key, convert_email_token_key(actual))) || 73 (hmac_secret_rotation? && timing_safe_eql?(key, compute_old_hmac(actual))) || 74 ((!hmac_secret || allow_raw_email_token?) && timing_safe_eql?(key, actual)) 75 return 76 end 77 _account_from_id(id, status_id) 78 end
# File lib/rodauth/features/base.rb 311 def account_from_login(login) 312 @account = _account_from_login(login) 313 end
# File lib/rodauth/features/jwt_refresh.rb 90 def account_from_refresh_token(token) 91 @account = _account_from_refresh_token(token) 92 end
# File lib/rodauth/features/remember.rb 219 def account_from_remember_cookie 220 unless id = remembered_session_id 221 # Only set expired cookie if there is already a cookie set. 222 forget_login if _get_remember_cookie 223 return 224 end 225 226 set_session_value(session_key, id) 227 account_from_session 228 remove_session_value(session_key) 229 230 unless account 231 remove_remember_key(id) 232 forget_login 233 return 234 end 235 236 account 237 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 420 def account_from_session 421 @account = _account_from_session 422 end
# File lib/rodauth/features/lockout.rb 223 def account_from_unlock_key(key) 224 @account = _account_from_unlock_key(key) 225 end
# File lib/rodauth/features/verify_account.rb 198 def account_from_verify_account_key(key) 199 @account = _account_from_verify_account_key(key) 200 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/webauthn_autofill.rb 45 def account_from_webauthn_login 46 return super if param_or_nil(login_param) 47 48 credential_id = webauthn_auth_data["id"] 49 account_id = db[webauthn_keys_table] 50 .where(webauthn_keys_webauthn_id_column => credential_id) 51 .get(webauthn_keys_account_id_column) 52 53 unless account_id 54 throw_error_reason(:invalid_webauthn_id, invalid_field_error_status, webauthn_auth_param, webauthn_invalid_webauthn_id_message) 55 end 56 57 account_from_id(account_id) 58 end
# File lib/rodauth/features/base.rb 301 def account_id 302 account[account_id_column] 303 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 412 def account_initial_status_value 413 account_open_status_value 414 end
# File lib/rodauth/features/lockout.rb 308 def account_lockouts_ds(id=account_id) 309 db[account_lockouts_table].where(account_lockouts_id_column=>id) 310 end
# File lib/rodauth/features/lockout.rb 304 def account_login_failures_ds 305 db[account_login_failures_table].where(account_login_failures_id_column=>account_id) 306 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 335 def account_password_hash_column 336 nil 337 end
# File lib/rodauth/features/base.rb 878 def account_session_status_filter 879 {account_status_column=>account_open_status_value} 880 end
# File lib/rodauth/features/base.rb 893 def account_table_ds 894 db[accounts_table] 895 end
# File lib/rodauth/features/webauthn.rb 271 def account_webauthn_ids 272 webauthn_keys_ds.select_map(webauthn_keys_webauthn_id_column) 273 end
# File lib/rodauth/features/webauthn.rb 275 def account_webauthn_usage 276 webauthn_keys_ds.select_hash(webauthn_keys_webauthn_id_column, webauthn_keys_last_use_column) 277 end
# File lib/rodauth/features/webauthn.rb 279 def account_webauthn_user_id 280 unless webauthn_id = webauthn_user_ids_ds.get(webauthn_user_ids_webauthn_id_column) 281 webauthn_id = WebAuthn.generate_user_id 282 if e = raised_uniqueness_violation do 283 webauthn_user_ids_ds.insert( 284 webauthn_user_ids_account_id_column => webauthn_account_id, 285 webauthn_user_ids_webauthn_id_column => webauthn_id 286 ) 287 end 288 # If two requests to create a webauthn user id are sent at the same time and an insert 289 # is attempted for both, one will fail with a unique constraint violation. In that case 290 # it is safe for the second one to use the webauthn user id inserted by the other request. 291 # If there is still no webauthn user id at this point, then we'll just reraise the 292 # exception. 293 # :nocov: 294 raise e unless webauthn_id = webauthn_user_ids_ds.get(webauthn_user_ids_webauthn_id_column) 295 # :nocov: 296 end 297 end 298 299 webauthn_id 300 end
# File lib/rodauth/features/remember.rb 267 def active_remember_key_ds(id=account_id) 268 remember_key_ds(id).where(Sequel.expr(remember_deadline_column) > Sequel::CURRENT_TIMESTAMP) 269 end
# File lib/rodauth/features/active_sessions.rb 236 def active_sessions_ds 237 db[active_sessions_table]. 238 where(active_sessions_account_id_column=>session_value || account_id) 239 end
# File lib/rodauth/features/active_sessions.rb 199 def active_sessions_insert_hash 200 {active_sessions_account_id_column => session_value, active_sessions_session_id_column => compute_hmac(active_sessions_key)} 201 end
# File lib/rodauth/features/active_sessions.rb 203 def active_sessions_update_hash 204 h = {active_sessions_last_use_column => Sequel::CURRENT_TIMESTAMP} 205 206 if hmac_secret_rotation? 207 h[active_sessions_session_id_column] = compute_hmac(session[session_id_session_key]) 208 end 209 210 h 211 end
# File lib/rodauth/features/active_sessions.rb 70 def add_active_session 71 key = generate_active_sessions_key 72 set_session_value(session_id_session_key, key) 73 if e = raises_uniqueness_violation?{active_sessions_ds.insert(active_sessions_insert_hash)} 74 handle_duplicate_active_session_id(e) 75 end 76 nil 77 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 216 def add_field_error_class(field) 217 if field_error(field) 218 " #{input_field_error_class}" 219 end 220 end
# File lib/rodauth/features/disallow_password_reuse.rb 27 def add_previous_password_hash(hash) 28 ds = previous_password_ds 29 30 unless @dont_check_previous_password 31 keep_before = ds.reverse(previous_password_id_column). 32 limit(nil, previous_passwords_to_check). 33 get(previous_password_id_column) 34 35 if keep_before 36 ds.where(Sequel.expr(previous_password_id_column) <= keep_before). 37 delete 38 end 39 end 40 41 # This should never raise uniqueness violations, as it uses a serial primary key 42 ds.insert(previous_password_account_id_column=>account_id, previous_password_hash_column=>hash) 43 end
# File lib/rodauth/features/recovery_codes.rb 189 def add_recovery_code 190 # This should never raise uniqueness violations unless the recovery code is the same, and the odds of that 191 # are 1/256**32 assuming a good random number generator. Still, attempt to handle that case by retrying 192 # on such a uniqueness violation. 193 retry_on_uniqueness_violation do 194 recovery_codes_ds.insert(recovery_codes_id_column=>session_value, recovery_codes_column=>new_recovery_code) 195 end 196 end
# File lib/rodauth/features/recovery_codes.rb 179 def add_recovery_codes(number) 180 return if number <= 0 181 transaction do 182 number.times do 183 add_recovery_code 184 end 185 end 186 @recovery_codes = nil 187 end
# File lib/rodauth/features/remember.rb 161 def add_remember_key 162 hash = {remember_id_column=>account_id, remember_key_column=>remember_key_value} 163 set_deadline_value(hash, remember_deadline_column, remember_deadline_interval) 164 165 if e = raised_uniqueness_violation{remember_key_ds.insert(hash)} 166 # If inserting into the remember key table causes a violation, we can pull the 167 # existing row from the table. If there is no invalid row, we can then reraise. 168 raise e unless @remember_key_value = active_remember_key_ds.get(remember_key_column) 169 end 170 end
# File lib/rodauth/features/recovery_codes.rb 153 def add_webauthn_credential(_) 154 super if defined?(super) 155 auto_add_missing_recovery_codes 156 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 89 def after_create_account 90 if account_password_hash_column && !(respond_to?(:verify_account_set_password?) && verify_account_set_password?) 91 add_previous_password_hash(password_hash(param(password_param))) 92 end 93 super if defined?(super) 94 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 if force_email_auth? 154 155 super 156 end
# File lib/rodauth/features/lockout.rb 275 def after_login_failure 276 invalid_login_attempted 277 super 278 end
# File lib/rodauth/features/remember.rb 243 def after_logout 244 forget_login 245 super if defined?(super) 246 end
# File lib/rodauth/features/otp_lockout_email.rb 18 def after_otp_authentication_failure 19 super 20 21 if otp_locked_out? && send_otp_locked_out_email? 22 send_otp_locked_out_email 23 end 24 end
# File lib/rodauth/features/otp_modify_email.rb 18 def after_otp_disable 19 super 20 send_otp_disabled_email 21 end
# File lib/rodauth/features/active_sessions.rb 153 def after_otp_setup 154 super if defined?(super) 155 remove_all_active_sessions_except_current if @clear_active_sessions_after_two_factor_setup 156 end
# File lib/rodauth/features/json.rb 100 def after_otp_unlock_auth_failure 101 super if defined?(super) 102 _set_otp_unlock_info 103 end
# File lib/rodauth/features/json.rb 92 def after_otp_unlock_auth_success 93 super if defined?(super) 94 if otp_locked_out? 95 _set_otp_unlock_info 96 json_response[:deadline] = otp_unlock_deadline.to_i 97 end 98 end
# File lib/rodauth/features/json.rb 105 def after_otp_unlock_not_yet_available 106 super if defined?(super) 107 _set_otp_unlock_info 108 end
# File lib/rodauth/features/active_sessions.rb 137 def after_refresh_token 138 super if defined?(super) 139 if prev_key = session[session_id_session_key] 140 key = generate_active_sessions_key 141 set_session_value(session_id_session_key, key) 142 active_sessions_ds. 143 where(active_sessions_session_id_column => compute_hmacs(prev_key)). 144 update(active_sessions_session_id_column => compute_hmac(key)) 145 end 146 end
# File lib/rodauth/features/password_grace_period.rb 41 def after_reset_password 42 super if defined?(super) 43 @last_password_entry = Time.now.to_i 44 end
# File lib/rodauth/features/active_sessions.rb 158 def after_sms_confirm 159 super if defined?(super) 160 remove_all_active_sessions_except_current if @clear_active_sessions_after_two_factor_setup 161 end
# File lib/rodauth/features/recovery_codes.rb 232 def after_sms_disable 233 super if defined?(super) 234 auto_remove_recovery_codes 235 end
# File lib/rodauth/features/recovery_codes.rb 237 def after_webauthn_remove 238 super if defined?(super) 239 auto_remove_recovery_codes 240 end
# File lib/rodauth/features/active_sessions.rb 163 def after_webauthn_setup 164 super if defined?(super) 165 remove_all_active_sessions_except_current if @clear_active_sessions_after_two_factor_setup 166 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 165 def allow_resending_verify_account_email? 166 account[account_status_column] == account_unverified_status_value 167 end
# File lib/rodauth/features/base.rb 343 def already_logged_in 344 nil 345 end
:nocov:
# File lib/rodauth/features/argon2.rb 107 def argon2_hash_algorithm?(hash) 108 hash.start_with?('$argon2id$') 109 end
# File lib/rodauth/features/argon2.rb 78 def argon2_hash_cost 79 { t_cost: 1, m_cost: 5, p_cost: 1 } 80 end
# File lib/rodauth/features/argon2.rb 111 def argon2_password_hash_match?(hash, password) 112 ret = ::Argon2::Password.verify_password(password, hash, argon2_secret) 113 114 if ret == false && argon2_old_secret != argon2_secret && (ret = ::Argon2::Password.verify_password(password, hash, argon2_old_secret)) 115 @update_password_hash = true 116 end 117 118 ret 119 end
# File lib/rodauth/features/argon2.rb 62 def argon2_password_hash_using_salt_and_secret(password, salt, secret) 63 argon2_params = Hash[extract_password_hash_cost(salt)] 64 argon2_params[argon2_salt_option] = salt.split('$').last.unpack("m")[0] 65 argon2_params[:secret] = secret 66 ::Argon2::Password.new(argon2_params).create(password) 67 end
# File lib/rodauth/features/argon2.rb 36 def argon2_salt_option 37 :salt_do_not_supply 38 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 151 def auth_class_eval(&block) 152 auth.class_eval(&block) 153 end
# File lib/rodauth/features/base.rb 399 def authenticated? 400 logged_in? 401 end
# File lib/rodauth/features/base.rb 503 def authenticated_by 504 session[authenticated_by_session_key] 505 end
# File lib/rodauth/features/webauthn.rb 251 def authenticated_webauthn_id 252 session[authenticated_webauthn_id_session_key] 253 end
# File lib/rodauth/features/recovery_codes.rb 250 def auto_add_missing_recovery_codes 251 if auto_add_recovery_codes? 252 add_recovery_codes(recovery_codes_limit - recovery_codes.length) 253 end 254 end
# File lib/rodauth/features/recovery_codes.rb 256 def auto_remove_recovery_codes 257 if auto_remove_recovery_codes? && (%w'totp webauthn sms_code' & possible_authentication_methods).empty? 258 recovery_codes_remove 259 end 260 end
# File lib/rodauth/features/base.rb 246 def autocomplete_for_field?(_param) 247 mark_input_fields_with_autocomplete? 248 end
# File lib/rodauth/features/base.rb 516 def autologin_session(autologin_type) 517 login_session('autologin') 518 set_session_value(autologin_type_session_key, autologin_type) 519 end
# File lib/rodauth/features/base.rb 512 def autologin_type 513 session[autologin_type_session_key] 514 end
# File lib/rodauth/features/otp.rb 414 def base32_encode(data, length) 415 chars = 'abcdefghijklmnopqrstuvwxyz234567' 416 length.times.map{|i|chars[data[i].ord % 32]}.join 417 end
# File lib/rodauth/features/base.rb 562 def base_url 563 url = String.new("#{request.scheme}://#{domain}") 564 url << ":#{request.port}" if request.port != Rack::Request::DEFAULT_PORTS[request.scheme] 565 url 566 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 79 def before_create_account_route 80 super if defined?(super) 81 @dont_check_previous_password = true 82 end
# File lib/rodauth/features/lockout.rb 263 def before_login_attempt 264 if locked_out? 265 show_lockout_page 266 end 267 super 268 end
# File lib/rodauth/features/active_sessions.rb 168 def before_logout 169 if param_or_nil(global_logout_param) 170 remove_remember_key(session_value) if respond_to?(:remove_remember_key) 171 remove_all_active_sessions 172 else 173 remove_current_session 174 end 175 super 176 end
# File lib/rodauth/features/active_sessions.rb 178 def before_otp_setup 179 @clear_active_sessions_after_two_factor_setup = !two_factor_authentication_setup? 180 super if defined?(super) 181 end
# File lib/rodauth/features/json.rb 174 def before_otp_setup_route 175 super if defined?(super) 176 if use_json? && otp_keys_use_hmac? && !param_or_nil(otp_setup_raw_param) 177 _otp_tmp_key(otp_new_secret) 178 json_response[otp_setup_param] = otp_user_key 179 json_response[otp_setup_raw_param] = otp_key 180 end 181 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 183 def before_rodauth 184 if json_request? 185 if json_check_accept? && (accept = request.env['HTTP_ACCEPT']) && accept !~ json_accept_regexp 186 response.status = 406 187 json_response[json_response_error_key] = json_not_accepted_error_message 188 _return_json_response 189 end 190 191 unless request.post? 192 response.status = 405 193 set_response_header('allow', 'POST') 194 json_response[json_response_error_key] = json_non_post_error_message 195 return_json_response 196 end 197 elsif only_json? 198 response.status = json_response_error_status 199 return_response non_json_request_error_message 200 end 201 202 super 203 end
# File lib/rodauth/features/active_sessions.rb 183 def before_sms_confirm 184 @clear_active_sessions_after_two_factor_setup = !two_factor_authentication_setup? 185 super if defined?(super) 186 end
# File lib/rodauth/features/json.rb 120 def before_two_factor_auth_route 121 super if defined?(super) 122 if use_json? 123 json_response[:auth_links] = two_factor_auth_links.sort.map{|_,link| link} 124 json_response[json_response_success_key] ||= "" if include_success_messages? 125 return_json_response 126 end 127 end
# File lib/rodauth/features/json.rb 110 def before_two_factor_manage_route 111 super if defined?(super) 112 if use_json? 113 json_response[:setup_links] = two_factor_setup_links.sort.map{|_,link| link} 114 json_response[:remove_links] = two_factor_remove_links.sort.map{|_,link| link} 115 json_response[json_response_success_key] ||= "" if include_success_messages? 116 return_json_response 117 end 118 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 32 def before_verify_account 33 super 34 if features.include?(:json) && use_json? && !param_or_nil(webauthn_setup_param) 35 cred = new_webauthn_credential 36 json_response[webauthn_setup_param] = cred.as_json 37 json_response[webauthn_setup_challenge_param] = cred.challenge 38 json_response[webauthn_setup_challenge_hmac_param] = compute_hmac(cred.challenge) 39 end 40 @webauthn_credential = webauthn_setup_credential_from_form_submission 41 add_webauthn_credential(@webauthn_credential) 42 end
# File lib/rodauth/features/disallow_password_reuse.rb 84 def before_verify_account_route 85 super if defined?(super) 86 @dont_check_previous_password = true 87 end
# File lib/rodauth/features/json.rb 129 def before_view_recovery_codes 130 super if defined?(super) 131 if use_json? 132 json_response[:codes] = recovery_codes 133 json_response[json_response_success_key] ||= "" if include_success_messages? 134 end 135 end
# File lib/rodauth/features/json.rb 147 def before_webauthn_auth_route 148 super if defined?(super) 149 if use_json? && !param_or_nil(webauthn_auth_param) 150 cred = webauthn_credential_options_for_get 151 json_response[webauthn_auth_param] = cred.as_json 152 json_response[webauthn_auth_challenge_param] = cred.challenge 153 json_response[webauthn_auth_challenge_hmac_param] = compute_hmac(cred.challenge) 154 end 155 end
# File lib/rodauth/features/json.rb 157 def before_webauthn_login_route 158 super if defined?(super) 159 if use_json? && !param_or_nil(webauthn_auth_param) && webauthn_login_options? 160 cred = webauthn_credential_options_for_get 161 json_response[webauthn_auth_param] = cred.as_json 162 json_response[webauthn_auth_challenge_param] = cred.challenge 163 json_response[webauthn_auth_challenge_hmac_param] = compute_hmac(cred.challenge) 164 end 165 end
# File lib/rodauth/features/json.rb 167 def before_webauthn_remove_route 168 super if defined?(super) 169 if use_json? && !param_or_nil(webauthn_remove_param) 170 json_response[webauthn_remove_param] = account_webauthn_usage 171 end 172 end
# File lib/rodauth/features/active_sessions.rb 188 def before_webauthn_setup 189 @clear_active_sessions_after_two_factor_setup = !two_factor_authentication_setup? 190 super if defined?(super) 191 end
# File lib/rodauth/features/json.rb 137 def before_webauthn_setup_route 138 super if defined?(super) 139 if use_json? && !param_or_nil(webauthn_setup_param) 140 cred = new_webauthn_credential 141 json_response[webauthn_setup_param] = cred.as_json 142 json_response[webauthn_setup_challenge_param] = cred.challenge 143 json_response[webauthn_setup_challenge_hmac_param] = compute_hmac(cred.challenge) 144 end 145 end
# File lib/rodauth/features/base.rb 453 def button(value, opts=OPTS) 454 scope.render(button_opts(value, opts)) 455 end
# File lib/rodauth/features/base.rb 639 def button_fixed_locals 640 '(value:, opts:)' 641 end
# File lib/rodauth/features/base.rb 444 def button_opts(value, opts) 445 opts = Hash[template_opts].merge!(opts) 446 _merge_fixed_locals_opts(opts, button_fixed_locals) 447 opts[:locals] = {:value=>value, :opts=>opts} 448 opts[:cache] = cache_templates 449 opts[:cache_key] = :rodauth_button 450 _template_opts(opts, 'button') 451 end
# File lib/rodauth/features/recovery_codes.rb 175 def can_add_recovery_codes? 176 recovery_codes.length < recovery_codes_limit 177 end
# File lib/rodauth/features/base.rb 740 def catch_error(&block) 741 catch(:rodauth_error, &block) 742 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 56 def check_active_session 57 if logged_in? && !currently_active_session? 58 no_longer_active_session 59 end 60 end
# File lib/rodauth/features/base.rb 339 def check_already_logged_in 340 already_logged_in if logged_in? 341 end
# File lib/rodauth/features/base.rb 428 def check_csrf 429 scope.check_csrf!(check_csrf_opts, &check_csrf_block) 430 end
# File lib/rodauth/features/base.rb 802 def check_csrf? 803 scope.opts[:rodauth_route_csrf] 804 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 52 def check_single_session 53 if logged_in? && !currently_active_session? 54 no_longer_active_session 55 end 56 end
# File lib/rodauth/features/otp.rb 372 def clear_cached_otp 373 @otp = nil 374 end
# File lib/rodauth/features/lockout.rb 168 def clear_invalid_login_attempts 169 unlock_account 170 end
# File lib/rodauth/features/base.rb 355 def clear_session 356 if use_scope_clear_session? 357 scope.clear_session 358 else 359 session.clear 360 end 361 end
# File lib/rodauth/features/active_sessions.rb 130 def clear_tokens(reason) 131 super 132 remove_all_active_sessions_except_current 133 end
# File lib/rodauth/features/close_account.rb 67 def close_account 68 unless skip_status_checks? 69 update_account(account_status_column=>account_closed_status_value) 70 end 71 72 unless account_password_hash_column 73 password_hash_ds.delete 74 end 75 end
# File lib/rodauth/features/close_account.rb 63 def close_account_requires_password? 64 modifications_require_password? 65 end
Return urlsafe base64 HMAC for data, assumes hmac_secret is set.
# File lib/rodauth/features/base.rb 280 def compute_hmac(data) 281 _process_raw_hmac(compute_raw_hmac(data)) 282 end
Return array of hmacs. Array has two strings if hmac_old_secret is set, or one string otherwise.
# File lib/rodauth/features/base.rb 291 def compute_hmacs(data) 292 hmacs = [compute_hmac(data)] 293 294 if hmac_old_secret 295 hmacs << compute_old_hmac(data) 296 end 297 298 hmacs 299 end
Return urlsafe base64 HMAC for data using hmac_old_secret, assumes hmac_old_secret is set.
# File lib/rodauth/features/base.rb 285 def compute_old_hmac(data) 286 _process_raw_hmac(compute_raw_hmac_with_secret(data, hmac_old_secret)) 287 end
# File lib/rodauth/features/base.rb 857 def compute_raw_hmac(data) 858 raise ConfigurationError, "hmac_secret not set" unless hmac_secret 859 compute_raw_hmac_with_secret(data, hmac_secret) 860 end
# File lib/rodauth/features/base.rb 862 def compute_raw_hmac_with_secret(data, secret) 863 OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, secret, data) 864 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 607 def convert_response_header_key(key) 608 key 609 end
# File lib/rodauth/features/base.rb 719 def convert_session_key(key) 720 key = :"#{session_key_prefix}#{key}" if session_key_prefix 721 normalize_session_or_flash_key(key) 722 end
This is needed for jdbc/sqlite, which returns timestamp columns as strings
# File lib/rodauth/features/base.rb 902 def convert_timestamp(timestamp) 903 timestamp = db.to_application_timestamp(timestamp) if timestamp.is_a?(String) 904 timestamp 905 end
# File lib/rodauth/features/base.rb 672 def convert_token_id(id) 673 if convert_token_id_to_integer? 674 convert_token_id_to_integer(id) 675 else 676 id 677 end 678 end
# File lib/rodauth/features/base.rb 680 def convert_token_id_to_integer(id) 681 if id = (Integer(id, 10) rescue nil) 682 if id > 9223372036854775807 || id < -9223372036854775808 683 # Only allow 64-bit signed integer range to avoid problems on PostgreSQL 684 id = nil 685 end 686 end 687 688 id 689 end
# File lib/rodauth/features/base.rb 660 def convert_token_key(key) 661 if key && hmac_secret 662 compute_hmac(key) 663 else 664 key 665 end 666 end
# File lib/rodauth/features/verify_account.rb 218 def create_account_autologin? 219 false 220 end
# File lib/rodauth/features/verify_account.rb 185 def create_account_notice_flash 186 verify_account_email_sent_notice_flash 187 end
# File lib/rodauth/features/verify_account.rb 222 def create_account_set_password? 223 return false if verify_account_set_password? 224 super 225 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 165 def create_reset_password_key 166 transaction do 167 if reset_password_key_value = get_password_reset_key(account_id) 168 set_reset_password_email_last_sent 169 @reset_password_key_value = reset_password_key_value 170 elsif e = raised_uniqueness_violation{password_reset_ds.insert(reset_password_key_insert_hash)} 171 # If inserting into the reset password table causes a violation, we can pull the 172 # existing reset password key from the table, or reraise. 173 raise e unless @reset_password_key_value = get_password_reset_key(account_id) 174 end 175 end 176 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 432 def csrf_tag(path=request.path) 433 return unless scope.respond_to?(:csrf_tag) 434 435 if use_request_specific_csrf_tokens? 436 scope.csrf_tag(path) 437 else 438 # :nocov: 439 scope.csrf_tag 440 # :nocov: 441 end 442 end
# File lib/rodauth/features/active_sessions.rb 42 def currently_active_session? 43 return false unless session_id = session[session_id_session_key] 44 45 remove_inactive_sessions 46 ds = active_sessions_ds. 47 where(active_sessions_session_id_column => compute_hmacs(session_id)) 48 49 if update_current_session? 50 ds.update(active_sessions_update_hash) == 1 51 else 52 ds.count == 1 53 end 54 end
# File lib/rodauth/features/argon2.rb 121 def database_function_password_match?(name, hash_id, password, salt) 122 return true if super 123 124 if use_argon2? && argon2_hash_algorithm?(salt) && argon2_old_secret != argon2_secret && (ret = db.get(Sequel.function(function_name(name), hash_id, argon2_password_hash_using_salt_and_secret(password, salt, argon2_old_secret)))) 125 @update_password_hash = true 126 end 127 128 !!ret 129 end
# File lib/rodauth/features/base.rb 319 def db 320 Sequel::DATABASES.first or raise "Sequel database connection is missing" 321 end
# File lib/rodauth/features/close_account.rb 77 def delete_account 78 account_ds.delete 79 end
# File lib/rodauth/features/close_account.rb 81 def delete_account_on_close? 82 skip_status_checks? 83 end
# File lib/rodauth/features/remember.rb 157 def disable_remember_login 158 remove_remember_key 159 end
# File lib/rodauth/features/email_auth.rb 234 def email_auth_ds(id=account_id) 235 db[email_auth_table].where(email_auth_id_column=>id) 236 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 168 def email_auth_email_recently_sent? 169 (email_last_sent = get_email_auth_email_last_sent) && (Time.now - email_last_sent < email_auth_skip_resend_email_within) 170 end
# File lib/rodauth/features/email_auth.rb 228 def email_auth_key_insert_hash 229 hash = {email_auth_id_column=>account_id, email_auth_key_column=>email_auth_key_value} 230 set_deadline_value(hash, email_auth_deadline_column, email_auth_deadline_interval) 231 hash 232 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 214 def extend_remember_deadline 215 active_remember_key_ds.update(remember_deadline_column=>Sequel.date_add(Sequel::CURRENT_TIMESTAMP, remember_period)) 216 remember_login 217 end
# File lib/rodauth/features/remember.rb 202 def extend_remember_deadline_while_logged_in? 203 return false unless extend_remember_deadline? 204 205 if extended_at = session[remember_deadline_extended_session_key] 206 extended_at + extend_remember_deadline_period < Time.now.to_i 207 elsif logged_in_via_remember_key? 208 # Handle existing sessions before the change to extend remember deadline 209 # while logged in. 210 true 211 end 212 end
# File lib/rodauth/features/argon2.rb 70 def extract_password_hash_cost(hash) 71 return super unless argon2_hash_algorithm?(hash) 72 73 /\A\$argon2id\$v=\d+\$m=(\d+),t=(\d+),p=(\d+)/ =~ hash 74 { t_cost: $2.to_i, m_cost: Math.log2($1.to_i).to_i, p_cost: $3.to_i } 75 end
# File lib/rodauth/features/base.rb 179 def features 180 self.class.features 181 end
# File lib/rodauth/features/base.rb 254 def field_attributes(field) 255 _field_attributes(field) || default_field_attributes 256 end
# File lib/rodauth/features/base.rb 211 def field_error(field) 212 return nil unless @field_errors 213 @field_errors[field] 214 end
# File lib/rodauth/features/base.rb 258 def field_error_attributes(field) 259 if field_error(field) 260 _field_error_attributes(field) 261 end 262 end
# File lib/rodauth/features/remember.rb 140 def forget_login 141 opts = Hash[remember_cookie_options] 142 opts[:path] = "/" unless opts.key?(:path) 143 ::Rack::Utils.delete_cookie_header!(response.headers, remember_cookie_key, opts) 144 end
# File lib/rodauth/features/base.rb 264 def formatted_field_error(field) 265 if error = field_error(field) 266 _formatted_field_error(field, error) 267 end 268 end
# File lib/rodauth/features/base.rb 806 def function_name(name) 807 if db.database_type == :mssql 808 # :nocov: 809 "dbo.#{name}" 810 # :nocov: 811 else 812 name 813 end 814 end
# File lib/rodauth/features/active_sessions.rb 195 def generate_active_sessions_key 196 @active_sessions_key = random_key 197 end
# File lib/rodauth/features/email_auth.rb 220 def generate_email_auth_key_value 221 @email_auth_key_value = random_key 222 end
# File lib/rodauth/features/jwt_refresh.rb 192 def generate_refresh_token 193 hash = jwt_refresh_token_insert_hash 194 [account_id, jwt_refresh_token_ds.insert(hash), convert_token_key(hash[jwt_refresh_token_key_column])].join(token_separator) 195 end
# File lib/rodauth/features/remember.rb 255 def generate_remember_key_value 256 @remember_key_value = random_key 257 end
# File lib/rodauth/features/reset_password.rb 241 def generate_reset_password_key_value 242 @reset_password_key_value = random_key 243 end
# File lib/rodauth/features/lockout.rb 285 def generate_unlock_account_key 286 random_key 287 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 165 def get_active_refresh_token(account_id, token_id) 166 jwt_refresh_token_account_ds(account_id). 167 where(Sequel::CURRENT_TIMESTAMP > jwt_refresh_token_deadline_column). 168 delete 169 170 jwt_refresh_token_account_token_ds(account_id, token_id). 171 get(jwt_refresh_token_key_column) 172 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 822 def get_password_hash 823 if account_password_hash_column 824 account[account_password_hash_column] if account! 825 elsif use_database_authentication_functions? 826 db.get(Sequel.function(function_name(:rodauth_get_salt), account ? account_id : session_value)) 827 else 828 # :nocov: 829 password_hash_ds.get(password_hash_column) 830 # :nocov: 831 end 832 end
# File lib/rodauth/features/reset_password.rb 194 def get_password_reset_key(id) 195 # RODAUTH3: Remove method and call get_reset_password_key directly 196 get_reset_password_key(id) 197 end
# File lib/rodauth/features/remember.rb 146 def get_remember_key 147 unless @remember_key_value = active_remember_key_ds.get(remember_key_column) 148 generate_remember_key_value 149 transaction do 150 remove_remember_key 151 add_remember_key 152 end 153 end 154 nil 155 end
# File lib/rodauth/features/reset_password.rb 209 def get_reset_password_email_last_sent 210 if column = reset_password_email_last_sent_column 211 if ts = password_reset_ds.get(column) 212 convert_timestamp(ts) 213 end 214 end 215 end
# File lib/rodauth/features/reset_password.rb 199 def get_reset_password_key(id) 200 ds = password_reset_ds(id) 201 ds.where(Sequel::CURRENT_TIMESTAMP > reset_password_deadline_column).delete 202 ds.get(reset_password_key_column) 203 end
# File lib/rodauth/features/lockout.rb 231 def get_unlock_account_email_last_sent 232 if column = account_lockouts_email_last_sent_column 233 if ts = account_lockouts_ds.get(column) 234 convert_timestamp(ts) 235 end 236 end 237 end
# File lib/rodauth/features/lockout.rb 219 def get_unlock_account_key 220 account_lockouts_ds.get(account_lockouts_key_column) 221 end
# File lib/rodauth/features/verify_account.rb 231 def get_verify_account_email_last_sent 232 if column = verify_account_email_last_sent_column 233 if ts = verify_account_ds.get(column) 234 convert_timestamp(ts) 235 end 236 end 237 end
# File lib/rodauth/features/verify_account.rb 210 def get_verify_account_key(id) 211 verify_account_ds(id).get(verify_account_key_column) 212 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 79 def handle_duplicate_active_session_id(_e) 80 # Do nothing by default as session is already tracked. This will result in 81 # the current session and the existing session with the same id 82 # being tracked together, so that a logout of one will logout 83 # the other, and updating the last use on one will update the other, 84 # but this should be acceptable. However, this can be overridden if different 85 # behavior is desired. 86 end
# File lib/rodauth/features/webauthn.rb 352 def handle_webauthn_sign_count_verification_error 353 throw_error_reason(:invalid_webauthn_sign_count, invalid_field_error_status, webauthn_auth_param, webauthn_invalid_sign_count_message) 354 end
# File lib/rodauth/features/base.rb 580 def has_password? 581 return @has_password unless @has_password.nil? 582 return false unless account || session_value 583 @has_password = !!get_password_hash 584 end
# File lib/rodauth/features/base.rb 853 def hmac_secret_rotation? 854 hmac_secret && hmac_old_secret && hmac_secret != hmac_old_secret 855 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 36 def http_basic_auth 37 unless @checked_http_basic_auth.nil? 38 return (@checked_http_basic_auth ? true : nil) 39 end 40 41 @checked_http_basic_auth = false 42 return unless token = ((v = request.env['HTTP_AUTHORIZATION']) && v[/\A *Basic (.*)\Z/, 1]) 43 44 username, password = token.unpack("m*").first.split(/:/, 2) 45 return unless username && password 46 47 catch_error do 48 unless account_from_login(username) 49 after_no_matching_login 50 throw_basic_auth_error(login_param, no_matching_login_message) 51 end 52 53 before_login_attempt 54 55 unless open_account? 56 throw_basic_auth_error(login_param, no_matching_login_message) 57 end 58 59 unless password_match?(password) 60 after_login_failure 61 throw_basic_auth_error(password_param, invalid_password_message) 62 end 63 64 transaction do 65 before_login 66 login_session('password') 67 after_login 68 end 69 70 @checked_http_basic_auth = true 71 return true 72 end 73 74 nil 75 end
# File lib/rodauth/features/active_sessions.rb 225 def inactive_session_cond 226 cond = session_inactivity_deadline_condition 227 cond2 = session_lifetime_deadline_condition 228 return false unless cond || cond2 229 Sequel.|(*[cond, cond2].compact) 230 end
# File lib/rodauth/features/json.rb 220 def include_success_messages? 221 !json_response_success_key.nil? 222 end
# File lib/rodauth/features/base.rb 222 def input_field_string(param, id, opts=OPTS) 223 type = opts.fetch(:type, "text") 224 225 unless type == "password" 226 value = opts.fetch(:value){scope.h param(param)} 227 end 228 229 field_class = opts.fetch(:class, "form-control") 230 231 if autocomplete_for_field?(param) && opts[:autocomplete] 232 autocomplete = "autocomplete=\"#{opts[:autocomplete]}\"" 233 end 234 235 if inputmode_for_field?(param) && opts[:inputmode] 236 inputmode = "inputmode=\"#{opts[:inputmode]}\"" 237 end 238 239 if mark_input_fields_as_required? && opts[:required] != false 240 required = "required=\"required\"" 241 end 242 243 "<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]}" 244 end
# File lib/rodauth/features/base.rb 250 def inputmode_for_field?(_param) 251 mark_input_fields_with_inputmode? 252 end
:nocov:
# File lib/rodauth/features/base.rb 974 def instance_variables_to_inspect 975 instance_variables.reject{|v| instance_variable_get(v).nil?} 976 end
# File lib/rodauth/features/base.rb 967 def internal_request? 968 false 969 end
# File lib/rodauth/features/internal_request.rb 387 def internal_request_configuration(&block) 388 @auth.instance_exec do 389 (@internal_request_configuration_blocks ||= []) << block 390 end 391 end
# File lib/rodauth/features/lockout.rb 178 def invalid_login_attempted 179 ds = account_login_failures_ds. 180 where(account_login_failures_id_column=>account_id) 181 182 number = if db.database_type == :postgres 183 ds.returning(account_login_failures_number_column). 184 with_sql(:update_sql, account_login_failures_number_column=>Sequel.expr(account_login_failures_number_column)+1). 185 single_value 186 else 187 # :nocov: 188 if ds.update(account_login_failures_number_column=>Sequel.expr(account_login_failures_number_column)+1) > 0 189 ds.get(account_login_failures_number_column) 190 end 191 # :nocov: 192 end 193 194 unless number 195 # Ignoring the violation is safe here. It may allow slightly more than max_invalid_logins invalid logins before 196 # lockout, but allowing a few extra is OK if the race is lost. 197 ignore_uniqueness_violation{account_login_failures_ds.insert(account_login_failures_id_column=>account_id)} 198 number = 1 199 end 200 201 if number >= max_invalid_logins 202 @unlock_account_key_value = generate_unlock_account_key 203 hash = _setup_account_lockouts_hash(account_id, unlock_account_key_value) 204 205 if e = raised_uniqueness_violation{account_lockouts_ds.insert(hash)} 206 # If inserting into the lockout table raises a violation, we should just be able to pull the already inserted 207 # key out of it. If that doesn't return a valid key, we should reraise the error. 208 raise e unless @unlock_account_key_value = account_lockouts_ds.get(account_lockouts_key_column) 209 210 after_account_lockout 211 show_lockout_page 212 else 213 after_account_lockout 214 e 215 end 216 end 217 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 59 def json_request? 60 return @json_request unless @json_request.nil? 61 @json_request = !(request.content_type !~ json_request_content_type_regexp) 62 end
# File lib/rodauth/features/json.rb 228 def json_response 229 @json_response ||= {} 230 end
# File lib/rodauth/features/json.rb 73 def json_response_error? 74 !!json_response[json_response_error_key] 75 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 124 def jwt_payload 125 return @jwt_payload unless @jwt_payload.nil? 126 @jwt_payload = JWT.decode(jwt_token, _jwt_decode_secrets, true, _jwt_decode_opts.merge(:algorithm=>jwt_algorithm))[0] 127 rescue JWT::DecodeError => e 128 rescue_jwt_payload(e) 129 end
# File lib/rodauth/features/jwt_refresh.rb 174 def jwt_refresh_token_account_ds(account_id) 175 jwt_refresh_token_ds.where(jwt_refresh_token_account_id_column => account_id) 176 end
# File lib/rodauth/features/jwt_refresh.rb 178 def jwt_refresh_token_account_token_ds(account_id, token_id) 179 jwt_refresh_token_account_ds(account_id). 180 where(jwt_refresh_token_id_column=>token_id) 181 end
# File lib/rodauth/features/jwt_refresh.rb 183 def jwt_refresh_token_ds 184 db[jwt_refresh_token_table] 185 end
# File lib/rodauth/features/jwt_refresh.rb 197 def jwt_refresh_token_insert_hash 198 hash = {jwt_refresh_token_account_id_column => account_id, jwt_refresh_token_key_column => random_key} 199 set_deadline_value(hash, jwt_refresh_token_deadline_column, jwt_refresh_token_deadline_interval) 200 hash 201 end
# File lib/rodauth/features/jwt_refresh.rb 154 def jwt_refresh_token_match?(key) 155 # We don't need to match tokens if we are requiring a valid current access token 156 return true unless allow_refresh_with_expired_jwt_access_token? 157 158 # If allowing with expired jwt access token, check the expired session contains 159 # hmac matching submitted and active refresh token. 160 s = session[jwt_refresh_token_hmac_session_key].to_s 161 h = session[jwt_refresh_token_data_session_key].to_s + key 162 timing_safe_eql?(compute_hmac(h), s) || (hmac_secret_rotation? && timing_safe_eql?(compute_old_hmac(h), s)) 163 end
# File lib/rodauth/features/jwt.rb 68 def jwt_secret 69 raise ConfigurationError, "jwt_secret not set" 70 end
# File lib/rodauth/features/jwt.rb 72 def jwt_session_hash 73 jwt_session_key ? {jwt_session_key=>session} : session 74 end
# File lib/rodauth/features/jwt.rb 80 def jwt_token 81 return @jwt_token if @jwt_token 82 83 if (v = request.env['HTTP_AUTHORIZATION']) && v !~ jwt_authorization_ignore 84 @jwt_token = v.sub(jwt_authorization_remove, '') 85 end 86 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 116 def load_memory 117 if logged_in? 118 if extend_remember_deadline_while_logged_in? 119 if account_from_session 120 extend_remember_deadline 121 else 122 forget_login 123 clear_session 124 end 125 end 126 elsif account_from_remember_cookie 127 before_load_memory 128 login_session('remember') 129 extend_remember_deadline if extend_remember_deadline? 130 after_load_memory 131 end 132 end
# File lib/rodauth/features/base.rb 907 def loaded_templates 908 [] 909 end
# File lib/rodauth/features/lockout.rb 149 def locked_out? 150 if t = convert_timestamp(account_lockouts_ds.get(account_lockouts_deadline_column)) 151 if Time.now < t 152 true 153 else 154 unlock_account 155 false 156 end 157 else 158 false 159 end 160 end
# File lib/rodauth/features/remember.rb 176 def logged_in_via_remember_key? 177 authenticated_by.include?('remember') 178 end
# File lib/rodauth/features/login.rb 97 def login(auth_type) 98 @saved_login_redirect = remove_session_value(login_redirect_session_key) 99 transaction do 100 before_login 101 login_session(auth_type) 102 yield if block_given? 103 after_login 104 end 105 require_response(:_login_response) 106 end
# File lib/rodauth/features/login_password_requirements_base.rb 49 def login_confirm_label 50 "Confirm #{login_label}" 51 end
# File lib/rodauth/features/login_password_requirements_base.rb 133 def login_confirmation_matches?(login, login_confirmation) 134 login.casecmp?(login_confirmation) 135 end
# File lib/rodauth/features/login_password_requirements_base.rb 111 def login_does_not_meet_requirements_message 112 "invalid login#{", #{login_requirement_message}" if login_requirement_message}" 113 end
# File lib/rodauth/features/reset_password.rb 245 def login_failed_reset_password_request_form 246 render("reset-password-request") 247 end
# File lib/rodauth/features/base.rb 323 def login_field_autocomplete_value 324 login_uses_email? ? "email" : "on" 325 end
# File lib/rodauth/features/base.rb 347 def login_input_type 348 login_uses_email? ? 'email' : 'text' 349 end
# File lib/rodauth/features/login_password_requirements_base.rb 159 def login_meets_email_requirements?(login) 160 return true unless require_email_address_logins? 161 return true if login_valid_email?(login) 162 set_login_requirement_error_message(:login_not_valid_email, login_not_valid_email_message) 163 return false 164 end
# File lib/rodauth/features/login_password_requirements_base.rb 144 def login_meets_length_requirements?(login) 145 if login_minimum_length > login.length 146 set_login_requirement_error_message(:login_too_short, login_too_short_message) 147 false 148 elsif login_maximum_length < login.length 149 set_login_requirement_error_message(:login_too_long, login_too_long_message) 150 false 151 elsif login_maximum_bytes < login.bytesize 152 set_login_requirement_error_message(:login_too_many_bytes, login_too_many_bytes_message) 153 false 154 else 155 true 156 end 157 end
# File lib/rodauth/features/login_password_requirements_base.rb 57 def login_meets_requirements?(login) 58 login_meets_length_requirements?(login) && \ 59 login_meets_email_requirements?(login) 60 end
The normalized value of the login parameter
# File lib/rodauth/features/base.rb 545 def login_param_value 546 normalize_login(param(login_param)) 547 end
# File lib/rodauth/features/base.rb 366 def login_required 367 set_redirect_error_status(login_required_error_status) 368 set_error_reason :login_required 369 set_redirect_error_flash require_login_error_flash 370 redirect require_login_redirect 371 end
# File lib/rodauth/features/login.rb 115 def login_return_to_requested_location_path 116 request.fullpath if request.get? 117 end
# File lib/rodauth/features/base.rb 507 def login_session(auth_type) 508 update_session 509 set_session_value(authenticated_by_session_key, [auth_type]) 510 end
# File lib/rodauth/features/login_password_requirements_base.rb 115 def login_too_long_message 116 "maximum #{login_maximum_length} characters" 117 end
# File lib/rodauth/features/login_password_requirements_base.rb 119 def login_too_many_bytes_message 120 "maximum #{login_maximum_bytes} bytes" 121 end
# File lib/rodauth/features/login_password_requirements_base.rb 123 def login_too_short_message 124 "minimum #{login_minimum_length} characters" 125 end
# File lib/rodauth/features/base.rb 351 def login_uses_email? 352 login_column == :email 353 end
# File lib/rodauth/features/login_password_requirements_base.rb 166 def login_valid_email?(login) 167 login =~ login_email_regexp 168 end
# File lib/rodauth/features/active_sessions.rb 120 def logout_additional_form_tags 121 super.to_s + render('global-logout-field') 122 end
# File lib/rodauth/features/base.rb 572 def modifications_require_password? 573 has_password? 574 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 242 def new_recovery_code 243 random_key 244 end
# File lib/rodauth/features/webauthn.rb 302 def new_webauthn_credential 303 WebAuthn::Credential.options_for_create( 304 :timeout => webauthn_setup_timeout, 305 :user => {:id=>account_webauthn_user_id, :name=>webauthn_user_name}, 306 :authenticator_selection => webauthn_authenticator_selection, 307 :attestation => webauthn_attestation, 308 :extensions => webauthn_extensions, 309 :exclude => account_webauthn_ids, 310 **webauthn_create_relying_party_opts 311 ) 312 end
# File lib/rodauth/features/active_sessions.rb 62 def no_longer_active_session 63 clear_session 64 set_redirect_error_status inactive_session_error_status 65 set_error_reason :inactive_session 66 set_redirect_error_flash active_sessions_error_flash 67 redirect active_sessions_redirect 68 end
# File lib/rodauth/features/base.rb 549 def normalize_login(login) 550 login 551 end
# File lib/rodauth/features/base.rb 724 def normalize_session_or_flash_key(key) 725 scope.opts[:sessions_convert_symbols] ? key.to_s : key 726 end
Return nil by default for values with null bytes
# File lib/rodauth/features/base.rb 554 def null_byte_parameter_value(key, value) 555 nil 556 end
# File lib/rodauth/features/base.rb 466 def only_json? 467 scope.class.opts[:rodauth_json] == :only 468 end
# File lib/rodauth/features/base.rb 315 def open_account? 316 skip_status_checks? || account[account_status_column] == account_open_status_value 317 end
# File lib/rodauth/features/otp.rb 285 def otp_add_key 286 _otp_add_key(otp_key) 287 super if defined?(super) 288 end
# File lib/rodauth/features/otp.rb 245 def otp_available? 246 otp_exists? && !otp_locked_out? 247 end
# File lib/rodauth/features/otp.rb 249 def otp_exists? 250 !!otp_key 251 end
# File lib/rodauth/features/otp.rb 385 def otp_hmac_old_secret(key) 386 base32_encode(compute_raw_hmac_with_secret(ROTP::Base32.decode(key), hmac_old_secret), key.bytesize) 387 end
# File lib/rodauth/features/otp.rb 381 def otp_hmac_secret(key) 382 base32_encode(compute_raw_hmac(ROTP::Base32.decode(key)), key.bytesize) 383 end
# File lib/rodauth/features/otp.rb 452 def otp_key_ds 453 db[otp_keys_table].where(otp_keys_id_column=>session_value) 454 end
# File lib/rodauth/features/otp.rb 337 def otp_keys_use_hmac? 338 !!hmac_secret 339 end
# File lib/rodauth/features/otp.rb 296 def otp_last_use 297 convert_timestamp(otp_key_ds.get(otp_keys_last_use_column)) 298 end
# File lib/rodauth/features/otp.rb 308 def otp_locked_out? 309 otp_key_ds.get(otp_keys_failures_column) >= otp_auth_failures_limit 310 end
# File lib/rodauth/features/otp_unlock.rb 212 def otp_lockout_redirect 213 otp_unlock_path 214 end
# File lib/rodauth/features/otp.rb 403 def otp_new_secret 404 ROTP::Base32.random_base32.downcase 405 end
# File lib/rodauth/features/otp.rb 320 def otp_provisioning_name 321 account[login_column] 322 end
# File lib/rodauth/features/otp.rb 312 def otp_provisioning_uri 313 otp.provisioning_uri(otp_provisioning_name) 314 end
# File lib/rodauth/features/otp.rb 324 def otp_qr_code 325 svg = RQRCode::QRCode.new(otp_provisioning_uri).as_svg(:module_size=>8, :viewbox=>true, :use_path=>true, :fill=>"fff") 326 svg.sub(/\A<\?xml version="1\.0" standalone="yes"\?>/, '') 327 end
# File lib/rodauth/features/otp.rb 300 def otp_record_authentication_failure 301 otp_key_ds.update(otp_keys_failures_column=>Sequel.identifier(otp_keys_failures_column) + 1) 302 end
# File lib/rodauth/features/otp.rb 280 def otp_remove 281 otp_key_ds.delete 282 @otp_key = false 283 end
# File lib/rodauth/features/otp.rb 304 def otp_remove_auth_failures 305 otp_key_ds.update(otp_keys_failures_column=>0) 306 end
# File lib/rodauth/features/otp.rb 376 def otp_tmp_key(secret) 377 _otp_tmp_key(secret) 378 clear_cached_otp 379 end
# File lib/rodauth/features/otp_unlock.rb 147 def otp_unlock_auth_failure 148 h = { 149 otp_unlock_num_successes_column=>0, 150 otp_unlock_next_auth_attempt_after_column=>Sequel.date_add(Sequel::CURRENT_TIMESTAMP, :seconds=>otp_unlock_auth_failure_cooldown_seconds) 151 } 152 153 if otp_unlock_ds.update(h) == 0 154 h[otp_unlock_id_column] = session_value 155 156 # If row already exists when inserting, no need to do anything 157 raises_uniqueness_violation?{otp_unlock_ds.insert(h)} 158 end 159 end
# File lib/rodauth/features/otp_unlock.rb 161 def otp_unlock_auth_success 162 deadline = Sequel.date_add(Sequel::CURRENT_TIMESTAMP, :seconds=>otp_unlock_success_cooldown_seconds) 163 164 # Add WHERE to avoid possible race condition when multiple unlock auth requests 165 # are sent at the same time (only the first should increment num successes). 166 if otp_unlock_ds. 167 where(Sequel[otp_unlock_next_auth_attempt_after_column] < Sequel::CURRENT_TIMESTAMP). 168 update( 169 otp_unlock_num_successes_column=>Sequel[otp_unlock_num_successes_column]+1, 170 otp_unlock_next_auth_attempt_after_column=>deadline 171 ) == 0 172 173 # Ignore uniqueness errors when inserting after a failed update, 174 # which could be caused due to the race condition mentioned above. 175 raises_uniqueness_violation? do 176 otp_unlock_ds.insert( 177 otp_unlock_id_column=>session_value, 178 otp_unlock_next_auth_attempt_after_column=>deadline 179 ) 180 end 181 end 182 183 @otp_unlock_data = nil 184 # :nocov: 185 if otp_unlock_data 186 # :nocov: 187 if otp_unlock_num_successes >= otp_unlock_auths_required 188 # At least the requisite number of consecutive successful unlock 189 # authentications. Unlock OTP authentication. 190 otp_key_ds.update(otp_keys_failures_column => 0) 191 192 # Remove OTP unlock metadata when unlocking OTP authentication 193 otp_unlock_reset 194 # else 195 # # Still need additional consecutive successful unlock attempts. 196 end 197 # else 198 # # if row isn't available, probably the process was reset during this, 199 # # and it's safe to do nothing in that case. 200 end 201 end
# File lib/rodauth/features/otp_unlock.rb 117 def otp_unlock_available? 118 if otp_unlock_data 119 next_auth_attempt_after = otp_unlock_next_auth_attempt_after 120 current_timestamp = Time.now 121 122 if (next_auth_attempt_after < current_timestamp - otp_unlock_deadline_seconds) 123 # Unlock process not fully completed within deadline, reset process 124 otp_unlock_reset 125 true 126 else 127 if next_auth_attempt_after > current_timestamp 128 # If next auth attempt after timestamp is in the future, that means the next 129 # unlock attempt cannot happen until then. 130 false 131 else 132 if otp_unlock_num_successes == 0 133 # 0 value indicates previous attempt was a failure. Since failure cooldown 134 # period has passed, reset process so user gets full deadline period 135 otp_unlock_reset 136 end 137 true 138 end 139 end 140 else 141 # No row means no unlock attempts yet (or previous attempt was more than the 142 # deadline account, so unlocking is available 143 true 144 end 145 end
# File lib/rodauth/features/otp_unlock.rb 242 def otp_unlock_data 243 @otp_unlock_data ||= otp_unlock_ds.first 244 end
# File lib/rodauth/features/otp_unlock.rb 224 def otp_unlock_deadline 225 otp_unlock_next_auth_attempt_after + otp_unlock_deadline_seconds 226 end
# File lib/rodauth/features/otp_unlock.rb 203 def otp_unlock_deadline_passed? 204 otp_unlock_data ? (otp_unlock_next_auth_attempt_after < Time.now - otp_unlock_deadline_seconds) : false 205 end
# File lib/rodauth/features/otp_unlock.rb 255 def otp_unlock_ds 256 db[otp_unlock_table].where(otp_unlock_id_column=>session_value) 257 end
# File lib/rodauth/features/otp_unlock.rb 216 def otp_unlock_next_auth_attempt_after 217 if otp_unlock_data 218 convert_timestamp(otp_unlock_data[otp_unlock_next_auth_attempt_after_column]) 219 else 220 Time.now 221 end 222 end
# File lib/rodauth/features/otp_unlock.rb 232 def otp_unlock_not_available_set_refresh_header 233 response.headers["refresh"] = ((otp_unlock_next_auth_attempt_after - Time.now).to_i + 1).to_s 234 end
# File lib/rodauth/features/otp_unlock.rb 228 def otp_unlock_num_successes 229 otp_unlock_data ? otp_unlock_data[otp_unlock_num_successes_column] : 0 230 end
# File lib/rodauth/features/otp_unlock.rb 207 def otp_unlock_refresh_tag 208 # RODAUTH3: Remove 209 "<meta http-equiv=\"refresh\" content=\"#{(otp_unlock_next_auth_attempt_after - Time.now).to_i + 1}\">" 210 end
# File lib/rodauth/features/otp_unlock.rb 250 def otp_unlock_reset 251 otp_unlock_ds.delete 252 @otp_unlock_data = nil 253 end
# File lib/rodauth/features/otp_unlock.rb 246 def otp_unlock_success_cooldown_seconds 247 (_otp_interval+(otp_drift||0))*2 248 end
# File lib/rodauth/features/otp.rb 290 def otp_update_last_use 291 otp_key_ds. 292 where(Sequel.date_add(otp_keys_last_use_column, :seconds=>_otp_interval) < Sequel::CURRENT_TIMESTAMP). 293 update(otp_keys_last_use_column=>Sequel::CURRENT_TIMESTAMP) == 1 294 end
# File lib/rodauth/features/otp.rb 329 def otp_user_key 330 @otp_user_key ||= if otp_keys_use_hmac? 331 otp_hmac_secret(otp_key) 332 else 333 otp_key 334 end 335 end
# File lib/rodauth/features/otp.rb 253 def otp_valid_code?(ot_pass) 254 if _otp_valid_code?(ot_pass, otp) 255 true 256 elsif hmac_secret_rotation? && _otp_valid_code?(ot_pass, _otp_for_key(otp_hmac_old_secret(otp_key))) 257 _otp_valid_code_for_old_secret 258 true 259 else 260 false 261 end 262 end
# File lib/rodauth/features/otp.rb 389 def otp_valid_key?(secret) 390 return false unless secret =~ /\A([a-z2-7]{16}|[a-z2-7]{32})\z/ 391 if otp_keys_use_hmac? 392 # Purposely do not allow creating new OTPs with old secrets, 393 # since OTP rotation is difficult. The user will get shown 394 # the same page with an updated secret, which they can submit 395 # to setup OTP. 396 timing_safe_eql?(otp_hmac_secret(param(otp_setup_raw_param)), secret) 397 else 398 true 399 end 400 end
Return nil by default for values over maximum bytesize.
# File lib/rodauth/features/base.rb 540 def over_max_bytesize_param_value(key, value) 541 nil 542 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 523 def param(key) 524 param_or_nil(key).to_s 525 end
Return a string for the parameter name, or nil if there is no parameter with that name.
# File lib/rodauth/features/base.rb 529 def param_or_nil(key) 530 value = raw_param(key) 531 unless value.nil? 532 value = value.to_s 533 value = over_max_bytesize_param_value(key, value) if max_param_bytesize && value.bytesize > max_param_bytesize 534 value = null_byte_parameter_value(key, value) if value && value.include?("\0") 535 end 536 value 537 end
# File lib/rodauth/features/login_password_requirements_base.rb 53 def password_confirm_label 54 "Confirm #{password_label}" 55 end
# File lib/rodauth/features/login_password_requirements_base.rb 185 def password_does_not_contain_null_byte?(password) 186 return true unless password.include?("\0") 187 set_password_requirement_error_message(:password_contains_null_byte, contains_null_byte_message) 188 false 189 end
# File lib/rodauth/features/login_password_requirements_base.rb 90 def password_does_not_meet_requirements_message 91 "invalid password, does not meet requirements#{" (#{password_requirement_message})" if password_requirement_message}" 92 end
# File lib/rodauth/features/disallow_password_reuse.rb 52 def password_doesnt_match_previous_password?(password) 53 match = if use_database_authentication_functions? 54 salts = previous_password_ds. 55 select_map([previous_password_id_column, Sequel.function(function_name(:rodauth_get_previous_salt), previous_password_id_column).as(:salt)]) 56 return true if salts.empty? 57 58 salts.any? do |hash_id, salt| 59 database_function_password_match?(:rodauth_previous_password_hash_match, hash_id, password, salt) 60 end 61 else 62 # :nocov: 63 previous_password_ds.select_map(previous_password_hash_column).any? do |hash| 64 password_hash_match?(hash, password) 65 end 66 # :nocov: 67 end 68 69 return true unless match 70 set_password_requirement_error_message(:password_same_as_previous_password, password_same_as_previous_password_message) 71 false 72 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 327 def password_field_autocomplete_value 328 @password_field_autocomplete_value || 'current-password' 329 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 21 def password_hash(password) 22 return super unless use_argon2? 23 24 if secret = argon2_secret 25 argon2_params = Hash[password_hash_cost] 26 argon2_params[:secret] = secret 27 else 28 argon2_params = password_hash_cost 29 end 30 ::Argon2::Password.new(argon2_params).create(password) 31 end
# File lib/rodauth/features/argon2.rb 47 def password_hash_cost 48 return super unless use_argon2? 49 argon2_hash_cost 50 end
# File lib/rodauth/features/base.rb 897 def password_hash_ds 898 db[password_hash_table].where(password_hash_id_column=>account ? account_id : session_value) 899 end
# File lib/rodauth/features/argon2.rb 52 def password_hash_match?(hash, password) 53 return super unless argon2_hash_algorithm?(hash) 54 argon2_password_hash_match?(hash, password) 55 end
# File lib/rodauth/features/argon2.rb 57 def password_hash_using_salt(password, salt) 58 return super unless argon2_hash_algorithm?(salt) 59 argon2_password_hash_using_salt_and_secret(password, salt, argon2_secret) 60 end
# File lib/rodauth/features/base.rb 488 def password_match?(password) 489 if hash = get_password_hash 490 if account_password_hash_column || !use_database_authentication_functions? 491 password_hash_match?(hash, password) 492 else 493 database_function_password_match?(:rodauth_valid_password_hash, account_id, password, hash) 494 end 495 end 496 end
# File lib/rodauth/features/login_password_requirements_base.rb 170 def password_meets_length_requirements?(password) 171 if password_minimum_length > password.length 172 set_password_requirement_error_message(:password_too_short, password_too_short_message) 173 false 174 elsif password_maximum_length && password_maximum_length < password.length 175 set_password_requirement_error_message(:password_too_long, password_too_long_message) 176 false 177 elsif password_maximum_bytes && password_maximum_bytes < password.bytesize 178 set_password_requirement_error_message(:password_too_many_bytes, password_too_many_bytes_message) 179 false 180 else 181 true 182 end 183 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 24 def password_recently_entered? 25 return false unless last_password_entry = session[last_password_entry_session_key] 26 last_password_entry + password_grace_period > Time.now.to_i 27 end
# File lib/rodauth/features/reset_password.rb 259 def password_reset_ds(id=account_id) 260 db[reset_password_table].where(reset_password_id_column=>id) 261 end
# File lib/rodauth/features/login_password_requirements_base.rb 94 def password_too_long_message 95 "maximum #{password_maximum_length} characters" 96 end
# File lib/rodauth/features/login_password_requirements_base.rb 98 def password_too_many_bytes_message 99 "maximum #{password_maximum_bytes} bytes" 100 end
# File lib/rodauth/features/login_password_requirements_base.rb 102 def password_too_short_message 103 "minimum #{password_minimum_length} characters" 104 end
# File lib/rodauth/features/base.rb 576 def possible_authentication_methods 577 has_password? ? ['password'] : [] 578 end
# File lib/rodauth/features/base.rb 470 def post_configure 471 require 'bcrypt' if require_bcrypt? 472 db.extension :date_arithmetic if use_date_arithmetic? 473 474 if method(:convert_token_id_to_integer?).owner == Rodauth::Base && (db rescue false) && db.table_exists?(accounts_table) && db.schema(accounts_table).find{|col, v| break v[:type] == :integer if col == account_id_column} 475 self.class.send(:define_method, :convert_token_id_to_integer?){true} 476 end 477 478 route_hash= {} 479 self.class.routes.each do |meth| 480 route_meth = "#{meth.to_s.sub(/\Ahandle_/, '')}_route" 481 if route = send(route_meth) 482 route_hash["/#{route}"] = meth 483 end 484 end 485 self.class.route_hash = route_hash.freeze 486 end
# File lib/rodauth/features/disallow_password_reuse.rb 96 def previous_password_ds 97 db[previous_password_hash_table].where(previous_password_account_id_column=>account_id) 98 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 926 def raises_uniqueness_violation?(&block) 927 transaction(:savepoint=>:only, &block) 928 false 929 rescue unique_constraint_violation_class => e 930 e 931 end
# File lib/rodauth/features/base.rb 715 def random_key 716 SecureRandom.urlsafe_base64(32) 717 end
# File lib/rodauth/features/base.rb 558 def raw_param(key) 559 request.params[key] 560 end
# File lib/rodauth/features/recovery_codes.rb 162 def recovery_code_match?(code) 163 recovery_codes.each do |s| 164 if timing_safe_eql?(code, s) 165 recovery_codes_ds.where(recovery_codes_column=>code).delete 166 if recovery_codes_primary? 167 add_recovery_code 168 end 169 return true 170 end 171 end 172 false 173 end
# File lib/rodauth/features/recovery_codes.rb 198 def recovery_codes_available? 199 !recovery_codes_ds.empty? 200 end
# File lib/rodauth/features/recovery_codes.rb 266 def recovery_codes_ds 267 db[recovery_codes_table].where(recovery_codes_id_column=>session_value) 268 end
# File lib/rodauth/features/recovery_codes.rb 246 def recovery_codes_primary? 247 (features & [:otp, :sms_codes, :webauthn]).empty? 248 end
# File lib/rodauth/features/recovery_codes.rb 158 def recovery_codes_remove 159 recovery_codes_ds.delete 160 end
# File lib/rodauth/features/base.rb 691 def redirect(path) 692 request.redirect(path) 693 end
# File lib/rodauth/features/remember.rb 263 def remember_key_ds(id=account_id) 264 db[remember_table].where(remember_id_column=>id) 265 end
# File lib/rodauth/features/remember.rb 134 def remember_login 135 get_remember_key 136 set_remember_cookie 137 set_session_value(remember_deadline_extended_session_key, Time.now.to_i) if extend_remember_deadline? 138 end
# File lib/rodauth/features/remember.rb 93 def remembered_session_id 94 return unless cookie = _get_remember_cookie 95 id, key = cookie.split('_', 2) 96 return unless id && key 97 98 actual, deadline = active_remember_key_ds(id).get([remember_key_column, remember_deadline_column]) 99 return unless actual 100 101 if hmac_secret && !(valid = timing_safe_eql?(key, compute_hmac(actual))) 102 if hmac_secret_rotation? && (valid = timing_safe_eql?(key, compute_old_hmac(actual))) 103 _set_remember_cookie(id, actual, deadline) 104 elsif !(raw_remember_token_deadline && raw_remember_token_deadline > convert_timestamp(deadline)) 105 return 106 end 107 end 108 109 unless valid || timing_safe_eql?(key, actual) 110 return 111 end 112 113 id 114 end
# File lib/rodauth/features/active_sessions.rb 94 def remove_active_session(session_id) 95 active_sessions_ds.where(active_sessions_session_id_column=>session_id).delete 96 end
# File lib/rodauth/features/active_sessions.rb 98 def remove_all_active_sessions 99 active_sessions_ds.delete 100 end
# File lib/rodauth/features/active_sessions.rb 106 def remove_all_active_sessions_except_current 107 if session_id = session[session_id_session_key] 108 remove_all_active_sessions_except_for(session_id) 109 else 110 remove_all_active_sessions 111 end 112 end
# File lib/rodauth/features/active_sessions.rb 102 def remove_all_active_sessions_except_for(session_id) 103 active_sessions_ds.exclude(active_sessions_session_id_column=>compute_hmacs(session_id)).delete 104 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 88 def remove_current_session 89 if session_id = session[session_id_session_key] 90 remove_active_session(compute_hmacs(session_id)) 91 end 92 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 114 def remove_inactive_sessions 115 if cond = inactive_session_cond 116 active_sessions_ds.where(cond).delete 117 end 118 end
# File lib/rodauth/features/jwt_refresh.rb 187 def remove_jwt_refresh_token_key(token) 188 account_id, token_id, _ = _account_refresh_token_split(token) 189 jwt_refresh_token_account_token_ds(account_id, token_id).delete 190 end
# File lib/rodauth/features/lockout.rb 289 def remove_lockout_metadata 290 account_login_failures_ds.delete 291 account_lockouts_ds.delete 292 end
# File lib/rodauth/features/remember.rb 172 def remove_remember_key(id=account_id) 173 remember_key_ds(id).delete 174 end
# File lib/rodauth/features/reset_password.rb 182 def remove_reset_password_key 183 password_reset_ds.delete 184 end
# File lib/rodauth/features/base.rb 992 def remove_session_value(key) 993 session.delete(key) 994 end
# File lib/rodauth/features/verify_account.rb 169 def remove_verify_account_key 170 verify_account_ds.delete 171 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 462 def render(page) 463 _view(:render, page) 464 end
# File lib/rodauth/features/login.rb 148 def render_multi_phase_login_forms 149 multi_phase_login_forms.sort.map{|_, form, _| form}.join("\n") 150 end
# File lib/rodauth/features/base.rb 183 def request 184 scope.request 185 end
# File lib/rodauth/features/base.rb 407 def require_account 408 require_authentication 409 require_account_session 410 end
# File lib/rodauth/features/base.rb 733 def require_account_session 734 unless account_from_session 735 clear_session 736 login_required 737 end 738 end
# File lib/rodauth/features/base.rb 403 def require_authentication 404 require_login 405 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 29 def require_http_basic_auth 30 unless http_basic_auth 31 set_http_basic_auth_error_response 32 return_response 33 end 34 end
# File lib/rodauth/features/base.rb 395 def require_login 396 login_required unless logged_in? 397 end
# File lib/rodauth/features/verify_account.rb 161 def require_login_confirmation? 162 false 163 end
# File lib/rodauth/features/login.rb 152 def require_login_redirect 153 login_path 154 end
# File lib/rodauth/features/otp.rb 236 def require_otp_setup 237 unless otp_exists? 238 set_redirect_error_status(two_factor_not_setup_error_status) 239 set_error_reason :two_factor_not_setup 240 set_redirect_error_flash two_factor_not_setup_error_flash 241 redirect two_factor_need_setup_redirect 242 end 243 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/base.rb 983 def require_response(meth) 984 send(meth) 985 raise ConfigurationError, "#{meth.to_s.sub(/\A_/, '')} overridden without returning a response (should use redirect or request.halt)." 986 end
# File lib/rodauth/features/sms_codes.rb 338 def require_sms_available 339 require_sms_setup 340 341 if sms_locked_out? 342 set_redirect_error_status(lockout_error_status) 343 set_error_reason :sms_locked_out 344 set_redirect_error_flash sms_lockout_error_flash 345 redirect sms_lockout_redirect 346 end 347 end
# File lib/rodauth/features/sms_codes.rb 329 def require_sms_not_setup 330 if sms_setup? 331 set_redirect_error_status(sms_already_setup_error_status) 332 set_error_reason :sms_already_setup 333 set_redirect_error_flash sms_already_setup_error_flash 334 redirect sms_already_setup_redirect 335 end 336 end
# File lib/rodauth/features/sms_codes.rb 320 def require_sms_setup 321 unless sms_setup? 322 set_redirect_error_status(two_factor_not_setup_error_status) 323 set_error_reason :sms_not_setup 324 set_redirect_error_flash sms_not_setup_error_flash 325 redirect sms_needs_setup_redirect 326 end 327 end
# File lib/rodauth/features/two_factor_base.rb 158 def require_two_factor_authenticated 159 unless two_factor_authenticated? 160 if two_factor_auth_return_to_requested_location? 161 set_session_value(two_factor_auth_redirect_session_key, request.fullpath) 162 end 163 set_redirect_error_status(two_factor_need_authentication_error_status) 164 set_error_reason :two_factor_need_authentication 165 set_redirect_error_flash two_factor_need_authentication_error_flash 166 redirect two_factor_auth_required_redirect 167 end 168 end
# File lib/rodauth/features/two_factor_base.rb 149 def require_two_factor_not_authenticated(auth_type = nil) 150 if two_factor_authenticated? || (auth_type && two_factor_login_type_match?(auth_type)) 151 set_redirect_error_status(two_factor_already_authenticated_error_status) 152 set_error_reason :two_factor_already_authenticated 153 set_redirect_error_flash two_factor_already_authenticated_error_flash 154 redirect two_factor_already_authenticated_redirect 155 end 156 end
# File lib/rodauth/features/two_factor_base.rb 137 def require_two_factor_setup 138 # Avoid database query if already authenticated via 2nd factor 139 return if two_factor_authenticated? 140 141 return if uses_two_factor_authentication? 142 143 set_redirect_error_status(two_factor_not_setup_error_status) 144 set_error_reason :two_factor_not_setup 145 set_redirect_error_flash two_factor_not_setup_error_flash 146 redirect two_factor_need_setup_redirect 147 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 131 def rescue_jwt_payload(_) 132 @jwt_payload = false 133 end
# File lib/rodauth/features/reset_password.rb 267 def reset_password_account_status_value 268 account_open_status_value 269 end
# File lib/rodauth/features/reset_password.rb 190 def reset_password_email_link 191 token_link(reset_password_route, reset_password_key_param, reset_password_key_value) 192 end
# File lib/rodauth/features/reset_password.rb 217 def reset_password_email_recently_sent? 218 (email_last_sent = get_reset_password_email_last_sent) && (Time.now - email_last_sent < reset_password_skip_resend_email_within) 219 end
# File lib/rodauth/features/reset_password.rb 253 def reset_password_key_insert_hash 254 hash = {reset_password_id_column=>account_id, reset_password_key_column=>reset_password_key_value} 255 set_deadline_value(hash, reset_password_deadline_column, reset_password_deadline_interval) 256 hash 257 end
# File lib/rodauth/features/reset_password.rb 178 def reset_password_request_for_unverified_account 179 throw_error_reason(:unverified_account, unopen_account_error_status, login_param, unverified_account_message) 180 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 187 def response 188 scope.response 189 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 917 def retry_on_uniqueness_violation(&block) 918 if raises_uniqueness_violation?(&block) 919 yield 920 end 921 end
# File lib/rodauth/features/json.rb 210 def return_json_response 211 _return_json_response 212 end
# File lib/rodauth/features/base.rb 695 def return_response(body=nil) 696 response.write(body) if body 697 request.halt 698 end
# File lib/rodauth/features/base.rb 199 def route! 200 if meth = self.class.route_hash[request.remaining_path] 201 send(meth) 202 end 203 204 nil 205 end
# File lib/rodauth/features/base.rb 700 def route_path(route, opts=OPTS) 701 path = "#{prefix}/#{route}" 702 path += "?#{Rack::Utils.build_nested_query(opts)}" unless opts.empty? 703 path 704 end
# File lib/rodauth/features/base.rb 706 def route_url(route, opts=OPTS) 707 "#{base_url}#{route_path(route, opts)}" 708 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 191 def session 192 scope.session 193 end
# File lib/rodauth/features/active_sessions.rb 213 def session_inactivity_deadline_condition 214 if deadline = session_inactivity_deadline 215 Sequel[active_sessions_last_use_column] < Sequel.date_sub(Sequel::CURRENT_TIMESTAMP, seconds: deadline) 216 end 217 end
# File lib/rodauth/features/jwt.rb 76 def session_jwt 77 JWT.encode(jwt_session_hash, jwt_secret, jwt_algorithm) 78 end
# File lib/rodauth/features/active_sessions.rb 219 def session_lifetime_deadline_condition 220 if deadline = session_lifetime_deadline 221 Sequel[active_sessions_created_at_column] < Sequel.date_sub(Sequel::CURRENT_TIMESTAMP, seconds: deadline) 222 end 223 end
# File lib/rodauth/features/base.rb 306 def session_value 307 session[session_key] 308 end
This is needed on MySQL, which doesn’t support non constant defaults other than CURRENT_TIMESTAMP.
# File lib/rodauth/features/base.rb 953 def set_deadline_value(hash, column, interval) 954 if set_deadline_values? 955 # :nocov: 956 hash[column] = Sequel.date_add(Sequel::CURRENT_TIMESTAMP, interval) 957 # :nocov: 958 end 959 end
# File lib/rodauth/features/base.rb 783 def set_deadline_values? 784 db.database_type == :mysql 785 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 379 def set_error_flash(message) 380 flash.now[flash_error_key] = message 381 end
# File lib/rodauth/features/base.rb 771 def set_error_reason(reason) 772 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 207 def set_field_error(field, error) 208 (@field_errors ||= {})[field] = error 209 end
# File lib/rodauth/features/http_basic_auth.rb 79 def set_http_basic_auth_error_response 80 response.status = 401 81 set_response_header("www-authenticate", "Basic realm=\"#{http_basic_auth_realm}\"") 82 end
# File lib/rodauth/features/jwt.rb 152 def set_jwt 153 set_jwt_token(session_jwt) 154 end
# File lib/rodauth/features/jwt_refresh.rb 203 def set_jwt_refresh_token_hmac_session_key(token) 204 if allow_refresh_with_expired_jwt_access_token? 205 key = _account_refresh_token_split(token).last 206 data = random_key 207 set_session_value(jwt_refresh_token_data_session_key, data) 208 set_session_value(jwt_refresh_token_hmac_session_key, compute_hmac(data + key)) 209 end 210 end
# File lib/rodauth/features/jwt.rb 88 def set_jwt_token(token) 89 set_response_header('authorization', token) 90 end
# File lib/rodauth/features/password_grace_period.rb 46 def set_last_password_entry 47 set_session_value(last_password_entry_session_key, Time.now.to_i) 48 end
# File lib/rodauth/features/login_password_requirements_base.rb 127 def set_login_requirement_error_message(reason, message) 128 set_error_reason(reason) 129 @login_requirement_message = message 130 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 387 def set_notice_flash(message) 388 flash[flash_notice_key] = message 389 end
# File lib/rodauth/features/base.rb 391 def set_notice_now_flash(message) 392 flash.now[flash_notice_key] = message 393 end
# File lib/rodauth/features/disallow_password_reuse.rb 21 def set_password(password) 22 hash = super 23 add_previous_password_hash(hash) 24 hash 25 end
# File lib/rodauth/features/login_password_requirements_base.rb 106 def set_password_requirement_error_message(reason, message) 107 set_error_reason(reason) 108 @password_requirement_message = message 109 end
# File lib/rodauth/features/base.rb 383 def set_redirect_error_flash(message) 384 flash[flash_error_key] = message 385 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 745 def set_redirect_error_status(status) 746 end
# File lib/rodauth/features/remember.rb 198 def set_remember_cookie 199 _set_remember_cookie(account_id, remember_key_value, active_remember_key_ds.get(remember_deadline_column)) 200 end
# File lib/rodauth/features/reset_password.rb 205 def set_reset_password_email_last_sent 206 password_reset_ds.update(reset_password_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if reset_password_email_last_sent_column 207 end
# File lib/rodauth/features/base.rb 752 def set_response_error_reason_status(reason, status) 753 set_error_reason(reason) 754 set_response_error_status(status) 755 end
# File lib/rodauth/features/base.rb 748 def set_response_error_status(status) 749 response.status = status 750 end
# File lib/rodauth/features/base.rb 603 def set_response_header(key, value) 604 response.headers[key] = value 605 end
# File lib/rodauth/features/base.rb 988 def set_session_value(key, value) 989 session[key] = value 990 end
# File lib/rodauth/features/single_session.rb 99 def set_single_session_key(data) 100 data = compute_hmac(data) if hmac_secret 101 set_session_value(single_session_session_key, data) 102 end
# File lib/rodauth/features/base.rb 373 def set_title(title) 374 if title_instance_variable 375 scope.instance_variable_set(title_instance_variable, title) 376 end 377 end
# File lib/rodauth/features/lockout.rb 239 def set_unlock_account_email_last_sent 240 account_lockouts_ds.update(account_lockouts_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if account_lockouts_email_last_sent_column 241 end
# File lib/rodauth/features/verify_account.rb 227 def set_verify_account_email_last_sent 228 verify_account_ds.update(verify_account_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if verify_account_email_last_sent_column 229 end
# File lib/rodauth/features/verify_account.rb 239 def setup_account_verification 240 generate_verify_account_key_value 241 create_verify_account_key 242 send_verify_account_email 243 end
# File lib/rodauth/features/lockout.rb 294 def show_lockout_page 295 set_response_error_reason_status(:account_locked_out, lockout_error_status) 296 set_error_flash login_lockout_error_flash 297 return_response unlock_account_request_view 298 end
# File lib/rodauth/features/otp.rb 456 def show_otp_auth_link? 457 otp_available? 458 end
# File lib/rodauth/features/single_session.rb 104 def single_session_ds(id=session_value) 105 db[single_session_table]. 106 where(single_session_id_column=>id) 107 end
# File lib/rodauth/features/login.rb 126 def skip_login_field_on_login? 127 return false unless use_multi_phase_login? 128 valid_login_entered? 129 end
# File lib/rodauth/features/login.rb 131 def skip_password_field_on_login? 132 return false unless use_multi_phase_login? 133 !valid_login_entered? 134 end
# File lib/rodauth/features/close_account.rb 85 def skip_status_checks? 86 false 87 end
# File lib/rodauth/features/sms_codes.rb 525 def sms 526 case @sms 527 when nil 528 (@sms = sms_ds.first || false) || nil 529 when false 530 nil 531 else 532 @sms 533 end 534 end
# File lib/rodauth/features/sms_codes.rb 400 def sms_auth_message(code) 401 "SMS authentication code for #{domain} is #{code}" 402 end
# File lib/rodauth/features/sms_codes.rb 453 def sms_available? 454 sms_setup? && !sms_locked_out? 455 end
# File lib/rodauth/features/sms_codes.rb 432 def sms_code 433 sms[sms_code_column] 434 end
# File lib/rodauth/features/sms_codes.rb 436 def sms_code_issued_at 437 convert_timestamp(sms[sms_issued_at_column]) 438 end
# File lib/rodauth/features/sms_codes.rb 349 def sms_code_match?(code) 350 return false unless sms_current_auth? 351 timing_safe_eql?(code, sms_code) 352 end
# File lib/rodauth/features/sms_codes.rb 501 def sms_codes_primary? 502 (features & [:otp, :webauthn]).empty? 503 end
# File lib/rodauth/features/recovery_codes.rb 148 def sms_confirm 149 super if defined?(super) 150 auto_add_missing_recovery_codes 151 end
# File lib/rodauth/features/sms_codes.rb 363 def sms_confirm_failure 364 sms_ds.delete 365 end
# File lib/rodauth/features/sms_codes.rb 404 def sms_confirm_message(code) 405 "SMS confirmation code for #{domain} is #{code}" 406 end
# File lib/rodauth/features/sms_codes.rb 354 def sms_confirmation_match?(code) 355 sms_needs_confirmation? && sms_code_match?(code) 356 end
# File lib/rodauth/features/sms_codes.rb 461 def sms_current_auth? 462 sms_code && sms_code_issued_at + sms_code_allowed_seconds > Time.now 463 end
# File lib/rodauth/features/sms_codes.rb 358 def sms_disable 359 sms_ds.delete 360 @sms = false 361 end
# File lib/rodauth/features/sms_codes.rb 536 def sms_ds 537 db[sms_codes_table].where(sms_id_column=>session_value) 538 end
# File lib/rodauth/features/sms_codes.rb 440 def sms_failures 441 sms[sms_failures_column] 442 end
# File lib/rodauth/features/sms_codes.rb 457 def sms_locked_out? 458 sms_failures >= sms_failure_limit 459 end
# File lib/rodauth/features/sms_codes.rb 449 def sms_needs_confirmation? 450 sms && sms_failures.nil? 451 end
# File lib/rodauth/features/sms_codes.rb 408 def sms_needs_confirmation_notice_flash 409 sms_needs_confirmation_error_flash 410 end
# File lib/rodauth/features/sms_codes.rb 509 def sms_new_auth_code 510 SecureRandom.random_number(10**sms_auth_code_length).to_s.rjust(sms_auth_code_length, "0") 511 end
# File lib/rodauth/features/sms_codes.rb 513 def sms_new_confirm_code 514 SecureRandom.random_number(10**sms_confirm_code_length).to_s.rjust(sms_confirm_code_length, "0") 515 end
# File lib/rodauth/features/sms_codes.rb 505 def sms_normalize_phone(phone) 506 phone.to_s.gsub(/\D+/, '') 507 end
# File lib/rodauth/features/sms_codes.rb 428 def sms_phone 429 sms[sms_phone_column] 430 end
# File lib/rodauth/features/sms_codes.rb 423 def sms_record_failure 424 update_sms(sms_failures_column=>Sequel.expr(sms_failures_column)+1) 425 sms[sms_failures_column] = sms_ds.get(sms_failures_column) 426 end
# File lib/rodauth/features/sms_codes.rb 416 def sms_remove_expired_confirm_code 417 db[sms_codes_table]. 418 where(sms_id_column=>session_value, sms_failures_column => nil). 419 where(Sequel[sms_issued_at_column] < Sequel.date_sub(Sequel::CURRENT_TIMESTAMP, seconds: sms_confirm_deadline)). 420 delete 421 end
# File lib/rodauth/features/sms_codes.rb 374 def sms_remove_failures 375 return if sms_needs_confirmation? 376 update_hash_ds(sms, sms_ds.exclude(sms_failures_column => nil), sms_failures_column => 0, sms_code_column => nil) 377 end
# File lib/rodauth/features/sms_codes.rb 517 def sms_send(phone, message) 518 raise ConfigurationError, "sms_send needs to be defined in the Rodauth configuration for SMS sending to work" 519 end
# File lib/rodauth/features/sms_codes.rb 384 def sms_send_auth_code 385 code = sms_new_auth_code 386 sms_set_code(code) 387 sms_send(sms_phone, sms_auth_message(code)) 388 end
# File lib/rodauth/features/sms_codes.rb 390 def sms_send_confirm_code 391 code = sms_new_confirm_code 392 sms_set_code(code) 393 sms_send(sms_phone, sms_confirm_message(code)) 394 end
# File lib/rodauth/features/sms_codes.rb 412 def sms_set_code(code) 413 update_sms(sms_code_column=>code, sms_issued_at_column=>Sequel::CURRENT_TIMESTAMP) 414 end
# File lib/rodauth/features/sms_codes.rb 367 def sms_setup(phone_number) 368 # Cannot handle uniqueness violation here, as the phone number given may not match the 369 # one in the table. 370 sms_ds.insert(sms_id_column=>session_value, sms_phone_column=>phone_number, sms_failures_column => nil) 371 @sms = nil 372 end
# File lib/rodauth/features/sms_codes.rb 444 def sms_setup? 445 return false unless sms 446 !sms_needs_confirmation? 447 end
# File lib/rodauth/features/sms_codes.rb 396 def sms_valid_phone?(phone) 397 phone.length >= sms_phone_min_length 398 end
# File lib/rodauth/features/base.rb 668 def split_token(token) 669 token.split(token_separator, 2) 670 end
# File lib/rodauth/features/base.rb 882 def template_path(page) 883 File.join(File.dirname(__FILE__), '../../../templates', "#{page}.str") 884 end
# File lib/rodauth/features/http_basic_auth.rb 84 def throw_basic_auth_error(*args) 85 set_http_basic_auth_error_response 86 throw_error(*args) 87 end
# File lib/rodauth/features/base.rb 761 def throw_error(field, error) 762 set_field_error(field, error) 763 throw_rodauth_error 764 end
# File lib/rodauth/features/base.rb 774 def throw_error_reason(reason, status, field, message) 775 set_error_reason(reason) 776 throw_error_status(status, field, message) 777 end
# File lib/rodauth/features/base.rb 766 def throw_error_status(status, field, error) 767 set_response_error_status(status) 768 throw_error(field, error) 769 end
# File lib/rodauth/features/base.rb 757 def throw_rodauth_error 758 throw :rodauth_error 759 end
# File lib/rodauth/features/base.rb 728 def timing_safe_eql?(provided, actual) 729 provided = provided.to_s 730 Rack::Utils.secure_compare(provided.ljust(actual.length), actual) && provided.length == actual.length 731 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 710 def transaction(opts=OPTS, &block) 711 opts = opts.empty? ? transaction_opts : transaction_opts.merge(opts) 712 db.transaction(opts, &block) 713 end
# File lib/rodauth/features/base.rb 274 def translate(_key, default) 275 # do not attempt to translate by default 276 default 277 end
# File lib/rodauth/features/two_factor_base.rb 208 def two_factor_auth_links 209 @two_factor_auth_links ||= _filter_links(_two_factor_auth_links) 210 end
# File lib/rodauth/features/two_factor_base.rb 243 def two_factor_authenticate(type) 244 two_factor_update_session(type) 245 two_factor_remove_auth_failures 246 after_two_factor_authentication 247 require_response(:_two_factor_auth_response) 248 end
# File lib/rodauth/features/two_factor_base.rb 186 def two_factor_authenticated? 187 authenticated_by && authenticated_by.length >= 2 188 end
# File lib/rodauth/features/two_factor_base.rb 190 def two_factor_authentication_setup? 191 possible_authentication_methods.length >= 2 192 end
# File lib/rodauth/features/two_factor_base.rb 200 def two_factor_login_type_match?(type) 201 authenticated_by && authenticated_by.include?(type) 202 end
# File lib/rodauth/features/two_factor_base.rb 124 def two_factor_modifications_require_password? 125 modifications_require_password? 126 end
# File lib/rodauth/features/two_factor_base.rb 182 def two_factor_partially_authenticated? 183 logged_in? && !two_factor_authenticated? && uses_two_factor_authentication? 184 end
# File lib/rodauth/features/two_factor_base.rb 174 def two_factor_password_match?(password) 175 if two_factor_modifications_require_password? 176 password_match?(password) 177 else 178 true 179 end 180 end
# File lib/rodauth/features/otp.rb 226 def two_factor_remove 227 super 228 otp_remove 229 end
# File lib/rodauth/features/otp.rb 231 def two_factor_remove_auth_failures 232 super 233 otp_remove_auth_failures 234 end
# File lib/rodauth/features/two_factor_base.rb 216 def two_factor_remove_links 217 @two_factor_remove_links ||= _filter_links(_two_factor_remove_links) 218 end
# File lib/rodauth/features/two_factor_base.rb 256 def two_factor_remove_session(type) 257 authenticated_by.delete(type) 258 remove_session_value(two_factor_setup_session_key) 259 if authenticated_by.empty? 260 clear_session 261 end 262 end
# File lib/rodauth/features/two_factor_base.rb 212 def two_factor_setup_links 213 @two_factor_setup_links ||= _filter_links(_two_factor_setup_links) 214 end
# File lib/rodauth/features/two_factor_base.rb 264 def two_factor_update_session(auth_type) 265 authenticated_by << auth_type 266 set_session_value(two_factor_setup_session_key, true) 267 end
Work around jdbc/sqlite issue where it only raises ConstraintViolation and not UniqueConstraintViolation.
# File lib/rodauth/features/base.rb 935 def unique_constraint_violation_class 936 if db.adapter_scheme == :jdbc && db.database_type == :sqlite 937 # :nocov: 938 Sequel::ConstraintViolation 939 # :nocov: 940 else 941 Sequel::UniqueConstraintViolation 942 end 943 end
# File lib/rodauth/features/lockout.rb 162 def unlock_account 163 transaction do 164 remove_lockout_metadata 165 end 166 end
# File lib/rodauth/features/lockout.rb 227 def unlock_account_email_link 228 token_link(unlock_account_route, unlock_account_key_param, unlock_account_key_value) 229 end
# File lib/rodauth/features/lockout.rb 243 def unlock_account_email_recently_sent? 244 (email_last_sent = get_unlock_account_email_last_sent) && (Time.now - email_last_sent < unlock_account_skip_resend_email_within) 245 end
# File lib/rodauth/features/lockout.rb 254 def unlock_account_key 255 @unlock_account_key_value 256 end
# File lib/rodauth/features/lockout.rb 258 def unlock_account_key_value 259 # RODAUTH3: call unlock_account_key directly 260 unlock_account_key 261 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 1006 def update_account(values, ds=account_ds) 1007 update_hash_ds(account, ds, values) 1008 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 232 def update_current_session? 233 !!session_inactivity_deadline 234 end
# File lib/rodauth/features/base.rb 996 def update_hash_ds(hash, ds, values) 997 num = ds.update(values) 998 if num == 1 999 values.each do |k, v| 1000 hash[k] = Sequel::CURRENT_TIMESTAMP == v ? Time.now : v 1001 end 1002 end 1003 num 1004 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 20 def update_password_hash? 21 password_hash_cost != @current_password_hash_cost || @update_password_hash 22 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 66 def update_single_session_key 67 key = random_key 68 set_single_session_key(key) 69 if single_session_ds.update(single_session_key_column=>key) == 0 70 # Don't handle uniqueness violations here. While we could get the stored key from the 71 # database, it could lead to two sessions sharing the same key, which this feature is 72 # designed to prevent. 73 single_session_ds.insert(single_session_id_column=>session_value, single_session_key_column=>key) 74 end 75 end
# File lib/rodauth/features/sms_codes.rb 521 def update_sms(values) 522 update_hash_ds(sms, sms_ds, values) 523 end
# File lib/rodauth/features/base.rb 787 def use_database_authentication_functions? 788 case db.database_type 789 when :postgres, :mysql, :mssql 790 true 791 else 792 # :nocov: 793 false 794 # :nocov: 795 end 796 end
# File lib/rodauth/features/active_sessions.rb 241 def use_date_arithmetic? 242 true 243 end
# File lib/rodauth/features/json.rb 64 def use_json? 65 json_request? || only_json? 66 end
# File lib/rodauth/features/email_auth.rb 158 def use_multi_phase_login? 159 true 160 end
# File lib/rodauth/features/base.rb 798 def use_request_specific_csrf_tokens? 799 scope.opts[:rodauth_route_csrf] && scope.use_request_specific_csrf_tokens? 800 end
# File lib/rodauth/features/base.rb 979 def use_scope_clear_session? 980 scope.respond_to?(:clear_session) 981 end
# File lib/rodauth/features/base.rb 155 def uses_instance_variables(*ivs) 156 auth.define_singleton_method(:instance_variables_used) do 157 super() + ivs 158 end 159 end
# File lib/rodauth/features/two_factor_base.rb 194 def uses_two_factor_authentication? 195 return false unless logged_in? 196 set_session_value(two_factor_setup_session_key, two_factor_authentication_setup?) unless session.has_key?(two_factor_setup_session_key) 197 session[two_factor_setup_session_key] 198 end
# File lib/rodauth/features/jwt.rb 100 def valid_jwt? 101 !!(jwt_token && jwt_payload) 102 end
# File lib/rodauth/features/login.rb 136 def valid_login_entered? 137 @valid_login_entered 138 end
# File lib/rodauth/features/webauthn.rb 314 def valid_new_webauthn_credential?(webauthn_credential) 315 _override_webauthn_credential_response_verify(webauthn_credential) 316 (challenge = param_or_nil(webauthn_setup_challenge_param)) && 317 (hmac = param_or_nil(webauthn_setup_challenge_hmac_param)) && 318 (timing_safe_eql?(compute_hmac(challenge), hmac) || (hmac_secret_rotation? && timing_safe_eql?(compute_old_hmac(challenge), hmac))) && 319 webauthn_credential.verify(challenge) 320 end
# File lib/rodauth/features/webauthn.rb 362 def valid_webauthn_credential_auth?(webauthn_credential) 363 ds = webauthn_keys_ds.where(webauthn_keys_webauthn_id_column => webauthn_credential.id) 364 pub_key, sign_count = ds.get([webauthn_keys_public_key_column, webauthn_keys_sign_count_column]) 365 366 _override_webauthn_credential_response_verify(webauthn_credential) 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) || (hmac_secret_rotation? && timing_safe_eql?(compute_old_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 173 def verify_account 174 update_account(account_status_column=>account_open_status_value) == 1 175 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 206 def verify_account_email_link 207 token_link(verify_account_route, verify_account_key_param, verify_account_key_value) 208 end
# File lib/rodauth/features/verify_account.rb 245 def verify_account_email_recently_sent? 246 account && (email_last_sent = get_verify_account_email_last_sent) && (Time.now - email_last_sent < verify_account_skip_resend_email_within) 247 end
# File lib/rodauth/features/verify_account.rb 177 def verify_account_email_resend 178 if @verify_account_key_value = get_verify_account_key(account_id) 179 set_verify_account_email_last_sent 180 send_verify_account_email 181 true 182 end 183 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 9 def verify_account_view 10 webauthn_setup_view 11 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 457 def view(page, title) 458 set_title(title) 459 _view(:view, page) 460 end
# File lib/rodauth/features/webauthn.rb 503 def webauthn_account_id 504 session_value 505 end
# File lib/rodauth/features/webauthn.rb 340 def webauthn_allow 341 account_webauthn_ids 342 end
# File lib/rodauth/features/webauthn_login.rb 45 def webauthn_auth_additional_form_tags 46 if @webauthn_login 47 super.to_s + login_hidden_field 48 else 49 super 50 end 51 end
# File lib/rodauth/features/webauthn.rb 515 def webauthn_auth_credential_from_form_submission 516 begin 517 webauthn_credential = webauthn_form_submission_call(:from_get, webauthn_auth_data) 518 519 unless valid_webauthn_credential_auth?(webauthn_credential) 520 throw_error_reason(:invalid_webauthn_auth_param, invalid_key_error_status, webauthn_auth_param, webauthn_invalid_auth_param_message) 521 end 522 rescue WebAuthn::SignCountVerificationError 523 handle_webauthn_sign_count_verification_error 524 rescue WebAuthn::Error, RuntimeError, NoMethodError 525 throw_error_reason(:invalid_webauthn_auth_param, invalid_field_error_status, webauthn_auth_param, webauthn_invalid_auth_param_message) 526 end 527 528 webauthn_credential 529 end
# File lib/rodauth/features/webauthn.rb 531 def webauthn_auth_data 532 case auth_data = raw_param(webauthn_auth_param) 533 when String 534 begin 535 JSON.parse(auth_data) 536 rescue 537 throw_error_reason(:invalid_webauthn_auth_param, invalid_field_error_status, webauthn_auth_param, webauthn_invalid_auth_param_message) 538 end 539 when Hash 540 auth_data 541 else 542 throw_error_reason(:invalid_webauthn_auth_param, invalid_field_error_status, webauthn_auth_param, webauthn_invalid_auth_param_message) 543 end 544 end
# File lib/rodauth/features/webauthn.rb 247 def webauthn_auth_form_path 248 webauthn_auth_path 249 end
# File lib/rodauth/features/webauthn.rb 263 def webauthn_authenticator_selection 264 {'requireResidentKey' => false, 'userVerification' => webauthn_user_verification} 265 end
# File lib/rodauth/features/webauthn.rb 434 def webauthn_create_relying_party_opts 435 { :relying_party => webauthn_relying_party } 436 end
# File lib/rodauth/features/webauthn.rb 322 def webauthn_credential_options_for_get 323 WebAuthn::Credential.options_for_get( 324 :allow => webauthn_allow, 325 :timeout => webauthn_auth_timeout, 326 :user_verification => webauthn_user_verification, 327 :extensions => webauthn_extensions, 328 **webauthn_get_relying_party_opts 329 ) 330 end
# File lib/rodauth/features/webauthn.rb 267 def webauthn_extensions 268 {} 269 end
# File lib/rodauth/features/webauthn.rb 439 def webauthn_form_submission_call(meth, arg) 440 WebAuthn::Credential.public_send(meth, arg, :relying_party => webauthn_relying_party) 441 end
# File lib/rodauth/features/webauthn.rb 494 def webauthn_key_insert_hash(webauthn_credential) 495 { 496 webauthn_keys_account_id_column => webauthn_account_id, 497 webauthn_keys_webauthn_id_column => webauthn_credential.id, 498 webauthn_keys_public_key_column => webauthn_credential.public_key, 499 webauthn_keys_sign_count_column => Integer(webauthn_credential.sign_count) 500 } 501 end
# File lib/rodauth/features/webauthn.rb 511 def webauthn_keys_ds 512 db[webauthn_keys_table].where(webauthn_keys_account_id_column => webauthn_account_id) 513 end
# File lib/rodauth/features/webauthn_autofill.rb 60 def webauthn_login_options? 61 return true unless param_or_nil(login_param) 62 super 63 end
# File lib/rodauth/features/webauthn_login.rb 72 def webauthn_login_verification_factor?(webauthn_credential) 73 webauthn_login_user_verification_additional_factor? && 74 webauthn_credential.response.authenticator_data.user_verified? && 75 uses_two_factor_authentication? 76 end
# File lib/rodauth/features/webauthn.rb 336 def webauthn_origin 337 base_url 338 end
# File lib/rodauth/features/webauthn.rb 414 def webauthn_relying_party 415 # No need to memoize, only called once per request 416 WebAuthn::RelyingParty.new( 417 allowed_origins: [webauthn_origin], 418 id: webauthn_rp_id, 419 name: webauthn_rp_name, 420 ) 421 end
# File lib/rodauth/features/webauthn.rb 255 def webauthn_remove_authenticated_session 256 remove_session_value(authenticated_webauthn_id_session_key) 257 end
# File lib/rodauth/features/webauthn.rb 344 def webauthn_rp_id 345 webauthn_origin.sub(/\Ahttps?:\/\//, '').sub(/:\d+\z/, '') 346 end
# File lib/rodauth/features/webauthn.rb 348 def webauthn_rp_name 349 webauthn_rp_id 350 end
# File lib/rodauth/features/webauthn.rb 386 def webauthn_setup? 387 !webauthn_keys_ds.empty? 388 end
# File lib/rodauth/features/webauthn.rb 546 def webauthn_setup_credential_from_form_submission 547 unless two_factor_password_match?(param(password_param)) 548 throw_error_reason(:invalid_password, invalid_password_error_status, password_param, invalid_password_message) 549 end 550 551 begin 552 webauthn_credential = webauthn_form_submission_call(:from_create, webauthn_setup_data) 553 554 unless valid_new_webauthn_credential?(webauthn_credential) 555 throw_error_reason(:invalid_webauthn_setup_param, invalid_field_error_status, webauthn_setup_param, webauthn_invalid_setup_param_message) 556 end 557 rescue WebAuthn::Error, RuntimeError, NoMethodError 558 throw_error_reason(:invalid_webauthn_setup_param, invalid_field_error_status, webauthn_setup_param, webauthn_invalid_setup_param_message) 559 end 560 561 webauthn_credential 562 end
# File lib/rodauth/features/webauthn.rb 564 def webauthn_setup_data 565 case setup_data = raw_param(webauthn_setup_param) 566 when String 567 begin 568 JSON.parse(setup_data) 569 rescue 570 throw_error_reason(:invalid_webauthn_setup_param, invalid_field_error_status, webauthn_setup_param, webauthn_invalid_setup_param_message) 571 end 572 when Hash 573 setup_data 574 else 575 throw_error_reason(:invalid_webauthn_setup_param, invalid_field_error_status, webauthn_setup_param, webauthn_invalid_setup_param_message) 576 end 577 end
# File lib/rodauth/features/webauthn.rb 259 def webauthn_update_session(webauthn_id) 260 set_session_value(authenticated_webauthn_id_session_key, webauthn_id) 261 end
# File lib/rodauth/features/webauthn.rb 507 def webauthn_user_ids_ds 508 db[webauthn_user_ids_table].where(webauthn_user_ids_account_id_column => webauthn_account_id) 509 end
# File lib/rodauth/features/webauthn.rb 332 def webauthn_user_name 333 account![login_column] 334 end
# File lib/rodauth/features/webauthn_autofill.rb 25 def webauthn_user_verification 26 'preferred' 27 end