: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
- _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
- _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_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_route
- before_reset_password
- before_reset_password_request
- before_rodauth
- before_two_factor_auth_route
- before_two_factor_manage_route
- before_unlock_account
- before_unlock_account_request
- before_verify_account
- before_verify_account_route
- before_view_recovery_codes
- before_webauthn_auth_route
- before_webauthn_login_route
- before_webauthn_remove_route
- before_webauthn_setup_route
- button
- button_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_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?
- 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_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_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?
- 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_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 | = | 41 |
The minor version of |
|
TINY | = | 0 |
The patch version of |
|
VERSION | = | "#{MAJOR}.#{MINOR}.#{TINY}".freeze |
The full version of |
|
VERSION_NUMBER | = | MAJOR*10000 + MINOR*100 + TINY |
Public Instance Aliases
account_session_value | -> | account_id | |
ignore_uniqueness_violation | -> | raises_uniqueness_violation? |
If you just want to ignore uniqueness violations, this alias makes more sense. |
logged_in? | -> | session_value | |
raised_uniqueness_violation | -> | raises_uniqueness_violation? |
If you would like to operate/reraise the exception, this alias makes more sense. |
webauthn_get_relying_party_opts | -> | webauthn_create_relying_party_opts |
Public Class methods
# File lib/rodauth.rb 48 def self.configure(app, opts={}, &block) 49 json_opt = app.opts[:rodauth_json] = opts.fetch(:json, app.opts[:rodauth_json]) 50 csrf = app.opts[:rodauth_csrf] = opts.fetch(:csrf, app.opts[:rodauth_csrf]) 51 app.opts[:rodauth_route_csrf] = case csrf 52 when false, :rack_csrf 53 false 54 else 55 json_opt != :only 56 end 57 auth_class = (app.opts[:rodauths] ||= {})[opts[:name]] ||= opts[:auth_class] || Class.new(Auth) 58 if !auth_class.roda_class 59 auth_class.roda_class = app 60 elsif auth_class.roda_class != app 61 auth_class = app.opts[:rodauths][opts[:name]] = Class.new(auth_class) 62 auth_class.roda_class = app 63 end 64 auth_class.class_eval{@configuration_name = opts[:name] unless defined?(@configuration_name)} 65 auth_class.configure(&block) if block 66 auth_class.allocate.post_configure if auth_class.method_defined?(:post_configure) 67 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 8 def self.lib(opts={}, &block) 9 require 'roda' 10 c = Class.new(Roda) 11 c.plugin(:rodauth, opts) do 12 enable :internal_request 13 instance_exec(&block) 14 end 15 c.freeze 16 c.rodauth 17 end
# File lib/rodauth.rb 19 def self.load_dependencies(app, opts={}, &_) 20 json_opt = opts.fetch(:json, app.opts[:rodauth_json]) 21 if json_opt 22 app.plugin :json 23 app.plugin :json_parser 24 end 25 26 unless json_opt == :only 27 unless opts[:render] == false 28 require 'tilt/string' 29 app.plugin :render 30 end 31 32 case opts.fetch(:csrf, app.opts[:rodauth_csrf]) 33 when false 34 # nothing 35 when :rack_csrf 36 # :nocov: 37 app.plugin :csrf 38 # :nocov: 39 else 40 app.plugin :route_csrf 41 end 42 43 app.plugin :flash unless opts[:flash] == false 44 app.plugin :h 45 end 46 end
# File lib/rodauth/features/base.rb 146 def initialize(scope) 147 @scope = scope 148 end
Public Instance methods
# File lib/rodauth/features/email_auth.rb 236 def _account_from_email_auth_key(token) 237 account_from_key(token, account_open_status_value){|id| get_email_auth_key(id)} 238 end
# File lib/rodauth/features/base.rb 814 def _account_from_id(id, status_id=nil) 815 ds = account_ds(id) 816 ds = ds.where(account_status_column=>status_id) if status_id && !skip_status_checks? 817 ds.first 818 end
# File lib/rodauth/features/base.rb 801 def _account_from_login(login) 802 ds = account_table_ds.where(login_column=>login) 803 ds = ds.select(*account_select) if account_select 804 ds = ds.where(account_status_column=>[account_unverified_status_value, account_open_status_value]) unless skip_status_checks? 805 ds.first 806 end
# File lib/rodauth/features/jwt_refresh.rb 116 def _account_from_refresh_token(token) 117 id, token_id, key = _account_refresh_token_split(token) 118 119 unless key && 120 (id.to_s == session_value.to_s) && 121 (actual = get_active_refresh_token(id, token_id)) && 122 (timing_safe_eql?(key, convert_token_key(actual)) || (hmac_secret_rotation? && timing_safe_eql?(key, compute_old_hmac(actual)))) && 123 jwt_refresh_token_match?(key) 124 return 125 end 126 127 ds = account_ds(id) 128 ds = ds.where(account_session_status_filter) unless skip_status_checks? 129 ds.first 130 end
# File lib/rodauth/features/reset_password.rb 256 def _account_from_reset_password_key(token) 257 account_from_key(token, account_open_status_value){|id| get_password_reset_key(id)} 258 end
# File lib/rodauth/features/base.rb 808 def _account_from_session 809 ds = account_ds(session_value) 810 ds = ds.where(account_session_status_filter) unless skip_status_checks? 811 ds.first 812 end
# File lib/rodauth/features/lockout.rb 303 def _account_from_unlock_key(token) 304 account_from_key(token){|id| account_lockouts_ds(id).get(account_lockouts_key_column)} 305 end
# File lib/rodauth/features/verify_account.rb 307 def _account_from_verify_account_key(token) 308 account_from_key(token, account_unverified_status_value){|id| get_verify_account_key(id)} 309 end
# File lib/rodauth/features/verify_login_change.rb 210 def _account_from_verify_login_change_key(token) 211 account_from_key(token) do |id| 212 @verify_login_change_new_login, key = get_verify_login_change_login_and_key(id) 213 key 214 end 215 end
# File lib/rodauth/features/jwt_refresh.rb 132 def _account_refresh_token_split(token) 133 id, token = split_token(token) 134 id = convert_token_id(id) 135 return unless id && token 136 137 token_id, key = split_token(token) 138 token_id = convert_token_id(token_id) 139 return unless token_id && key 140 141 [id, token_id, key] 142 end
# File lib/rodauth/features/base.rb 559 def _around_rodauth 560 yield 561 end
# File lib/rodauth/features/email_auth.rb 183 def _email_auth_request 184 if email_auth_email_recently_sent? 185 set_redirect_error_flash email_auth_email_recently_sent_error_flash 186 redirect email_auth_email_recently_sent_redirect 187 end 188 189 generate_email_auth_key_value 190 transaction do 191 before_email_auth_request 192 create_email_auth_key 193 send_email_auth_email 194 after_email_auth_request 195 end 196 197 email_auth_email_sent_response 198 end
# File lib/rodauth/features/base.rb 833 def _field_attributes(field) 834 nil 835 end
# File lib/rodauth/features/base.rb 837 def _field_error_attributes(field) 838 " aria-invalid=\"true\" aria-describedby=\"#{field}_error_message\" " 839 end
# File lib/rodauth/features/base.rb 928 def _filter_links(links) 929 links.select!{|_, link| link} 930 links.sort! 931 links 932 end
# File lib/rodauth/features/base.rb 841 def _formatted_field_error(field, error) 842 "<span class=\"#{input_field_error_message_class}\" id=\"#{field}_error_message\">#{error}</span>" 843 end
# File lib/rodauth/features/remember.rb 237 def _get_remember_cookie 238 request.cookies[remember_cookie_key] 239 end
# File lib/rodauth/features/json.rb 220 def _json_response_body(hash) 221 request.send(:convert_to_json, hash) 222 end
# File lib/rodauth/features/jwt.rb 104 def _jwt_decode_opts 105 jwt_decode_opts 106 end
# File lib/rodauth/features/jwt.rb 109 def _jwt_decode_secrets 110 secrets = [jwt_secret, jwt_old_secret] 111 secrets.compact! 112 secrets 113 end
# File lib/rodauth/features/login.rb 168 def _login(auth_type) 169 warn("Deprecated #_login method called, use #login instead.") 170 login(auth_type) 171 end
# File lib/rodauth/features/login.rb 148 def _login_response 149 set_notice_flash login_notice_flash 150 redirect(saved_login_redirect || login_redirect) 151 end
# File lib/rodauth/features/base.rb 979 def _merge_fixed_locals_opts(opts, fixed_locals) 980 if use_template_fixed_locals? && !opts[:locals] 981 fixed_locals_opts = {default_fixed_locals: fixed_locals} 982 fixed_locals_opts.merge!(opts[:template_opts]) if opts[:template_opts] 983 opts[:template_opts] = fixed_locals_opts 984 end 985 end
# File lib/rodauth/features/email_auth.rb 177 def _multi_phase_login_forms 178 forms = super 179 forms << [30, email_auth_request_form, :_email_auth_request] if valid_login_entered? && allow_email_auth? 180 forms 181 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 446 def _otp 447 _otp_for_key(otp_user_key) 448 end
# File lib/rodauth/features/otp.rb 431 def _otp_add_key(secret) 432 # Uniqueness errors can't be handled here, as we can't be sure the secret provided 433 # is the same as the current secret. 434 otp_key_ds.insert(otp_keys_id_column=>session_value, otp_keys_column=>secret) 435 end
# File lib/rodauth/features/otp.rb 442 def _otp_for_key(key) 443 otp_class.new(key, :issuer=>otp_issuer, :digits=>otp_digits, :interval=>otp_interval) 444 end
# File lib/rodauth/features/otp.rb 423 def _otp_interval 424 otp_interval || 30 425 end
# File lib/rodauth/features/otp.rb 437 def _otp_key 438 @otp_user_key = nil 439 otp_key_ds.get(otp_keys_column) 440 end
# File lib/rodauth/features/otp.rb 417 def _otp_tmp_key(secret) 418 @otp_tmp_key = true 419 @otp_user_key = nil 420 @otp_key = secret 421 end
# File lib/rodauth/features/otp.rb 262 def _otp_valid_code?(ot_pass, otp) 263 return false unless otp_exists? 264 ot_pass = ot_pass.gsub(/\s+/, '') 265 if drift = otp_drift 266 if otp.respond_to?(:verify_with_drift) 267 # :nocov: 268 otp.verify_with_drift(ot_pass, drift) 269 # :nocov: 270 else 271 otp.verify(ot_pass, :drift_behind=>drift, :drift_ahead=>drift) 272 end 273 else 274 otp.verify(ot_pass) 275 end 276 end
Called for valid OTP codes for old secrets
# File lib/rodauth/features/otp.rb 428 def _otp_valid_code_for_old_secret 429 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 563 def _process_raw_hmac(hmac) 564 s = [hmac].pack('m') 565 s.chomp!("=\n") 566 s.tr!('+/', '-_') 567 s 568 end
# File lib/rodauth/features/recovery_codes.rb 260 def _recovery_codes 261 recovery_codes_ds.select_map(recovery_codes_column) 262 end
# File lib/rodauth/features/json.rb 210 def _return_json_response 211 response.status ||= json_response_error_status if json_response_error? 212 response.headers[convert_response_header_key('content-type')] ||= json_response_content_type 213 return_response _json_response_body(json_response) 214 end
# File lib/rodauth/features/json.rb 80 def _set_otp_unlock_info 81 if use_json? 82 json_response[:num_successes] = otp_unlock_num_successes 83 json_response[:required_successes] = otp_unlock_auths_required 84 json_response[:next_attempt_after] = otp_unlock_next_auth_attempt_after.to_i 85 end 86 end
# File lib/rodauth/features/remember.rb 186 def _set_remember_cookie(account_id, remember_key_value, deadline) 187 opts = Hash[remember_cookie_options] 188 opts[:value] = "#{account_id}_#{convert_token_key(remember_key_value)}" 189 opts[:expires] = convert_timestamp(deadline) 190 opts[:path] = "/" unless opts.key?(:path) 191 opts[:httponly] = true unless opts.key?(:httponly) || opts.key?(:http_only) 192 opts[:secure] = true unless opts.key?(:secure) || !request.ssl? 193 ::Rack::Utils.set_cookie_header!(response.headers, remember_cookie_key, opts) 194 end
# File lib/rodauth/features/lockout.rb 170 def _setup_account_lockouts_hash(account_id, key) 171 hash = {account_lockouts_id_column=>account_id, account_lockouts_key_column=>key} 172 set_deadline_value(hash, account_lockouts_deadline_column, account_lockouts_deadline_interval) 173 hash 174 end
# File lib/rodauth/features/sms_codes.rb 472 def _sms_request_response 473 set_notice_flash sms_request_notice_flash 474 redirect sms_auth_redirect 475 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 989 def _template_opts(opts, page) 990 opts = scope.send(:find_template, scope.send(:parse_template_opts, page, opts)) 991 unless File.file?(scope.send(:template_path, opts)) 992 opts[:path] = template_path(page) 993 end 994 opts 995 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 248 def _two_factor_auth_response 249 saved_two_factor_auth_redirect = remove_session_value(two_factor_auth_redirect_session_key) 250 set_notice_flash two_factor_auth_notice_flash 251 redirect(saved_two_factor_auth_redirect || two_factor_auth_redirect) 252 end
# File lib/rodauth/features/otp.rb 365 def _two_factor_remove_all_from_session 366 two_factor_remove_session('totp') 367 super 368 end
# File lib/rodauth/features/otp.rb 359 def _two_factor_remove_links 360 links = super 361 links << [20, otp_disable_path, otp_disable_link_text] if otp_exists? 362 links 363 end
# File lib/rodauth/features/otp.rb 353 def _two_factor_setup_links 354 links = super 355 links << [20, otp_setup_path, otp_setup_link_text] unless otp_exists? 356 links 357 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 997 def _view(meth, page) 998 unless scope.respond_to?(meth) 999 raise ConfigurationError, "attempted to render a built-in view/email template (#{page.inspect}), but rendering is disabled" 1000 end 1001 1002 scope.send(meth, _view_opts(page)) 1003 end
# File lib/rodauth/features/base.rb 969 def _view_opts(page) 970 opts = template_opts.dup 971 _merge_fixed_locals_opts(opts, '(rodauth: self.rodauth)') 972 opts[:locals] = opts[:locals] ? opts[:locals].dup : {} 973 opts[:locals][:rodauth] = self 974 opts[:cache] = cache_templates 975 opts[:cache_key] = :"rodauth_#{page}" 976 _template_opts(opts, page) 977 end
# File lib/rodauth/features/base.rb 387 def account! 388 account || (session_value && account_from_session) 389 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 853 def account_ds(id=account_id) 854 raise ArgumentError, "invalid account id passed to account_ds" unless id 855 ds = account_table_ds.where(account_id_column=>id) 856 ds = ds.select(*account_select) if account_select 857 ds 858 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 131 def account_from_email_auth_key(key) 132 @account = _account_from_email_auth_key(key) 133 end
# File lib/rodauth/features/base.rb 395 def account_from_id(id, status_id=nil) 396 @account = _account_from_id(id, status_id) 397 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 282 def account_from_login(login) 283 @account = _account_from_login(login) 284 end
# File lib/rodauth/features/jwt_refresh.rb 88 def account_from_refresh_token(token) 89 @account = _account_from_refresh_token(token) 90 end
# File lib/rodauth/features/remember.rb 217 def account_from_remember_cookie 218 unless id = remembered_session_id 219 # Only set expired cookie if there is already a cookie set. 220 forget_login if _get_remember_cookie 221 return 222 end 223 224 set_session_value(session_key, id) 225 account_from_session 226 remove_session_value(session_key) 227 228 unless account 229 remove_remember_key(id) 230 forget_login 231 return 232 end 233 234 account 235 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 391 def account_from_session 392 @account = _account_from_session 393 end
# File lib/rodauth/features/lockout.rb 221 def account_from_unlock_key(key) 222 @account = _account_from_unlock_key(key) 223 end
# File lib/rodauth/features/verify_account.rb 196 def account_from_verify_account_key(key) 197 @account = _account_from_verify_account_key(key) 198 end
# File lib/rodauth/features/verify_login_change.rb 120 def account_from_verify_login_change_key(key) 121 @account = _account_from_verify_login_change_key(key) 122 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 272 def account_id 273 account[account_id_column] 274 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 383 def account_initial_status_value 384 account_open_status_value 385 end
# File lib/rodauth/features/lockout.rb 299 def account_lockouts_ds(id=account_id) 300 db[account_lockouts_table].where(account_lockouts_id_column=>id) 301 end
# File lib/rodauth/features/lockout.rb 295 def account_login_failures_ds 296 db[account_login_failures_table].where(account_login_failures_id_column=>account_id) 297 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 306 def account_password_hash_column 307 nil 308 end
# File lib/rodauth/features/base.rb 845 def account_session_status_filter 846 {account_status_column=>account_open_status_value} 847 end
# File lib/rodauth/features/base.rb 860 def account_table_ds 861 db[accounts_table] 862 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 265 def active_remember_key_ds(id=account_id) 266 remember_key_ds(id).where(Sequel.expr(remember_deadline_column) > Sequel::CURRENT_TIMESTAMP) 267 end
# File lib/rodauth/features/active_sessions.rb 204 def active_sessions_ds 205 db[active_sessions_table]. 206 where(active_sessions_account_id_column=>session_value || account_id) 207 end
# File lib/rodauth/features/active_sessions.rb 167 def active_sessions_insert_hash 168 {active_sessions_account_id_column => session_value, active_sessions_session_id_column => compute_hmac(active_sessions_key)} 169 end
# File lib/rodauth/features/active_sessions.rb 171 def active_sessions_update_hash 172 h = {active_sessions_last_use_column => Sequel::CURRENT_TIMESTAMP} 173 174 if hmac_secret_rotation? 175 h[active_sessions_session_id_column] = compute_hmac(session[session_id_session_key]) 176 end 177 178 h 179 end
# File lib/rodauth/features/active_sessions.rb 68 def add_active_session 69 key = generate_active_sessions_key 70 set_session_value(session_id_session_key, key) 71 if e = raises_uniqueness_violation?{active_sessions_ds.insert(active_sessions_insert_hash)} 72 handle_duplicate_active_session_id(e) 73 end 74 nil 75 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 187 def add_field_error_class(field) 188 if field_error(field) 189 " #{input_field_error_class}" 190 end 191 end
# File lib/rodauth/features/disallow_password_reuse.rb 25 def add_previous_password_hash(hash) 26 ds = previous_password_ds 27 28 unless @dont_check_previous_password 29 keep_before = ds.reverse(previous_password_id_column). 30 limit(nil, previous_passwords_to_check). 31 get(previous_password_id_column) 32 33 if keep_before 34 ds.where(Sequel.expr(previous_password_id_column) <= keep_before). 35 delete 36 end 37 end 38 39 # This should never raise uniqueness violations, as it uses a serial primary key 40 ds.insert(previous_password_account_id_column=>account_id, previous_password_hash_column=>hash) 41 end
# File lib/rodauth/features/recovery_codes.rb 187 def add_recovery_code 188 # This should never raise uniqueness violations unless the recovery code is the same, and the odds of that 189 # are 1/256**32 assuming a good random number generator. Still, attempt to handle that case by retrying 190 # on such a uniqueness violation. 191 retry_on_uniqueness_violation do 192 recovery_codes_ds.insert(recovery_codes_id_column=>session_value, recovery_codes_column=>new_recovery_code) 193 end 194 end
# File lib/rodauth/features/recovery_codes.rb 177 def add_recovery_codes(number) 178 return if number <= 0 179 transaction do 180 number.times do 181 add_recovery_code 182 end 183 end 184 remove_instance_variable(:@recovery_codes) 185 end
# File lib/rodauth/features/remember.rb 159 def add_remember_key 160 hash = {remember_id_column=>account_id, remember_key_column=>remember_key_value} 161 set_deadline_value(hash, remember_deadline_column, remember_deadline_interval) 162 163 if e = raised_uniqueness_violation{remember_key_ds.insert(hash)} 164 # If inserting into the remember key table causes a violation, we can pull the 165 # existing row from the table. If there is no invalid row, we can then reraise. 166 raise e unless @remember_key_value = active_remember_key_ds.get(remember_key_column) 167 end 168 end
# File lib/rodauth/features/recovery_codes.rb 151 def add_webauthn_credential(_) 152 super if defined?(super) 153 auto_add_missing_recovery_codes 154 end
# File lib/rodauth/features/change_password_notify.rb 11 def after_change_password 12 super 13 send_password_changed_email 14 end
# File lib/rodauth/features/account_expiration.rb 99 def after_close_account 100 super if defined?(super) 101 account_activity_ds(account_id).delete 102 end
# File lib/rodauth/features/disallow_password_reuse.rb 87 def after_create_account 88 if account_password_hash_column && !(respond_to?(:verify_account_set_password?) && verify_account_set_password?) 89 add_previous_password_hash(password_hash(param(password_param))) 90 end 91 super if defined?(super) 92 end
# File lib/rodauth/features/email_auth.rb 206 def after_login 207 # Remove the email auth key after any login, even if 208 # it is a password login. This is done to invalidate 209 # the email login when a user has a password and requests 210 # email authentication, but then remembers their password 211 # and doesn't need the link. At that point, the link 212 # that allows login access to the account becomes a 213 # security liability, and it is best to remove it. 214 remove_email_auth_key 215 super 216 end
# File lib/rodauth/features/email_auth.rb 149 def after_login_entered_during_multi_phase_login 150 # If forcing email auth, just send the email link. 151 _email_auth_request if force_email_auth? 152 153 super 154 end
# File lib/rodauth/features/lockout.rb 266 def after_login_failure 267 invalid_login_attempted 268 super 269 end
# File lib/rodauth/features/remember.rb 241 def after_logout 242 forget_login 243 super if defined?(super) 244 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/otp_modify_email.rb 13 def after_otp_setup 14 super 15 send_otp_setup_email 16 end
# File lib/rodauth/features/json.rb 96 def after_otp_unlock_auth_failure 97 super if defined?(super) 98 _set_otp_unlock_info 99 end
# File lib/rodauth/features/json.rb 88 def after_otp_unlock_auth_success 89 super if defined?(super) 90 if otp_locked_out? 91 _set_otp_unlock_info 92 json_response[:deadline] = otp_unlock_deadline.to_i 93 end 94 end
# File lib/rodauth/features/json.rb 101 def after_otp_unlock_not_yet_available 102 super if defined?(super) 103 _set_otp_unlock_info 104 end
# File lib/rodauth/features/active_sessions.rb 135 def after_refresh_token 136 super if defined?(super) 137 if prev_key = session[session_id_session_key] 138 key = generate_active_sessions_key 139 set_session_value(session_id_session_key, key) 140 active_sessions_ds. 141 where(active_sessions_session_id_column => compute_hmacs(prev_key)). 142 update(active_sessions_session_id_column => compute_hmac(key)) 143 end 144 end
# File lib/rodauth/features/password_grace_period.rb 39 def after_reset_password 40 super if defined?(super) 41 @last_password_entry = Time.now.to_i 42 end
# File lib/rodauth/features/recovery_codes.rb 230 def after_sms_disable 231 super if defined?(super) 232 auto_remove_recovery_codes 233 end
# File lib/rodauth/features/recovery_codes.rb 235 def after_webauthn_remove 236 super if defined?(super) 237 auto_remove_recovery_codes 238 end
# File lib/rodauth/features/webauthn_modify_email.rb 13 def after_webauthn_setup 14 super 15 send_webauthn_authenticator_added_email 16 end
# File lib/rodauth/features/email_auth.rb 202 def allow_email_auth? 203 defined?(super) ? super : true 204 end
# File lib/rodauth/features/verify_account.rb 163 def allow_resending_verify_account_email? 164 account[account_status_column] == account_unverified_status_value 165 end
# File lib/rodauth/features/base.rb 314 def already_logged_in 315 nil 316 end
:nocov:
# File lib/rodauth/features/argon2.rb 105 def argon2_hash_algorithm?(hash) 106 hash.start_with?('$argon2id$') 107 end
# File lib/rodauth/features/argon2.rb 76 def argon2_hash_cost 77 { t_cost: 1, m_cost: 5, p_cost: 1 } 78 end
# File lib/rodauth/features/argon2.rb 109 def argon2_password_hash_match?(hash, password) 110 ret = ::Argon2::Password.verify_password(password, hash, argon2_secret) 111 112 if ret == false && argon2_old_secret != argon2_secret && (ret = ::Argon2::Password.verify_password(password, hash, argon2_old_secret)) 113 @update_password_hash = true 114 end 115 116 ret 117 end
# File lib/rodauth/features/argon2.rb 60 def argon2_password_hash_using_salt_and_secret(password, salt, secret) 61 argon2_params = Hash[extract_password_hash_cost(salt)] 62 argon2_params[argon2_salt_option] = salt.split('$').last.unpack("m")[0] 63 argon2_params[:secret] = secret 64 ::Argon2::Password.new(argon2_params).create(password) 65 end
# File lib/rodauth/features/argon2.rb 34 def argon2_salt_option 35 :salt_do_not_supply 36 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 137 def auth_class_eval(&block) 138 auth.class_eval(&block) 139 end
# File lib/rodauth/features/base.rb 370 def authenticated? 371 logged_in? 372 end
# File lib/rodauth/features/base.rb 474 def authenticated_by 475 session[authenticated_by_session_key] 476 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 248 def auto_add_missing_recovery_codes 249 if auto_add_recovery_codes? 250 add_recovery_codes(recovery_codes_limit - recovery_codes.length) 251 end 252 end
# File lib/rodauth/features/recovery_codes.rb 254 def auto_remove_recovery_codes 255 if auto_remove_recovery_codes? && (%w'totp webauthn sms_code' & possible_authentication_methods).empty? 256 recovery_codes_remove 257 end 258 end
# File lib/rodauth/features/base.rb 217 def autocomplete_for_field?(_param) 218 mark_input_fields_with_autocomplete? 219 end
# File lib/rodauth/features/base.rb 487 def autologin_session(autologin_type) 488 login_session('autologin') 489 set_session_value(autologin_type_session_key, autologin_type) 490 end
# File lib/rodauth/features/base.rb 483 def autologin_type 484 session[autologin_type_session_key] 485 end
# File lib/rodauth/features/otp.rb 412 def base32_encode(data, length) 413 chars = 'abcdefghijklmnopqrstuvwxyz234567' 414 length.times.map{|i|chars[data[i].ord % 32]}.join 415 end
# File lib/rodauth/features/base.rb 533 def base_url 534 url = String.new("#{request.scheme}://#{domain}") 535 url << ":#{request.port}" if request.port != Rack::Request::DEFAULT_PORTS[request.scheme] 536 url 537 end
# File lib/rodauth/features/verify_account_grace_period.rb 58 def before_change_login_route 59 unless verified_account? 60 set_redirect_error_flash unverified_change_login_error_flash 61 redirect unverified_change_login_redirect 62 end 63 super if defined?(super) 64 end
# File lib/rodauth/features/password_expiration.rb 90 def before_change_password_route 91 check_password_change_allowed 92 super 93 end
# File lib/rodauth/features/disallow_password_reuse.rb 77 def before_create_account_route 78 super if defined?(super) 79 @dont_check_previous_password = true 80 end
# File lib/rodauth/features/lockout.rb 254 def before_login_attempt 255 if locked_out? 256 show_lockout_page 257 end 258 super 259 end
# File lib/rodauth/features/active_sessions.rb 151 def before_logout 152 if param_or_nil(global_logout_param) 153 remove_remember_key(session_value) if respond_to?(:remove_remember_key) 154 remove_all_active_sessions 155 else 156 remove_current_session 157 end 158 super 159 end
# File lib/rodauth/features/json.rb 170 def before_otp_setup_route 171 super if defined?(super) 172 if use_json? && otp_keys_use_hmac? && !param_or_nil(otp_setup_raw_param) 173 _otp_tmp_key(otp_new_secret) 174 json_response[otp_setup_param] = otp_user_key 175 json_response[otp_setup_raw_param] = otp_key 176 end 177 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 179 def before_rodauth 180 if json_request? 181 if json_check_accept? && (accept = request.env['HTTP_ACCEPT']) && accept !~ json_accept_regexp 182 response.status = 406 183 json_response[json_response_error_key] = json_not_accepted_error_message 184 _return_json_response 185 end 186 187 unless request.post? 188 response.status = 405 189 set_response_header('allow', 'POST') 190 json_response[json_response_error_key] = json_non_post_error_message 191 return_json_response 192 end 193 elsif only_json? 194 response.status = json_response_error_status 195 return_response non_json_request_error_message 196 end 197 198 super 199 end
# File lib/rodauth/features/json.rb 116 def before_two_factor_auth_route 117 super if defined?(super) 118 if use_json? 119 json_response[:auth_links] = two_factor_auth_links.sort.map{|_,link| link} 120 json_response[json_response_success_key] ||= "" if include_success_messages? 121 return_json_response 122 end 123 end
# File lib/rodauth/features/json.rb 106 def before_two_factor_manage_route 107 super if defined?(super) 108 if use_json? 109 json_response[:setup_links] = two_factor_setup_links.sort.map{|_,link| link} 110 json_response[:remove_links] = two_factor_remove_links.sort.map{|_,link| link} 111 json_response[json_response_success_key] ||= "" if include_success_messages? 112 return_json_response 113 end 114 end
# File lib/rodauth/features/account_expiration.rb 89 def before_unlock_account 90 check_account_expiration 91 super if defined?(super) 92 end
# File lib/rodauth/features/account_expiration.rb 94 def before_unlock_account_request 95 check_account_expiration 96 super if defined?(super) 97 end
# File lib/rodauth/features/webauthn_verify_account.rb 30 def before_verify_account 31 super 32 if features.include?(:json) && use_json? && !param_or_nil(webauthn_setup_param) 33 cred = new_webauthn_credential 34 json_response[webauthn_setup_param] = cred.as_json 35 json_response[webauthn_setup_challenge_param] = cred.challenge 36 json_response[webauthn_setup_challenge_hmac_param] = compute_hmac(cred.challenge) 37 end 38 @webauthn_credential = webauthn_setup_credential_from_form_submission 39 add_webauthn_credential(@webauthn_credential) 40 end
# File lib/rodauth/features/disallow_password_reuse.rb 82 def before_verify_account_route 83 super if defined?(super) 84 @dont_check_previous_password = true 85 end
# File lib/rodauth/features/json.rb 125 def before_view_recovery_codes 126 super if defined?(super) 127 if use_json? 128 json_response[:codes] = recovery_codes 129 json_response[json_response_success_key] ||= "" if include_success_messages? 130 end 131 end
# File lib/rodauth/features/json.rb 143 def before_webauthn_auth_route 144 super if defined?(super) 145 if use_json? && !param_or_nil(webauthn_auth_param) 146 cred = webauthn_credential_options_for_get 147 json_response[webauthn_auth_param] = cred.as_json 148 json_response[webauthn_auth_challenge_param] = cred.challenge 149 json_response[webauthn_auth_challenge_hmac_param] = compute_hmac(cred.challenge) 150 end 151 end
# File lib/rodauth/features/json.rb 153 def before_webauthn_login_route 154 super if defined?(super) 155 if use_json? && !param_or_nil(webauthn_auth_param) && webauthn_login_options? 156 cred = webauthn_credential_options_for_get 157 json_response[webauthn_auth_param] = cred.as_json 158 json_response[webauthn_auth_challenge_param] = cred.challenge 159 json_response[webauthn_auth_challenge_hmac_param] = compute_hmac(cred.challenge) 160 end 161 end
# File lib/rodauth/features/json.rb 163 def before_webauthn_remove_route 164 super if defined?(super) 165 if use_json? && !param_or_nil(webauthn_remove_param) 166 json_response[webauthn_remove_param] = account_webauthn_usage 167 end 168 end
# File lib/rodauth/features/json.rb 133 def before_webauthn_setup_route 134 super if defined?(super) 135 if use_json? && !param_or_nil(webauthn_setup_param) 136 cred = new_webauthn_credential 137 json_response[webauthn_setup_param] = cred.as_json 138 json_response[webauthn_setup_challenge_param] = cred.challenge 139 json_response[webauthn_setup_challenge_hmac_param] = compute_hmac(cred.challenge) 140 end 141 end
# File lib/rodauth/features/base.rb 424 def button(value, opts={}) 425 scope.render(button_opts(value, opts)) 426 end
# File lib/rodauth/features/base.rb 607 def button_fixed_locals 608 '(value:, opts:)' 609 end
# File lib/rodauth/features/base.rb 415 def button_opts(value, opts) 416 opts = Hash[template_opts].merge!(opts) 417 _merge_fixed_locals_opts(opts, button_fixed_locals) 418 opts[:locals] = {:value=>value, :opts=>opts} 419 opts[:cache] = cache_templates 420 opts[:cache_key] = :rodauth_button 421 _template_opts(opts, 'button') 422 end
# File lib/rodauth/features/recovery_codes.rb 173 def can_add_recovery_codes? 174 recovery_codes.length < recovery_codes_limit 175 end
# File lib/rodauth/features/base.rb 707 def catch_error(&block) 708 catch(:rodauth_error, &block) 709 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 136 def change_login_notice_flash 137 change_login_needs_verification_notice_flash 138 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 54 def check_active_session 55 if logged_in? && !currently_active_session? 56 no_longer_active_session 57 end 58 end
# File lib/rodauth/features/base.rb 310 def check_already_logged_in 311 already_logged_in if logged_in? 312 end
# File lib/rodauth/features/base.rb 399 def check_csrf 400 scope.check_csrf!(check_csrf_opts, &check_csrf_block) 401 end
# File lib/rodauth/features/base.rb 769 def check_csrf? 770 scope.opts[:rodauth_route_csrf] 771 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 370 def clear_cached_otp 371 remove_instance_variable(:@otp) if defined?(@otp) 372 end
# File lib/rodauth/features/lockout.rb 166 def clear_invalid_login_attempts 167 unlock_account 168 end
# File lib/rodauth/features/base.rb 326 def clear_session 327 if use_scope_clear_session? 328 scope.clear_session 329 else 330 session.clear 331 end 332 end
# File lib/rodauth/features/active_sessions.rb 128 def clear_tokens(reason) 129 super 130 remove_all_active_sessions_except_current 131 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 251 def compute_hmac(data) 252 _process_raw_hmac(compute_raw_hmac(data)) 253 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 262 def compute_hmacs(data) 263 hmacs = [compute_hmac(data)] 264 265 if hmac_old_secret 266 hmacs << compute_old_hmac(data) 267 end 268 269 hmacs 270 end
Return urlsafe base64 HMAC for data using hmac_old_secret, assumes hmac_old_secret is set.
# File lib/rodauth/features/base.rb 256 def compute_old_hmac(data) 257 _process_raw_hmac(compute_raw_hmac_with_secret(data, hmac_old_secret)) 258 end
# File lib/rodauth/features/base.rb 824 def compute_raw_hmac(data) 825 raise ConfigurationError, "hmac_secret not set" unless hmac_secret 826 compute_raw_hmac_with_secret(data, hmac_secret) 827 end
# File lib/rodauth/features/base.rb 829 def compute_raw_hmac_with_secret(data, secret) 830 OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, secret, data) 831 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 575 def convert_response_header_key(key) 576 key 577 end
# File lib/rodauth/features/base.rb 686 def convert_session_key(key) 687 key = :"#{session_key_prefix}#{key}" if session_key_prefix 688 normalize_session_or_flash_key(key) 689 end
This is needed for jdbc/sqlite, which returns timestamp columns as strings
# File lib/rodauth/features/base.rb 869 def convert_timestamp(timestamp) 870 timestamp = db.to_application_timestamp(timestamp) if timestamp.is_a?(String) 871 timestamp 872 end
# File lib/rodauth/features/base.rb 640 def convert_token_id(id) 641 if convert_token_id_to_integer? 642 convert_token_id_to_integer(id) 643 else 644 id 645 end 646 end
# File lib/rodauth/features/base.rb 648 def convert_token_id_to_integer(id) 649 if id = (Integer(id, 10) rescue nil) 650 if id > 9223372036854775807 || id < -9223372036854775808 651 # Only allow 64-bit signed integer range to avoid problems on PostgreSQL 652 id = nil 653 end 654 end 655 656 id 657 end
# File lib/rodauth/features/base.rb 628 def convert_token_key(key) 629 if key && hmac_secret 630 compute_hmac(key) 631 else 632 key 633 end 634 end
# File lib/rodauth/features/verify_account.rb 216 def create_account_autologin? 217 false 218 end
# File lib/rodauth/features/verify_account.rb 183 def create_account_notice_flash 184 verify_account_email_sent_notice_flash 185 end
# File lib/rodauth/features/verify_account.rb 220 def create_account_set_password? 221 return false if verify_account_set_password? 222 super 223 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 102 def create_email_auth_key 103 transaction do 104 if email_auth_key_value = get_email_auth_key(account_id) 105 set_email_auth_email_last_sent 106 @email_auth_key_value = email_auth_key_value 107 elsif e = raised_uniqueness_violation{email_auth_ds.insert(email_auth_key_insert_hash)} 108 # If inserting into the email auth table causes a violation, we can pull the 109 # existing email auth key from the table, or reraise. 110 raise e unless @email_auth_key_value = get_email_auth_key(account_id) 111 end 112 end 113 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 163 def create_reset_password_key 164 transaction do 165 if reset_password_key_value = get_password_reset_key(account_id) 166 set_reset_password_email_last_sent 167 @reset_password_key_value = reset_password_key_value 168 elsif e = raised_uniqueness_violation{password_reset_ds.insert(reset_password_key_insert_hash)} 169 # If inserting into the reset password table causes a violation, we can pull the 170 # existing reset password key from the table, or reraise. 171 raise e unless @reset_password_key_value = get_password_reset_key(account_id) 172 end 173 end 174 end
# File lib/rodauth/features/verify_account.rb 286 def create_verify_account_key 287 ds = verify_account_ds 288 transaction do 289 if ds.empty? 290 if e = raised_uniqueness_violation{ds.insert(verify_account_key_insert_hash)} 291 # If inserting into the verify account table causes a violation, we can pull the 292 # key from the verify account table, or reraise. 293 raise e unless @verify_account_key_value = get_verify_account_key(account_id) 294 end 295 end 296 end 297 end
# File lib/rodauth/features/verify_login_change.rb 198 def create_verify_login_change_email(login) 199 create_email_to(login, verify_login_change_email_subject, verify_login_change_email_body) 200 end
# File lib/rodauth/features/verify_login_change.rb 176 def create_verify_login_change_key(login) 177 ds = verify_login_change_ds 178 transaction do 179 ds.where((Sequel::CURRENT_TIMESTAMP > verify_login_change_deadline_column) | ~Sequel.expr(verify_login_change_login_column=>login)).delete 180 if e = raised_uniqueness_violation{ds.insert(verify_login_change_key_insert_hash(login))} 181 old_login, key = get_verify_login_change_login_and_key(account_id) 182 # If inserting into the verify login change table causes a violation, we can pull the 183 # key from the verify login change table if the logins match, or reraise. 184 @verify_login_change_key_value = if old_login.downcase == login.downcase 185 key 186 end 187 raise e unless @verify_login_change_key_value 188 end 189 end 190 end
# File lib/rodauth/features/base.rb 403 def csrf_tag(path=request.path) 404 return unless scope.respond_to?(:csrf_tag) 405 406 if use_request_specific_csrf_tokens? 407 scope.csrf_tag(path) 408 else 409 # :nocov: 410 scope.csrf_tag 411 # :nocov: 412 end 413 end
# File lib/rodauth/features/active_sessions.rb 40 def currently_active_session? 41 return false unless session_id = session[session_id_session_key] 42 43 remove_inactive_sessions 44 ds = active_sessions_ds. 45 where(active_sessions_session_id_column => compute_hmacs(session_id)) 46 47 if update_current_session? 48 ds.update(active_sessions_update_hash) == 1 49 else 50 ds.count == 1 51 end 52 end
# File lib/rodauth/features/argon2.rb 119 def database_function_password_match?(name, hash_id, password, salt) 120 return true if super 121 122 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)))) 123 @update_password_hash = true 124 end 125 126 !!ret 127 end
# File lib/rodauth/features/base.rb 290 def db 291 Sequel::DATABASES.first or raise "Sequel database connection is missing" 292 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 155 def disable_remember_login 156 remove_remember_key 157 end
# File lib/rodauth/features/email_auth.rb 232 def email_auth_ds(id=account_id) 233 db[email_auth_table].where(email_auth_id_column=>id) 234 end
# File lib/rodauth/features/email_auth.rb 135 def email_auth_email_link 136 token_link(email_auth_route, email_auth_key_param, email_auth_key_value) 137 end
# File lib/rodauth/features/email_auth.rb 166 def email_auth_email_recently_sent? 167 (email_last_sent = get_email_auth_email_last_sent) && (Time.now - email_last_sent < email_auth_skip_resend_email_within) 168 end
# File lib/rodauth/features/email_auth.rb 226 def email_auth_key_insert_hash 227 hash = {email_auth_id_column=>account_id, email_auth_key_column=>email_auth_key_value} 228 set_deadline_value(hash, email_auth_deadline_column, email_auth_deadline_interval) 229 hash 230 end
# File lib/rodauth/features/email_auth.rb 145 def email_auth_request_form 146 render('email-auth-request-form') 147 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 212 def extend_remember_deadline 213 active_remember_key_ds.update(remember_deadline_column=>Sequel.date_add(Sequel::CURRENT_TIMESTAMP, remember_period)) 214 remember_login 215 end
# File lib/rodauth/features/remember.rb 200 def extend_remember_deadline_while_logged_in? 201 return false unless extend_remember_deadline? 202 203 if extended_at = session[remember_deadline_extended_session_key] 204 extended_at + extend_remember_deadline_period < Time.now.to_i 205 elsif logged_in_via_remember_key? 206 # Handle existing sessions before the change to extend remember deadline 207 # while logged in. 208 true 209 end 210 end
# File lib/rodauth/features/argon2.rb 68 def extract_password_hash_cost(hash) 69 return super unless argon2_hash_algorithm?(hash) 70 71 /\A\$argon2id\$v=\d+\$m=(\d+),t=(\d+),p=(\d+)/ =~ hash 72 { t_cost: $2.to_i, m_cost: Math.log2($1.to_i).to_i, p_cost: $3.to_i } 73 end
# File lib/rodauth/features/base.rb 150 def features 151 self.class.features 152 end
# File lib/rodauth/features/base.rb 225 def field_attributes(field) 226 _field_attributes(field) || default_field_attributes 227 end
# File lib/rodauth/features/base.rb 182 def field_error(field) 183 return nil unless @field_errors 184 @field_errors[field] 185 end
# File lib/rodauth/features/base.rb 229 def field_error_attributes(field) 230 if field_error(field) 231 _field_error_attributes(field) 232 end 233 end
# File lib/rodauth/features/remember.rb 138 def forget_login 139 opts = Hash[remember_cookie_options] 140 opts[:path] = "/" unless opts.key?(:path) 141 ::Rack::Utils.delete_cookie_header!(response.headers, remember_cookie_key, opts) 142 end
# File lib/rodauth/features/base.rb 235 def formatted_field_error(field) 236 if error = field_error(field) 237 _formatted_field_error(field, error) 238 end 239 end
# File lib/rodauth/features/base.rb 773 def function_name(name) 774 if db.database_type == :mssql 775 # :nocov: 776 "dbo.#{name}" 777 # :nocov: 778 else 779 name 780 end 781 end
# File lib/rodauth/features/active_sessions.rb 163 def generate_active_sessions_key 164 @active_sessions_key = random_key 165 end
# File lib/rodauth/features/email_auth.rb 218 def generate_email_auth_key_value 219 @email_auth_key_value = random_key 220 end
# File lib/rodauth/features/jwt_refresh.rb 190 def generate_refresh_token 191 hash = jwt_refresh_token_insert_hash 192 [account_id, jwt_refresh_token_ds.insert(hash), convert_token_key(hash[jwt_refresh_token_key_column])].join(token_separator) 193 end
# File lib/rodauth/features/remember.rb 253 def generate_remember_key_value 254 @remember_key_value = random_key 255 end
# File lib/rodauth/features/reset_password.rb 234 def generate_reset_password_key_value 235 @reset_password_key_value = random_key 236 end
# File lib/rodauth/features/lockout.rb 276 def generate_unlock_account_key 277 random_key 278 end
# File lib/rodauth/features/verify_account.rb 282 def generate_verify_account_key_value 283 @verify_account_key_value = random_key 284 end
# File lib/rodauth/features/verify_login_change.rb 172 def generate_verify_login_change_key_value 173 @verify_login_change_key_value = random_key 174 end
# File lib/rodauth/features/jwt_refresh.rb 163 def get_active_refresh_token(account_id, token_id) 164 jwt_refresh_token_account_ds(account_id). 165 where(Sequel::CURRENT_TIMESTAMP > jwt_refresh_token_deadline_column). 166 delete 167 168 jwt_refresh_token_account_token_ds(account_id, token_id). 169 get(jwt_refresh_token_key_column) 170 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 119 def get_email_auth_email_last_sent 120 if column = email_auth_email_last_sent_column 121 if ts = email_auth_ds.get(column) 122 convert_timestamp(ts) 123 end 124 end 125 end
# File lib/rodauth/features/email_auth.rb 139 def get_email_auth_key(id) 140 ds = email_auth_ds(id) 141 ds.where(Sequel::CURRENT_TIMESTAMP > email_auth_deadline_column).delete 142 ds.get(email_auth_key_column) 143 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 789 def get_password_hash 790 if account_password_hash_column 791 account[account_password_hash_column] if account! 792 elsif use_database_authentication_functions? 793 db.get(Sequel.function(function_name(:rodauth_get_salt), account ? account_id : session_value)) 794 else 795 # :nocov: 796 password_hash_ds.get(password_hash_column) 797 # :nocov: 798 end 799 end
# File lib/rodauth/features/reset_password.rb 192 def get_password_reset_key(id) 193 ds = password_reset_ds(id) 194 ds.where(Sequel::CURRENT_TIMESTAMP > reset_password_deadline_column).delete 195 ds.get(reset_password_key_column) 196 end
# File lib/rodauth/features/remember.rb 144 def get_remember_key 145 unless @remember_key_value = active_remember_key_ds.get(remember_key_column) 146 generate_remember_key_value 147 transaction do 148 remove_remember_key 149 add_remember_key 150 end 151 end 152 nil 153 end
# File lib/rodauth/features/reset_password.rb 202 def get_reset_password_email_last_sent 203 if column = reset_password_email_last_sent_column 204 if ts = password_reset_ds.get(column) 205 convert_timestamp(ts) 206 end 207 end 208 end
# File lib/rodauth/features/lockout.rb 229 def get_unlock_account_email_last_sent 230 if column = account_lockouts_email_last_sent_column 231 if ts = account_lockouts_ds.get(column) 232 convert_timestamp(ts) 233 end 234 end 235 end
# File lib/rodauth/features/lockout.rb 217 def get_unlock_account_key 218 account_lockouts_ds.get(account_lockouts_key_column) 219 end
# File lib/rodauth/features/verify_account.rb 229 def get_verify_account_email_last_sent 230 if column = verify_account_email_last_sent_column 231 if ts = verify_account_ds.get(column) 232 convert_timestamp(ts) 233 end 234 end 235 end
# File lib/rodauth/features/verify_account.rb 208 def get_verify_account_key(id) 209 verify_account_ds(id).get(verify_account_key_column) 210 end
# File lib/rodauth/features/verify_login_change.rb 132 def get_verify_login_change_login_and_key(id) 133 verify_login_change_ds(id).get([verify_login_change_login_column, verify_login_change_key_column]) 134 end
# File lib/rodauth/features/active_sessions.rb 77 def handle_duplicate_active_session_id(_e) 78 # Do nothing by default as session is already tracked. This will result in 79 # the current session and the existing session with the same id 80 # being tracked together, so that a logout of one will logout 81 # the other, and updating the last use on one will update the other, 82 # but this should be acceptable. However, this can be overridden if different 83 # behavior is desired. 84 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 551 def has_password? 552 return @has_password if defined?(@has_password) 553 return false unless account || session_value 554 @has_password = !!get_password_hash 555 end
# File lib/rodauth/features/base.rb 820 def hmac_secret_rotation? 821 hmac_secret && hmac_old_secret && hmac_secret != hmac_old_secret 822 end
# File lib/rodauth/features/audit_logging.rb 31 def hook_action(hook_type, action) 32 super 33 # In after_logout, session is already cleared, so use before_logout in that case 34 if (hook_type == :after || action == :logout) && (id = account ? account_id : session_value) 35 add_audit_log(id, action) 36 end 37 end
# File lib/rodauth/features/http_basic_auth.rb 34 def http_basic_auth 35 return @checked_http_basic_auth if defined?(@checked_http_basic_auth) 36 37 @checked_http_basic_auth = nil 38 return unless token = ((v = request.env['HTTP_AUTHORIZATION']) && v[/\A *Basic (.*)\Z/, 1]) 39 40 username, password = token.unpack("m*").first.split(/:/, 2) 41 return unless username && password 42 43 catch_error do 44 unless account_from_login(username) 45 throw_basic_auth_error(login_param, no_matching_login_message) 46 end 47 48 before_login_attempt 49 50 unless open_account? 51 throw_basic_auth_error(login_param, no_matching_login_message) 52 end 53 54 unless password_match?(password) 55 after_login_failure 56 throw_basic_auth_error(password_param, invalid_password_message) 57 end 58 59 transaction do 60 before_login 61 login_session('password') 62 after_login 63 end 64 65 @checked_http_basic_auth = true 66 return true 67 end 68 69 nil 70 end
# File lib/rodauth/features/active_sessions.rb 193 def inactive_session_cond 194 cond = session_inactivity_deadline_condition 195 cond2 = session_lifetime_deadline_condition 196 return false unless cond || cond2 197 Sequel.|(*[cond, cond2].compact) 198 end
# File lib/rodauth/features/json.rb 216 def include_success_messages? 217 !json_response_success_key.nil? 218 end
# File lib/rodauth/features/base.rb 193 def input_field_string(param, id, opts={}) 194 type = opts.fetch(:type, "text") 195 196 unless type == "password" 197 value = opts.fetch(:value){scope.h param(param)} 198 end 199 200 field_class = opts.fetch(:class, "form-control") 201 202 if autocomplete_for_field?(param) && opts[:autocomplete] 203 autocomplete = "autocomplete=\"#{opts[:autocomplete]}\"" 204 end 205 206 if inputmode_for_field?(param) && opts[:inputmode] 207 inputmode = "inputmode=\"#{opts[:inputmode]}\"" 208 end 209 210 if mark_input_fields_as_required? && opts[:required] != false 211 required = "required=\"required\"" 212 end 213 214 "<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]}" 215 end
# File lib/rodauth/features/base.rb 221 def inputmode_for_field?(_param) 222 mark_input_fields_with_inputmode? 223 end
# File lib/rodauth/features/base.rb 934 def internal_request? 935 false 936 end
# File lib/rodauth/features/internal_request.rb 373 def internal_request_configuration(&block) 374 @auth.instance_exec do 375 (@internal_request_configuration_blocks ||= []) << block 376 end 377 end
# File lib/rodauth/features/lockout.rb 176 def invalid_login_attempted 177 ds = account_login_failures_ds. 178 where(account_login_failures_id_column=>account_id) 179 180 number = if db.database_type == :postgres 181 ds.returning(account_login_failures_number_column). 182 with_sql(:update_sql, account_login_failures_number_column=>Sequel.expr(account_login_failures_number_column)+1). 183 single_value 184 else 185 # :nocov: 186 if ds.update(account_login_failures_number_column=>Sequel.expr(account_login_failures_number_column)+1) > 0 187 ds.get(account_login_failures_number_column) 188 end 189 # :nocov: 190 end 191 192 unless number 193 # Ignoring the violation is safe here. It may allow slightly more than max_invalid_logins invalid logins before 194 # lockout, but allowing a few extra is OK if the race is lost. 195 ignore_uniqueness_violation{account_login_failures_ds.insert(account_login_failures_id_column=>account_id)} 196 number = 1 197 end 198 199 if number >= max_invalid_logins 200 @unlock_account_key_value = generate_unlock_account_key 201 hash = _setup_account_lockouts_hash(account_id, unlock_account_key_value) 202 203 if e = raised_uniqueness_violation{account_lockouts_ds.insert(hash)} 204 # If inserting into the lockout table raises a violation, we should just be able to pull the already inserted 205 # key out of it. If that doesn't return a valid key, we should reraise the error. 206 raise e unless @unlock_account_key_value = account_lockouts_ds.get(account_lockouts_key_column) 207 208 after_account_lockout 209 show_lockout_page 210 else 211 after_account_lockout 212 e 213 end 214 end 215 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 55 def json_request? 56 return @json_request if defined?(@json_request) 57 @json_request = request.content_type =~ json_request_content_type_regexp 58 end
# File lib/rodauth/features/json.rb 224 def json_response 225 @json_response ||= {} 226 end
# File lib/rodauth/features/json.rb 69 def json_response_error? 70 !!json_response[json_response_error_key] 71 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 122 def jwt_payload 123 return @jwt_payload if defined?(@jwt_payload) 124 @jwt_payload = JWT.decode(jwt_token, _jwt_decode_secrets, true, _jwt_decode_opts.merge(:algorithm=>jwt_algorithm))[0] 125 rescue JWT::DecodeError => e 126 rescue_jwt_payload(e) 127 end
# File lib/rodauth/features/jwt_refresh.rb 172 def jwt_refresh_token_account_ds(account_id) 173 jwt_refresh_token_ds.where(jwt_refresh_token_account_id_column => account_id) 174 end
# File lib/rodauth/features/jwt_refresh.rb 176 def jwt_refresh_token_account_token_ds(account_id, token_id) 177 jwt_refresh_token_account_ds(account_id). 178 where(jwt_refresh_token_id_column=>token_id) 179 end
# File lib/rodauth/features/jwt_refresh.rb 181 def jwt_refresh_token_ds 182 db[jwt_refresh_token_table] 183 end
# File lib/rodauth/features/jwt_refresh.rb 195 def jwt_refresh_token_insert_hash 196 hash = {jwt_refresh_token_account_id_column => account_id, jwt_refresh_token_key_column => random_key} 197 set_deadline_value(hash, jwt_refresh_token_deadline_column, jwt_refresh_token_deadline_interval) 198 hash 199 end
# File lib/rodauth/features/jwt_refresh.rb 152 def jwt_refresh_token_match?(key) 153 # We don't need to match tokens if we are requiring a valid current access token 154 return true unless allow_refresh_with_expired_jwt_access_token? 155 156 # If allowing with expired jwt access token, check the expired session contains 157 # hmac matching submitted and active refresh token. 158 s = session[jwt_refresh_token_hmac_session_key].to_s 159 h = session[jwt_refresh_token_data_session_key].to_s + key 160 timing_safe_eql?(compute_hmac(h), s) || (hmac_secret_rotation? && timing_safe_eql?(compute_old_hmac(h), s)) 161 end
# File lib/rodauth/features/jwt.rb 66 def jwt_secret 67 raise ConfigurationError, "jwt_secret not set" 68 end
# File lib/rodauth/features/jwt.rb 70 def jwt_session_hash 71 jwt_session_key ? {jwt_session_key=>session} : session 72 end
# File lib/rodauth/features/jwt.rb 78 def jwt_token 79 return @jwt_token if defined?(@jwt_token) 80 81 if (v = request.env['HTTP_AUTHORIZATION']) && v !~ jwt_authorization_ignore 82 @jwt_token = v.sub(jwt_authorization_remove, '') 83 end 84 end
# File lib/rodauth/features/account_expiration.rb 27 def last_account_activity_at 28 get_activity_timestamp(session_value, account_activity_last_activity_column) 29 end
# File lib/rodauth/features/account_expiration.rb 31 def last_account_login_at 32 get_activity_timestamp(session_value, account_activity_last_login_column) 33 end
# File lib/rodauth/features/remember.rb 114 def load_memory 115 if logged_in? 116 if extend_remember_deadline_while_logged_in? 117 if account_from_session 118 extend_remember_deadline 119 else 120 forget_login 121 clear_session 122 end 123 end 124 elsif account_from_remember_cookie 125 before_load_memory 126 login_session('remember') 127 extend_remember_deadline if extend_remember_deadline? 128 after_load_memory 129 end 130 end
# File lib/rodauth/features/base.rb 874 def loaded_templates 875 [] 876 end
# File lib/rodauth/features/lockout.rb 147 def locked_out? 148 if t = convert_timestamp(account_lockouts_ds.get(account_lockouts_deadline_column)) 149 if Time.now < t 150 true 151 else 152 unlock_account 153 false 154 end 155 else 156 false 157 end 158 end
# File lib/rodauth/features/remember.rb 174 def logged_in_via_remember_key? 175 authenticated_by.include?('remember') 176 end
# File lib/rodauth/features/login.rb 87 def login(auth_type) 88 @saved_login_redirect = remove_session_value(login_redirect_session_key) 89 transaction do 90 before_login 91 login_session(auth_type) 92 yield if block_given? 93 after_login 94 end 95 require_response(:_login_response) 96 end
# File lib/rodauth/features/login_password_requirements_base.rb 47 def login_confirm_label 48 "Confirm #{login_label}" 49 end
# File lib/rodauth/features/login_password_requirements_base.rb 131 def login_confirmation_matches?(login, login_confirmation) 132 login.casecmp?(login_confirmation) 133 end
# File lib/rodauth/features/login_password_requirements_base.rb 109 def login_does_not_meet_requirements_message 110 "invalid login#{", #{login_requirement_message}" if login_requirement_message}" 111 end
# File lib/rodauth/features/reset_password.rb 238 def login_failed_reset_password_request_form 239 render("reset-password-request") 240 end
# File lib/rodauth/features/base.rb 294 def login_field_autocomplete_value 295 login_uses_email? ? "email" : "on" 296 end
# File lib/rodauth/features/base.rb 318 def login_input_type 319 login_uses_email? ? 'email' : 'text' 320 end
# File lib/rodauth/features/login_password_requirements_base.rb 157 def login_meets_email_requirements?(login) 158 return true unless require_email_address_logins? 159 return true if login_valid_email?(login) 160 set_login_requirement_error_message(:login_not_valid_email, login_not_valid_email_message) 161 return false 162 end
# File lib/rodauth/features/login_password_requirements_base.rb 142 def login_meets_length_requirements?(login) 143 if login_minimum_length > login.length 144 set_login_requirement_error_message(:login_too_short, login_too_short_message) 145 false 146 elsif login_maximum_length < login.length 147 set_login_requirement_error_message(:login_too_long, login_too_long_message) 148 false 149 elsif login_maximum_bytes < login.bytesize 150 set_login_requirement_error_message(:login_too_many_bytes, login_too_many_bytes_message) 151 false 152 else 153 true 154 end 155 end
# File lib/rodauth/features/login_password_requirements_base.rb 55 def login_meets_requirements?(login) 56 login_meets_length_requirements?(login) && \ 57 login_meets_email_requirements?(login) 58 end
The normalized value of the login parameter
# File lib/rodauth/features/base.rb 516 def login_param_value 517 normalize_login(param(login_param)) 518 end
# File lib/rodauth/features/base.rb 337 def login_required 338 set_redirect_error_status(login_required_error_status) 339 set_error_reason :login_required 340 set_redirect_error_flash require_login_error_flash 341 redirect require_login_redirect 342 end
# File lib/rodauth/features/login.rb 105 def login_return_to_requested_location_path 106 request.fullpath if request.get? 107 end
# File lib/rodauth/features/base.rb 478 def login_session(auth_type) 479 update_session 480 set_session_value(authenticated_by_session_key, [auth_type]) 481 end
# File lib/rodauth/features/login_password_requirements_base.rb 113 def login_too_long_message 114 "maximum #{login_maximum_length} characters" 115 end
# File lib/rodauth/features/login_password_requirements_base.rb 117 def login_too_many_bytes_message 118 "maximum #{login_maximum_bytes} bytes" 119 end
# File lib/rodauth/features/login_password_requirements_base.rb 121 def login_too_short_message 122 "minimum #{login_minimum_length} characters" 123 end
# File lib/rodauth/features/base.rb 322 def login_uses_email? 323 login_column == :email 324 end
# File lib/rodauth/features/login_password_requirements_base.rb 164 def login_valid_email?(login) 165 login =~ login_email_regexp 166 end
# File lib/rodauth/features/active_sessions.rb 118 def logout_additional_form_tags 119 super.to_s + render('global-logout-field') 120 end
# File lib/rodauth/features/base.rb 543 def modifications_require_password? 544 has_password? 545 end
# File lib/rodauth/features/create_account.rb 96 def new_account(login) 97 @account = _new_account(login) 98 end
# File lib/rodauth/features/recovery_codes.rb 240 def new_recovery_code 241 random_key 242 end
# File lib/rodauth/features/webauthn.rb 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 60 def no_longer_active_session 61 clear_session 62 set_redirect_error_status inactive_session_error_status 63 set_error_reason :inactive_session 64 set_redirect_error_flash active_sessions_error_flash 65 redirect active_sessions_redirect 66 end
# File lib/rodauth/features/base.rb 520 def normalize_login(login) 521 login 522 end
# File lib/rodauth/features/base.rb 691 def normalize_session_or_flash_key(key) 692 scope.opts[:sessions_convert_symbols] ? key.to_s : key 693 end
Return nil by default for values with null bytes
# File lib/rodauth/features/base.rb 525 def null_byte_parameter_value(key, value) 526 nil 527 end
# File lib/rodauth/features/base.rb 437 def only_json? 438 scope.class.opts[:rodauth_json] == :only 439 end
# File lib/rodauth/features/base.rb 286 def open_account? 287 skip_status_checks? || account[account_status_column] == account_open_status_value 288 end
# File lib/rodauth/features/otp.rb 283 def otp_add_key 284 _otp_add_key(otp_key) 285 super if defined?(super) 286 end
# File lib/rodauth/features/otp.rb 243 def otp_available? 244 otp_exists? && !otp_locked_out? 245 end
# File lib/rodauth/features/otp.rb 247 def otp_exists? 248 !otp_key.nil? 249 end
# File lib/rodauth/features/otp.rb 383 def otp_hmac_old_secret(key) 384 base32_encode(compute_raw_hmac_with_secret(ROTP::Base32.decode(key), hmac_old_secret), key.bytesize) 385 end
# File lib/rodauth/features/otp.rb 379 def otp_hmac_secret(key) 380 base32_encode(compute_raw_hmac(ROTP::Base32.decode(key)), key.bytesize) 381 end
# File lib/rodauth/features/otp.rb 450 def otp_key_ds 451 db[otp_keys_table].where(otp_keys_id_column=>session_value) 452 end
# File lib/rodauth/features/otp.rb 335 def otp_keys_use_hmac? 336 !!hmac_secret 337 end
# File lib/rodauth/features/otp.rb 294 def otp_last_use 295 convert_timestamp(otp_key_ds.get(otp_keys_last_use_column)) 296 end
# File lib/rodauth/features/otp.rb 306 def otp_locked_out? 307 otp_key_ds.get(otp_keys_failures_column) >= otp_auth_failures_limit 308 end
# File lib/rodauth/features/otp_unlock.rb 210 def otp_lockout_redirect 211 otp_unlock_path 212 end
# File lib/rodauth/features/otp.rb 401 def otp_new_secret 402 ROTP::Base32.random_base32.downcase 403 end
# File lib/rodauth/features/otp.rb 318 def otp_provisioning_name 319 account[login_column] 320 end
# File lib/rodauth/features/otp.rb 310 def otp_provisioning_uri 311 otp.provisioning_uri(otp_provisioning_name) 312 end
# File lib/rodauth/features/otp.rb 322 def otp_qr_code 323 svg = RQRCode::QRCode.new(otp_provisioning_uri).as_svg(:module_size=>8, :viewbox=>true, :use_path=>true, :fill=>"fff") 324 svg.sub(/\A<\?xml version="1\.0" standalone="yes"\?>/, '') 325 end
# File lib/rodauth/features/otp.rb 298 def otp_record_authentication_failure 299 otp_key_ds.update(otp_keys_failures_column=>Sequel.identifier(otp_keys_failures_column) + 1) 300 end
# File lib/rodauth/features/otp.rb 278 def otp_remove 279 otp_key_ds.delete 280 @otp_key = nil 281 end
# File lib/rodauth/features/otp.rb 302 def otp_remove_auth_failures 303 otp_key_ds.update(otp_keys_failures_column=>0) 304 end
# File lib/rodauth/features/otp.rb 374 def otp_tmp_key(secret) 375 _otp_tmp_key(secret) 376 clear_cached_otp 377 end
# File lib/rodauth/features/otp_unlock.rb 145 def otp_unlock_auth_failure 146 h = { 147 otp_unlock_num_successes_column=>0, 148 otp_unlock_next_auth_attempt_after_column=>Sequel.date_add(Sequel::CURRENT_TIMESTAMP, :seconds=>otp_unlock_auth_failure_cooldown_seconds) 149 } 150 151 if otp_unlock_ds.update(h) == 0 152 h[otp_unlock_id_column] = session_value 153 154 # If row already exists when inserting, no need to do anything 155 raises_uniqueness_violation?{otp_unlock_ds.insert(h)} 156 end 157 end
# File lib/rodauth/features/otp_unlock.rb 159 def otp_unlock_auth_success 160 deadline = Sequel.date_add(Sequel::CURRENT_TIMESTAMP, :seconds=>otp_unlock_success_cooldown_seconds) 161 162 # Add WHERE to avoid possible race condition when multiple unlock auth requests 163 # are sent at the same time (only the first should increment num successes). 164 if otp_unlock_ds. 165 where(Sequel[otp_unlock_next_auth_attempt_after_column] < Sequel::CURRENT_TIMESTAMP). 166 update( 167 otp_unlock_num_successes_column=>Sequel[otp_unlock_num_successes_column]+1, 168 otp_unlock_next_auth_attempt_after_column=>deadline 169 ) == 0 170 171 # Ignore uniqueness errors when inserting after a failed update, 172 # which could be caused due to the race condition mentioned above. 173 raises_uniqueness_violation? do 174 otp_unlock_ds.insert( 175 otp_unlock_id_column=>session_value, 176 otp_unlock_next_auth_attempt_after_column=>deadline 177 ) 178 end 179 end 180 181 @otp_unlock_data = nil 182 # :nocov: 183 if otp_unlock_data 184 # :nocov: 185 if otp_unlock_num_successes >= otp_unlock_auths_required 186 # At least the requisite number of consecutive successful unlock 187 # authentications. Unlock OTP authentication. 188 otp_key_ds.update(otp_keys_failures_column => 0) 189 190 # Remove OTP unlock metadata when unlocking OTP authentication 191 otp_unlock_reset 192 # else 193 # # Still need additional consecutive successful unlock attempts. 194 end 195 # else 196 # # if row isn't available, probably the process was reset during this, 197 # # and it's safe to do nothing in that case. 198 end 199 end
# File lib/rodauth/features/otp_unlock.rb 115 def otp_unlock_available? 116 if otp_unlock_data 117 next_auth_attempt_after = otp_unlock_next_auth_attempt_after 118 current_timestamp = Time.now 119 120 if (next_auth_attempt_after < current_timestamp - otp_unlock_deadline_seconds) 121 # Unlock process not fully completed within deadline, reset process 122 otp_unlock_reset 123 true 124 else 125 if next_auth_attempt_after > current_timestamp 126 # If next auth attempt after timestamp is in the future, that means the next 127 # unlock attempt cannot happen until then. 128 false 129 else 130 if otp_unlock_num_successes == 0 131 # 0 value indicates previous attempt was a failure. Since failure cooldown 132 # period has passed, reset process so user gets full deadline period 133 otp_unlock_reset 134 end 135 true 136 end 137 end 138 else 139 # No row means no unlock attempts yet (or previous attempt was more than the 140 # deadline account, so unlocking is available 141 true 142 end 143 end
# File lib/rodauth/features/otp_unlock.rb 240 def otp_unlock_data 241 @otp_unlock_data ||= otp_unlock_ds.first 242 end
# File lib/rodauth/features/otp_unlock.rb 222 def otp_unlock_deadline 223 otp_unlock_next_auth_attempt_after + otp_unlock_deadline_seconds 224 end
# File lib/rodauth/features/otp_unlock.rb 201 def otp_unlock_deadline_passed? 202 otp_unlock_data ? (otp_unlock_next_auth_attempt_after < Time.now - otp_unlock_deadline_seconds) : false 203 end
# File lib/rodauth/features/otp_unlock.rb 253 def otp_unlock_ds 254 db[otp_unlock_table].where(otp_unlock_id_column=>session_value) 255 end
# File lib/rodauth/features/otp_unlock.rb 214 def otp_unlock_next_auth_attempt_after 215 if otp_unlock_data 216 convert_timestamp(otp_unlock_data[otp_unlock_next_auth_attempt_after_column]) 217 else 218 Time.now 219 end 220 end
# File lib/rodauth/features/otp_unlock.rb 230 def otp_unlock_not_available_set_refresh_header 231 response.headers["refresh"] = ((otp_unlock_next_auth_attempt_after - Time.now).to_i + 1).to_s 232 end
# File lib/rodauth/features/otp_unlock.rb 226 def otp_unlock_num_successes 227 otp_unlock_data ? otp_unlock_data[otp_unlock_num_successes_column] : 0 228 end
# File lib/rodauth/features/otp_unlock.rb 205 def otp_unlock_refresh_tag 206 # RODAUTH3: Remove 207 "<meta http-equiv=\"refresh\" content=\"#{(otp_unlock_next_auth_attempt_after - Time.now).to_i + 1}\">" 208 end
# File lib/rodauth/features/otp_unlock.rb 248 def otp_unlock_reset 249 otp_unlock_ds.delete 250 @otp_unlock_data = nil 251 end
# File lib/rodauth/features/otp_unlock.rb 244 def otp_unlock_success_cooldown_seconds 245 (_otp_interval+(otp_drift||0))*2 246 end
# File lib/rodauth/features/otp.rb 288 def otp_update_last_use 289 otp_key_ds. 290 where(Sequel.date_add(otp_keys_last_use_column, :seconds=>_otp_interval) < Sequel::CURRENT_TIMESTAMP). 291 update(otp_keys_last_use_column=>Sequel::CURRENT_TIMESTAMP) == 1 292 end
# File lib/rodauth/features/otp.rb 327 def otp_user_key 328 @otp_user_key ||= if otp_keys_use_hmac? 329 otp_hmac_secret(otp_key) 330 else 331 otp_key 332 end 333 end
# File lib/rodauth/features/otp.rb 251 def otp_valid_code?(ot_pass) 252 if _otp_valid_code?(ot_pass, otp) 253 true 254 elsif hmac_secret_rotation? && _otp_valid_code?(ot_pass, _otp_for_key(otp_hmac_old_secret(otp_key))) 255 _otp_valid_code_for_old_secret 256 true 257 else 258 false 259 end 260 end
# File lib/rodauth/features/otp.rb 387 def otp_valid_key?(secret) 388 return false unless secret =~ /\A([a-z2-7]{16}|[a-z2-7]{32})\z/ 389 if otp_keys_use_hmac? 390 # Purposely do not allow creating new OTPs with old secrets, 391 # since OTP rotation is difficult. The user will get shown 392 # the same page with an updated secret, which they can submit 393 # to setup OTP. 394 timing_safe_eql?(otp_hmac_secret(param(otp_setup_raw_param)), secret) 395 else 396 true 397 end 398 end
Return nil by default for values over maximum bytesize.
# File lib/rodauth/features/base.rb 511 def over_max_bytesize_param_value(key, value) 512 nil 513 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 494 def param(key) 495 param_or_nil(key).to_s 496 end
Return a string for the parameter name, or nil if there is no parameter with that name.
# File lib/rodauth/features/base.rb 500 def param_or_nil(key) 501 value = raw_param(key) 502 unless value.nil? 503 value = value.to_s 504 value = over_max_bytesize_param_value(key, value) if max_param_bytesize && value.bytesize > max_param_bytesize 505 value = null_byte_parameter_value(key, value) if value && value.include?("\0") 506 end 507 value 508 end
# File lib/rodauth/features/login_password_requirements_base.rb 51 def password_confirm_label 52 "Confirm #{password_label}" 53 end
# File lib/rodauth/features/login_password_requirements_base.rb 183 def password_does_not_contain_null_byte?(password) 184 return true unless password.include?("\0") 185 set_password_requirement_error_message(:password_contains_null_byte, contains_null_byte_message) 186 false 187 end
# File lib/rodauth/features/login_password_requirements_base.rb 88 def password_does_not_meet_requirements_message 89 "invalid password, does not meet requirements#{" (#{password_requirement_message})" if password_requirement_message}" 90 end
# File lib/rodauth/features/disallow_password_reuse.rb 50 def password_doesnt_match_previous_password?(password) 51 match = if use_database_authentication_functions? 52 salts = previous_password_ds. 53 select_map([previous_password_id_column, Sequel.function(function_name(:rodauth_get_previous_salt), previous_password_id_column).as(:salt)]) 54 return true if salts.empty? 55 56 salts.any? do |hash_id, salt| 57 database_function_password_match?(:rodauth_previous_password_hash_match, hash_id, password, salt) 58 end 59 else 60 # :nocov: 61 previous_password_ds.select_map(previous_password_hash_column).any? do |hash| 62 password_hash_match?(hash, password) 63 end 64 # :nocov: 65 end 66 67 return true unless match 68 set_password_requirement_error_message(:password_same_as_previous_password, password_same_as_previous_password_message) 69 false 70 end
# File lib/rodauth/features/password_expiration.rb 107 def password_expiration_ds 108 db[password_expiration_table].where(password_expiration_id_column=>account_id) 109 end
# File lib/rodauth/features/password_expiration.rb 68 def password_expired? 69 if password_changed_at = session[password_changed_at_session_key] 70 return password_changed_at + require_password_change_after < Time.now.to_i 71 end 72 73 account_from_session 74 if password_changed_at = get_password_changed_at 75 set_session_value(password_changed_at_session_key, password_changed_at.to_i) 76 password_changed_at + require_password_change_after < Time.now 77 else 78 set_session_value(password_changed_at_session_key, password_expiration_default ? 0 : 2147483647) 79 password_expiration_default 80 end 81 end
# File lib/rodauth/features/base.rb 298 def password_field_autocomplete_value 299 @password_field_autocomplete_value || 'current-password' 300 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 19 def password_hash(password) 20 return super unless use_argon2? 21 22 if secret = argon2_secret 23 argon2_params = Hash[password_hash_cost] 24 argon2_params[:secret] = secret 25 else 26 argon2_params = password_hash_cost 27 end 28 ::Argon2::Password.new(argon2_params).create(password) 29 end
# File lib/rodauth/features/argon2.rb 45 def password_hash_cost 46 return super unless use_argon2? 47 argon2_hash_cost 48 end
# File lib/rodauth/features/base.rb 864 def password_hash_ds 865 db[password_hash_table].where(password_hash_id_column=>account ? account_id : session_value) 866 end
# File lib/rodauth/features/argon2.rb 50 def password_hash_match?(hash, password) 51 return super unless argon2_hash_algorithm?(hash) 52 argon2_password_hash_match?(hash, password) 53 end
# File lib/rodauth/features/argon2.rb 55 def password_hash_using_salt(password, salt) 56 return super unless argon2_hash_algorithm?(salt) 57 argon2_password_hash_using_salt_and_secret(password, salt, argon2_secret) 58 end
# File lib/rodauth/features/base.rb 459 def password_match?(password) 460 if hash = get_password_hash 461 if account_password_hash_column || !use_database_authentication_functions? 462 password_hash_match?(hash, password) 463 else 464 database_function_password_match?(:rodauth_valid_password_hash, account_id, password, hash) 465 end 466 end 467 end
# File lib/rodauth/features/login_password_requirements_base.rb 168 def password_meets_length_requirements?(password) 169 if password_minimum_length > password.length 170 set_password_requirement_error_message(:password_too_short, password_too_short_message) 171 false 172 elsif password_maximum_length && password_maximum_length < password.length 173 set_password_requirement_error_message(:password_too_long, password_too_long_message) 174 false 175 elsif password_maximum_bytes && password_maximum_bytes < password.bytesize 176 set_password_requirement_error_message(:password_too_many_bytes, password_too_many_bytes_message) 177 false 178 else 179 true 180 end 181 end
# File lib/rodauth/features/disallow_common_passwords.rb 13 def password_meets_requirements?(password) 14 super && password_not_one_of_the_most_common?(password) 15 end
# File lib/rodauth/features/password_complexity.rb 75 def password_not_in_dictionary?(password) 76 return true unless dict = password_dictionary 77 return true unless password =~ /\A(?:\d*)([A-Za-z!@$+|][A-Za-z!@$+|0134578]+[A-Za-z!@$+|])(?:\d*)\z/ 78 word = $1.downcase.tr('!@$+|0134578', 'iastloleastb') 79 return true if !dict.include?(word) 80 set_password_requirement_error_message(:password_in_dictionary, password_in_dictionary_message) 81 false 82 end
# File lib/rodauth/features/disallow_common_passwords.rb 33 def password_not_one_of_the_most_common?(password) 34 return true unless password_one_of_most_common?(password) 35 set_password_requirement_error_message(:password_is_one_of_the_most_common, password_is_one_of_the_most_common_message) 36 false 37 end
# File lib/rodauth/features/password_complexity.rb 68 def password_not_too_many_repeating_characters?(password) 69 return true if password_max_repeating_characters < 2 70 return true if password !~ /(.)(\1){#{password_max_repeating_characters-1}}/ 71 set_password_requirement_error_message(:too_many_repeating_characters_in_password, password_too_many_repeating_characters_message) 72 false 73 end
# File lib/rodauth/features/disallow_common_passwords.rb 27 def password_one_of_most_common?(password) 28 most_common_passwords.include?(password) 29 end
# File lib/rodauth/features/password_grace_period.rb 22 def password_recently_entered? 23 return false unless last_password_entry = session[last_password_entry_session_key] 24 last_password_entry + password_grace_period > Time.now.to_i 25 end
# File lib/rodauth/features/reset_password.rb 252 def password_reset_ds(id=account_id) 253 db[reset_password_table].where(reset_password_id_column=>id) 254 end
# File lib/rodauth/features/login_password_requirements_base.rb 92 def password_too_long_message 93 "maximum #{password_maximum_length} characters" 94 end
# File lib/rodauth/features/login_password_requirements_base.rb 96 def password_too_many_bytes_message 97 "maximum #{password_maximum_bytes} bytes" 98 end
# File lib/rodauth/features/login_password_requirements_base.rb 100 def password_too_short_message 101 "minimum #{password_minimum_length} characters" 102 end
# File lib/rodauth/features/base.rb 547 def possible_authentication_methods 548 has_password? ? ['password'] : [] 549 end
# File lib/rodauth/features/base.rb 441 def post_configure 442 require 'bcrypt' if require_bcrypt? 443 db.extension :date_arithmetic if use_date_arithmetic? 444 445 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} 446 self.class.send(:define_method, :convert_token_id_to_integer?){true} 447 end 448 449 route_hash= {} 450 self.class.routes.each do |meth| 451 route_meth = "#{meth.to_s.sub(/\Ahandle_/, '')}_route" 452 if route = send(route_meth) 453 route_hash["/#{route}"] = meth 454 end 455 end 456 self.class.route_hash = route_hash.freeze 457 end
# File lib/rodauth/features/disallow_password_reuse.rb 94 def previous_password_ds 95 db[previous_password_hash_table].where(previous_password_account_id_column=>account_id) 96 end
In cases where retrying on uniqueness violations cannot work, this will detect whether a uniqueness violation is raised by the block and return the exception if so. This method should be used if you don’t care about the exception itself.
# File lib/rodauth/features/base.rb 893 def raises_uniqueness_violation?(&block) 894 transaction(:savepoint=>:only, &block) 895 false 896 rescue unique_constraint_violation_class => e 897 e 898 end
# File lib/rodauth/features/base.rb 682 def random_key 683 SecureRandom.urlsafe_base64(32) 684 end
# File lib/rodauth/features/base.rb 529 def raw_param(key) 530 request.params[key] 531 end
# File lib/rodauth/features/recovery_codes.rb 160 def recovery_code_match?(code) 161 recovery_codes.each do |s| 162 if timing_safe_eql?(code, s) 163 recovery_codes_ds.where(recovery_codes_column=>code).delete 164 if recovery_codes_primary? 165 add_recovery_code 166 end 167 return true 168 end 169 end 170 false 171 end
# File lib/rodauth/features/recovery_codes.rb 196 def recovery_codes_available? 197 !recovery_codes_ds.empty? 198 end
# File lib/rodauth/features/recovery_codes.rb 264 def recovery_codes_ds 265 db[recovery_codes_table].where(recovery_codes_id_column=>session_value) 266 end
# File lib/rodauth/features/recovery_codes.rb 244 def recovery_codes_primary? 245 (features & [:otp, :sms_codes, :webauthn]).empty? 246 end
# File lib/rodauth/features/recovery_codes.rb 156 def recovery_codes_remove 157 recovery_codes_ds.delete 158 end
# File lib/rodauth/features/base.rb 659 def redirect(path) 660 request.redirect(path) 661 end
# File lib/rodauth/features/remember.rb 261 def remember_key_ds(id=account_id) 262 db[remember_table].where(remember_id_column=>id) 263 end
# File lib/rodauth/features/remember.rb 132 def remember_login 133 get_remember_key 134 set_remember_cookie 135 set_session_value(remember_deadline_extended_session_key, Time.now.to_i) if extend_remember_deadline? 136 end
# File lib/rodauth/features/remember.rb 91 def remembered_session_id 92 return unless cookie = _get_remember_cookie 93 id, key = cookie.split('_', 2) 94 return unless id && key 95 96 actual, deadline = active_remember_key_ds(id).get([remember_key_column, remember_deadline_column]) 97 return unless actual 98 99 if hmac_secret && !(valid = timing_safe_eql?(key, compute_hmac(actual))) 100 if hmac_secret_rotation? && (valid = timing_safe_eql?(key, compute_old_hmac(actual))) 101 _set_remember_cookie(id, actual, deadline) 102 elsif !(raw_remember_token_deadline && raw_remember_token_deadline > convert_timestamp(deadline)) 103 return 104 end 105 end 106 107 unless valid || timing_safe_eql?(key, actual) 108 return 109 end 110 111 id 112 end
# File lib/rodauth/features/active_sessions.rb 92 def remove_active_session(session_id) 93 active_sessions_ds.where(active_sessions_session_id_column=>session_id).delete 94 end
# File lib/rodauth/features/active_sessions.rb 96 def remove_all_active_sessions 97 active_sessions_ds.delete 98 end
# File lib/rodauth/features/active_sessions.rb 104 def remove_all_active_sessions_except_current 105 if session_id = session[session_id_session_key] 106 remove_all_active_sessions_except_for(session_id) 107 else 108 remove_all_active_sessions 109 end 110 end
# File lib/rodauth/features/active_sessions.rb 100 def remove_all_active_sessions_except_for(session_id) 101 active_sessions_ds.exclude(active_sessions_session_id_column=>compute_hmacs(session_id)).delete 102 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 86 def remove_current_session 87 if session_id = session[session_id_session_key] 88 remove_active_session(compute_hmacs(session_id)) 89 end 90 end
# File lib/rodauth/features/email_auth.rb 127 def remove_email_auth_key 128 email_auth_ds.delete 129 end
# File lib/rodauth/features/active_sessions.rb 112 def remove_inactive_sessions 113 if cond = inactive_session_cond 114 active_sessions_ds.where(cond).delete 115 end 116 end
# File lib/rodauth/features/jwt_refresh.rb 185 def remove_jwt_refresh_token_key(token) 186 account_id, token_id, _ = _account_refresh_token_split(token) 187 jwt_refresh_token_account_token_ds(account_id, token_id).delete 188 end
# File lib/rodauth/features/lockout.rb 280 def remove_lockout_metadata 281 account_login_failures_ds.delete 282 account_lockouts_ds.delete 283 end
# File lib/rodauth/features/remember.rb 170 def remove_remember_key(id=account_id) 171 remember_key_ds(id).delete 172 end
# File lib/rodauth/features/reset_password.rb 180 def remove_reset_password_key 181 password_reset_ds.delete 182 end
# File lib/rodauth/features/base.rb 951 def remove_session_value(key) 952 session.delete(key) 953 end
# File lib/rodauth/features/verify_account.rb 167 def remove_verify_account_key 168 verify_account_ds.delete 169 end
# File lib/rodauth/features/verify_login_change.rb 108 def remove_verify_login_change_key 109 verify_login_change_ds.delete 110 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 433 def render(page) 434 _view(:render, page) 435 end
# File lib/rodauth/features/login.rb 138 def render_multi_phase_login_forms 139 multi_phase_login_forms.sort.map{|_, form, _| form}.join("\n") 140 end
# File lib/rodauth/features/base.rb 154 def request 155 scope.request 156 end
# File lib/rodauth/features/base.rb 378 def require_account 379 require_authentication 380 require_account_session 381 end
# File lib/rodauth/features/base.rb 700 def require_account_session 701 unless account_from_session 702 clear_session 703 login_required 704 end 705 end
# File lib/rodauth/features/base.rb 374 def require_authentication 375 require_login 376 end
# File lib/rodauth/features/password_expiration.rb 61 def require_current_password 62 if authenticated? && password_expired? && password_change_needed_redirect != request.path_info 63 set_redirect_error_flash password_expiration_error_flash 64 redirect password_change_needed_redirect 65 end 66 end
# File lib/rodauth/features/http_basic_auth.rb 27 def require_http_basic_auth 28 unless http_basic_auth 29 set_http_basic_auth_error_response 30 return_response 31 end 32 end
# File lib/rodauth/features/base.rb 366 def require_login 367 login_required unless logged_in? 368 end
# File lib/rodauth/features/verify_account.rb 159 def require_login_confirmation? 160 false 161 end
# File lib/rodauth/features/login.rb 142 def require_login_redirect 143 login_path 144 end
# File lib/rodauth/features/otp.rb 234 def require_otp_setup 235 unless otp_exists? 236 set_redirect_error_status(two_factor_not_setup_error_status) 237 set_error_reason :two_factor_not_setup 238 set_redirect_error_flash two_factor_not_setup_error_flash 239 redirect two_factor_need_setup_redirect 240 end 241 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 942 def require_response(meth) 943 send(meth) 944 raise ConfigurationError, "#{meth.to_s.sub(/\A_/, '')} overridden without returning a response (should use redirect or request.halt)." 945 end
# File lib/rodauth/features/sms_codes.rb 337 def require_sms_available 338 require_sms_setup 339 340 if sms_locked_out? 341 set_redirect_error_status(lockout_error_status) 342 set_error_reason :sms_locked_out 343 set_redirect_error_flash sms_lockout_error_flash 344 redirect sms_lockout_redirect 345 end 346 end
# File lib/rodauth/features/sms_codes.rb 328 def require_sms_not_setup 329 if sms_setup? 330 set_redirect_error_status(sms_already_setup_error_status) 331 set_error_reason :sms_already_setup 332 set_redirect_error_flash sms_already_setup_error_flash 333 redirect sms_already_setup_redirect 334 end 335 end
# File lib/rodauth/features/sms_codes.rb 319 def require_sms_setup 320 unless sms_setup? 321 set_redirect_error_status(two_factor_not_setup_error_status) 322 set_error_reason :sms_not_setup 323 set_redirect_error_flash sms_not_setup_error_flash 324 redirect sms_needs_setup_redirect 325 end 326 end
# File lib/rodauth/features/two_factor_base.rb 156 def require_two_factor_authenticated 157 unless two_factor_authenticated? 158 if two_factor_auth_return_to_requested_location? 159 set_session_value(two_factor_auth_redirect_session_key, request.fullpath) 160 end 161 set_redirect_error_status(two_factor_need_authentication_error_status) 162 set_error_reason :two_factor_need_authentication 163 set_redirect_error_flash two_factor_need_authentication_error_flash 164 redirect two_factor_auth_required_redirect 165 end 166 end
# File lib/rodauth/features/two_factor_base.rb 147 def require_two_factor_not_authenticated(auth_type = nil) 148 if two_factor_authenticated? || (auth_type && two_factor_login_type_match?(auth_type)) 149 set_redirect_error_status(two_factor_already_authenticated_error_status) 150 set_error_reason :two_factor_already_authenticated 151 set_redirect_error_flash two_factor_already_authenticated_error_flash 152 redirect two_factor_already_authenticated_redirect 153 end 154 end
# File lib/rodauth/features/two_factor_base.rb 135 def require_two_factor_setup 136 # Avoid database query if already authenticated via 2nd factor 137 return if two_factor_authenticated? 138 139 return if uses_two_factor_authentication? 140 141 set_redirect_error_status(two_factor_not_setup_error_status) 142 set_error_reason :two_factor_not_setup 143 set_redirect_error_flash two_factor_not_setup_error_flash 144 redirect two_factor_need_setup_redirect 145 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 129 def rescue_jwt_payload(_) 130 @jwt_payload = false 131 end
# File lib/rodauth/features/reset_password.rb 188 def reset_password_email_link 189 token_link(reset_password_route, reset_password_key_param, reset_password_key_value) 190 end
# File lib/rodauth/features/reset_password.rb 210 def reset_password_email_recently_sent? 211 (email_last_sent = get_reset_password_email_last_sent) && (Time.now - email_last_sent < reset_password_skip_resend_email_within) 212 end
# File lib/rodauth/features/reset_password.rb 246 def reset_password_key_insert_hash 247 hash = {reset_password_id_column=>account_id, reset_password_key_column=>reset_password_key_value} 248 set_deadline_value(hash, reset_password_deadline_column, reset_password_deadline_interval) 249 hash 250 end
# File lib/rodauth/features/reset_password.rb 176 def reset_password_request_for_unverified_account 177 throw_error_reason(:unverified_account, unopen_account_error_status, login_param, unverified_account_message) 178 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 158 def response 159 scope.response 160 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 884 def retry_on_uniqueness_violation(&block) 885 if raises_uniqueness_violation?(&block) 886 yield 887 end 888 end
# File lib/rodauth/features/json.rb 206 def return_json_response 207 _return_json_response 208 end
# File lib/rodauth/features/base.rb 663 def return_response(body=nil) 664 response.write(body) if body 665 request.halt 666 end
# File lib/rodauth/features/base.rb 170 def route! 171 if meth = self.class.route_hash[request.remaining_path] 172 send(meth) 173 end 174 175 nil 176 end
# File lib/rodauth/features/base.rb 668 def route_path(route, opts={}) 669 path = "#{prefix}/#{route}" 670 path += "?#{Rack::Utils.build_nested_query(opts)}" unless opts.empty? 671 path 672 end
# File lib/rodauth/features/base.rb 674 def route_url(route, opts={}) 675 "#{base_url}#{route_path(route, opts)}" 676 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 124 def send_verify_login_change_email(login) 125 send_email(create_verify_login_change_email(login)) 126 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 162 def session 163 scope.session 164 end
# File lib/rodauth/features/active_sessions.rb 181 def session_inactivity_deadline_condition 182 if deadline = session_inactivity_deadline 183 Sequel[active_sessions_last_use_column] < Sequel.date_sub(Sequel::CURRENT_TIMESTAMP, seconds: deadline) 184 end 185 end
# File lib/rodauth/features/jwt.rb 74 def session_jwt 75 JWT.encode(jwt_session_hash, jwt_secret, jwt_algorithm) 76 end
# File lib/rodauth/features/active_sessions.rb 187 def session_lifetime_deadline_condition 188 if deadline = session_lifetime_deadline 189 Sequel[active_sessions_created_at_column] < Sequel.date_sub(Sequel::CURRENT_TIMESTAMP, seconds: deadline) 190 end 191 end
# File lib/rodauth/features/base.rb 277 def session_value 278 session[session_key] 279 end
This is needed on MySQL, which doesn’t support non constant defaults other than CURRENT_TIMESTAMP.
# File lib/rodauth/features/base.rb 920 def set_deadline_value(hash, column, interval) 921 if set_deadline_values? 922 # :nocov: 923 hash[column] = Sequel.date_add(Sequel::CURRENT_TIMESTAMP, interval) 924 # :nocov: 925 end 926 end
# File lib/rodauth/features/base.rb 750 def set_deadline_values? 751 db.database_type == :mysql 752 end
# File lib/rodauth/features/email_auth.rb 115 def set_email_auth_email_last_sent 116 email_auth_ds.update(email_auth_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if email_auth_email_last_sent_column 117 end
# File lib/rodauth/features/base.rb 350 def set_error_flash(message) 351 flash.now[flash_error_key] = message 352 end
# File lib/rodauth/features/base.rb 738 def set_error_reason(reason) 739 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 178 def set_field_error(field, error) 179 (@field_errors ||= {})[field] = error 180 end
# File lib/rodauth/features/http_basic_auth.rb 74 def set_http_basic_auth_error_response 75 response.status = 401 76 set_response_header("www-authenticate", "Basic realm=\"#{http_basic_auth_realm}\"") 77 end
# File lib/rodauth/features/jwt.rb 150 def set_jwt 151 set_jwt_token(session_jwt) 152 end
# File lib/rodauth/features/jwt_refresh.rb 201 def set_jwt_refresh_token_hmac_session_key(token) 202 if allow_refresh_with_expired_jwt_access_token? 203 key = _account_refresh_token_split(token).last 204 data = random_key 205 set_session_value(jwt_refresh_token_data_session_key, data) 206 set_session_value(jwt_refresh_token_hmac_session_key, compute_hmac(data + key)) 207 end 208 end
# File lib/rodauth/features/jwt.rb 86 def set_jwt_token(token) 87 set_response_header('authorization', token) 88 end
# File lib/rodauth/features/password_grace_period.rb 44 def set_last_password_entry 45 set_session_value(last_password_entry_session_key, Time.now.to_i) 46 end
# File lib/rodauth/features/login_password_requirements_base.rb 125 def set_login_requirement_error_message(reason, message) 126 set_error_reason(reason) 127 @login_requirement_message = message 128 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 358 def set_notice_flash(message) 359 flash[flash_notice_key] = message 360 end
# File lib/rodauth/features/base.rb 362 def set_notice_now_flash(message) 363 flash.now[flash_notice_key] = message 364 end
# File lib/rodauth/features/disallow_password_reuse.rb 19 def set_password(password) 20 hash = super 21 add_previous_password_hash(hash) 22 hash 23 end
# File lib/rodauth/features/login_password_requirements_base.rb 104 def set_password_requirement_error_message(reason, message) 105 set_error_reason(reason) 106 @password_requirement_message = message 107 end
# File lib/rodauth/features/base.rb 354 def set_redirect_error_flash(message) 355 flash[flash_error_key] = message 356 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 712 def set_redirect_error_status(status) 713 end
# File lib/rodauth/features/remember.rb 196 def set_remember_cookie 197 _set_remember_cookie(account_id, remember_key_value, active_remember_key_ds.get(remember_deadline_column)) 198 end
# File lib/rodauth/features/reset_password.rb 198 def set_reset_password_email_last_sent 199 password_reset_ds.update(reset_password_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if reset_password_email_last_sent_column 200 end
# File lib/rodauth/features/base.rb 719 def set_response_error_reason_status(reason, status) 720 set_error_reason(reason) 721 set_response_error_status(status) 722 end
# File lib/rodauth/features/base.rb 715 def set_response_error_status(status) 716 response.status = status 717 end
# File lib/rodauth/features/base.rb 571 def set_response_header(key, value) 572 response.headers[key] = value 573 end
# File lib/rodauth/features/base.rb 947 def set_session_value(key, value) 948 session[key] = value 949 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 344 def set_title(title) 345 if title_instance_variable 346 scope.instance_variable_set(title_instance_variable, title) 347 end 348 end
# File lib/rodauth/features/lockout.rb 237 def set_unlock_account_email_last_sent 238 account_lockouts_ds.update(account_lockouts_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if account_lockouts_email_last_sent_column 239 end
# File lib/rodauth/features/verify_account.rb 225 def set_verify_account_email_last_sent 226 verify_account_ds.update(verify_account_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP) if verify_account_email_last_sent_column 227 end
# File lib/rodauth/features/verify_account.rb 237 def setup_account_verification 238 generate_verify_account_key_value 239 create_verify_account_key 240 send_verify_account_email 241 end
# File lib/rodauth/features/lockout.rb 285 def show_lockout_page 286 set_response_error_reason_status(:account_locked_out, lockout_error_status) 287 set_error_flash login_lockout_error_flash 288 return_response unlock_account_request_view 289 end
# File lib/rodauth/features/otp.rb 454 def show_otp_auth_link? 455 otp_available? 456 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 116 def skip_login_field_on_login? 117 return false unless use_multi_phase_login? 118 valid_login_entered? 119 end
# File lib/rodauth/features/login.rb 121 def skip_password_field_on_login? 122 return false unless use_multi_phase_login? 123 !valid_login_entered? 124 end
# File lib/rodauth/features/close_account.rb 85 def skip_status_checks? 86 false 87 end
# File lib/rodauth/features/sms_codes.rb 399 def sms_auth_message(code) 400 "SMS authentication code for #{domain} is #{code}" 401 end
# File lib/rodauth/features/sms_codes.rb 452 def sms_available? 453 sms_setup? && !sms_locked_out? 454 end
# File lib/rodauth/features/sms_codes.rb 431 def sms_code 432 sms[sms_code_column] 433 end
# File lib/rodauth/features/sms_codes.rb 435 def sms_code_issued_at 436 convert_timestamp(sms[sms_issued_at_column]) 437 end
# File lib/rodauth/features/sms_codes.rb 348 def sms_code_match?(code) 349 return false unless sms_current_auth? 350 timing_safe_eql?(code, sms_code) 351 end
# File lib/rodauth/features/sms_codes.rb 500 def sms_codes_primary? 501 (features & [:otp, :webauthn]).empty? 502 end
# File lib/rodauth/features/recovery_codes.rb 146 def sms_confirm 147 super if defined?(super) 148 auto_add_missing_recovery_codes 149 end
# File lib/rodauth/features/sms_codes.rb 362 def sms_confirm_failure 363 sms_ds.delete 364 end
# File lib/rodauth/features/sms_codes.rb 403 def sms_confirm_message(code) 404 "SMS confirmation code for #{domain} is #{code}" 405 end
# File lib/rodauth/features/sms_codes.rb 353 def sms_confirmation_match?(code) 354 sms_needs_confirmation? && sms_code_match?(code) 355 end
# File lib/rodauth/features/sms_codes.rb 460 def sms_current_auth? 461 sms_code && sms_code_issued_at + sms_code_allowed_seconds > Time.now 462 end
# File lib/rodauth/features/sms_codes.rb 357 def sms_disable 358 sms_ds.delete 359 @sms = nil 360 end
# File lib/rodauth/features/sms_codes.rb 528 def sms_ds 529 db[sms_codes_table].where(sms_id_column=>session_value) 530 end
# File lib/rodauth/features/sms_codes.rb 439 def sms_failures 440 sms[sms_failures_column] 441 end
# File lib/rodauth/features/sms_codes.rb 456 def sms_locked_out? 457 sms_failures >= sms_failure_limit 458 end
# File lib/rodauth/features/sms_codes.rb 448 def sms_needs_confirmation? 449 sms && sms_failures.nil? 450 end
# File lib/rodauth/features/sms_codes.rb 407 def sms_needs_confirmation_notice_flash 408 sms_needs_confirmation_error_flash 409 end
# File lib/rodauth/features/sms_codes.rb 508 def sms_new_auth_code 509 SecureRandom.random_number(10**sms_auth_code_length).to_s.rjust(sms_auth_code_length, "0") 510 end
# File lib/rodauth/features/sms_codes.rb 512 def sms_new_confirm_code 513 SecureRandom.random_number(10**sms_confirm_code_length).to_s.rjust(sms_confirm_code_length, "0") 514 end
# File lib/rodauth/features/sms_codes.rb 504 def sms_normalize_phone(phone) 505 phone.to_s.gsub(/\D+/, '') 506 end
# File lib/rodauth/features/sms_codes.rb 427 def sms_phone 428 sms[sms_phone_column] 429 end
# File lib/rodauth/features/sms_codes.rb 422 def sms_record_failure 423 update_sms(sms_failures_column=>Sequel.expr(sms_failures_column)+1) 424 sms[sms_failures_column] = sms_ds.get(sms_failures_column) 425 end
# File lib/rodauth/features/sms_codes.rb 415 def sms_remove_expired_confirm_code 416 db[sms_codes_table]. 417 where(sms_id_column=>session_value, sms_failures_column => nil). 418 where(Sequel[sms_issued_at_column] < Sequel.date_sub(Sequel::CURRENT_TIMESTAMP, seconds: sms_confirm_deadline)). 419 delete 420 end
# File lib/rodauth/features/sms_codes.rb 373 def sms_remove_failures 374 return if sms_needs_confirmation? 375 update_hash_ds(sms, sms_ds.exclude(sms_failures_column => nil), sms_failures_column => 0, sms_code_column => nil) 376 end
# File lib/rodauth/features/sms_codes.rb 516 def sms_send(phone, message) 517 raise ConfigurationError, "sms_send needs to be defined in the Rodauth configuration for SMS sending to work" 518 end
# File lib/rodauth/features/sms_codes.rb 383 def sms_send_auth_code 384 code = sms_new_auth_code 385 sms_set_code(code) 386 sms_send(sms_phone, sms_auth_message(code)) 387 end
# File lib/rodauth/features/sms_codes.rb 389 def sms_send_confirm_code 390 code = sms_new_confirm_code 391 sms_set_code(code) 392 sms_send(sms_phone, sms_confirm_message(code)) 393 end
# File lib/rodauth/features/sms_codes.rb 411 def sms_set_code(code) 412 update_sms(sms_code_column=>code, sms_issued_at_column=>Sequel::CURRENT_TIMESTAMP) 413 end
# File lib/rodauth/features/sms_codes.rb 366 def sms_setup(phone_number) 367 # Cannot handle uniqueness violation here, as the phone number given may not match the 368 # one in the table. 369 sms_ds.insert(sms_id_column=>session_value, sms_phone_column=>phone_number, sms_failures_column => nil) 370 remove_instance_variable(:@sms) if instance_variable_defined?(:@sms) 371 end
# File lib/rodauth/features/sms_codes.rb 443 def sms_setup? 444 return false unless sms 445 !sms_needs_confirmation? 446 end
# File lib/rodauth/features/sms_codes.rb 395 def sms_valid_phone?(phone) 396 phone.length >= sms_phone_min_length 397 end
# File lib/rodauth/features/base.rb 636 def split_token(token) 637 token.split(token_separator, 2) 638 end
# File lib/rodauth/features/base.rb 849 def template_path(page) 850 File.join(File.dirname(__FILE__), '../../../templates', "#{page}.str") 851 end
# File lib/rodauth/features/http_basic_auth.rb 79 def throw_basic_auth_error(*args) 80 set_http_basic_auth_error_response 81 throw_error(*args) 82 end
# File lib/rodauth/features/base.rb 728 def throw_error(field, error) 729 set_field_error(field, error) 730 throw_rodauth_error 731 end
# File lib/rodauth/features/base.rb 741 def throw_error_reason(reason, status, field, message) 742 set_error_reason(reason) 743 throw_error_status(status, field, message) 744 end
# File lib/rodauth/features/base.rb 733 def throw_error_status(status, field, error) 734 set_response_error_status(status) 735 throw_error(field, error) 736 end
# File lib/rodauth/features/base.rb 724 def throw_rodauth_error 725 throw :rodauth_error 726 end
# File lib/rodauth/features/base.rb 695 def timing_safe_eql?(provided, actual) 696 provided = provided.to_s 697 Rack::Utils.secure_compare(provided.ljust(actual.length), actual) && provided.length == actual.length 698 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 678 def transaction(opts={}, &block) 679 db.transaction(opts, &block) 680 end
# File lib/rodauth/features/base.rb 245 def translate(_key, default) 246 # do not attempt to translate by default 247 default 248 end
# File lib/rodauth/features/two_factor_base.rb 206 def two_factor_auth_links 207 @two_factor_auth_links ||= _filter_links(_two_factor_auth_links) 208 end
# File lib/rodauth/features/two_factor_base.rb 241 def two_factor_authenticate(type) 242 two_factor_update_session(type) 243 two_factor_remove_auth_failures 244 after_two_factor_authentication 245 require_response(:_two_factor_auth_response) 246 end
# File lib/rodauth/features/two_factor_base.rb 184 def two_factor_authenticated? 185 authenticated_by && authenticated_by.length >= 2 186 end
# File lib/rodauth/features/two_factor_base.rb 188 def two_factor_authentication_setup? 189 possible_authentication_methods.length >= 2 190 end
# File lib/rodauth/features/two_factor_base.rb 198 def two_factor_login_type_match?(type) 199 authenticated_by && authenticated_by.include?(type) 200 end
# File lib/rodauth/features/two_factor_base.rb 122 def two_factor_modifications_require_password? 123 modifications_require_password? 124 end
# File lib/rodauth/features/two_factor_base.rb 180 def two_factor_partially_authenticated? 181 logged_in? && !two_factor_authenticated? && uses_two_factor_authentication? 182 end
# File lib/rodauth/features/two_factor_base.rb 172 def two_factor_password_match?(password) 173 if two_factor_modifications_require_password? 174 password_match?(password) 175 else 176 true 177 end 178 end
# File lib/rodauth/features/otp.rb 224 def two_factor_remove 225 super 226 otp_remove 227 end
# File lib/rodauth/features/otp.rb 229 def two_factor_remove_auth_failures 230 super 231 otp_remove_auth_failures 232 end
# File lib/rodauth/features/two_factor_base.rb 214 def two_factor_remove_links 215 @two_factor_remove_links ||= _filter_links(_two_factor_remove_links) 216 end
# File lib/rodauth/features/two_factor_base.rb 254 def two_factor_remove_session(type) 255 authenticated_by.delete(type) 256 remove_session_value(two_factor_setup_session_key) 257 if authenticated_by.empty? 258 clear_session 259 end 260 end
# File lib/rodauth/features/two_factor_base.rb 210 def two_factor_setup_links 211 @two_factor_setup_links ||= _filter_links(_two_factor_setup_links) 212 end
# File lib/rodauth/features/two_factor_base.rb 262 def two_factor_update_session(auth_type) 263 authenticated_by << auth_type 264 set_session_value(two_factor_setup_session_key, true) 265 end
Work around jdbc/sqlite issue where it only raises ConstraintViolation and not UniqueConstraintViolation.
# File lib/rodauth/features/base.rb 902 def unique_constraint_violation_class 903 if db.adapter_scheme == :jdbc && db.database_type == :sqlite 904 # :nocov: 905 Sequel::ConstraintViolation 906 # :nocov: 907 else 908 Sequel::UniqueConstraintViolation 909 end 910 end
# File lib/rodauth/features/lockout.rb 160 def unlock_account 161 transaction do 162 remove_lockout_metadata 163 end 164 end
# File lib/rodauth/features/lockout.rb 225 def unlock_account_email_link 226 token_link(unlock_account_route, unlock_account_key_param, unlock_account_key_value) 227 end
# File lib/rodauth/features/lockout.rb 241 def unlock_account_email_recently_sent? 242 (email_last_sent = get_unlock_account_email_last_sent) && (Time.now - email_last_sent < unlock_account_skip_resend_email_within) 243 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 965 def update_account(values, ds=account_ds) 966 update_hash_ds(account, ds, values) 967 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 200 def update_current_session? 201 !!session_inactivity_deadline 202 end
# File lib/rodauth/features/base.rb 955 def update_hash_ds(hash, ds, values) 956 num = ds.update(values) 957 if num == 1 958 values.each do |k, v| 959 hash[k] = Sequel::CURRENT_TIMESTAMP == v ? Time.now : v 960 end 961 end 962 num 963 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 18 def update_password_hash? 19 password_hash_cost != @current_password_hash_cost || @update_password_hash 20 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 520 def update_sms(values) 521 update_hash_ds(sms, sms_ds, values) 522 end
# File lib/rodauth/features/base.rb 754 def use_database_authentication_functions? 755 case db.database_type 756 when :postgres, :mysql, :mssql 757 true 758 else 759 # :nocov: 760 false 761 # :nocov: 762 end 763 end
# File lib/rodauth/features/active_sessions.rb 209 def use_date_arithmetic? 210 true 211 end
# File lib/rodauth/features/json.rb 60 def use_json? 61 json_request? || only_json? 62 end
# File lib/rodauth/features/email_auth.rb 156 def use_multi_phase_login? 157 true 158 end
# File lib/rodauth/features/base.rb 765 def use_request_specific_csrf_tokens? 766 scope.opts[:rodauth_route_csrf] && scope.use_request_specific_csrf_tokens? 767 end
# File lib/rodauth/features/base.rb 938 def use_scope_clear_session? 939 scope.respond_to?(:clear_session) 940 end
# File lib/rodauth/features/two_factor_base.rb 192 def uses_two_factor_authentication? 193 return false unless logged_in? 194 set_session_value(two_factor_setup_session_key, two_factor_authentication_setup?) unless session.has_key?(two_factor_setup_session_key) 195 session[two_factor_setup_session_key] 196 end
# File lib/rodauth/features/jwt.rb 98 def valid_jwt? 99 !!(jwt_token && jwt_payload) 100 end
# File lib/rodauth/features/login.rb 126 def valid_login_entered? 127 @valid_login_entered 128 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 171 def verify_account 172 update_account(account_status_column=>account_open_status_value) == 1 173 end
# File lib/rodauth/features/verify_account.rb 278 def verify_account_check_already_logged_in 279 check_already_logged_in 280 end
# File lib/rodauth/features/verify_account.rb 303 def verify_account_ds(id=account_id) 304 db[verify_account_table].where(verify_account_id_column=>id) 305 end
# File lib/rodauth/features/verify_account.rb 204 def verify_account_email_link 205 token_link(verify_account_route, verify_account_key_param, verify_account_key_value) 206 end
# File lib/rodauth/features/verify_account.rb 243 def verify_account_email_recently_sent? 244 account && (email_last_sent = get_verify_account_email_last_sent) && (Time.now - email_last_sent < verify_account_skip_resend_email_within) 245 end
# File lib/rodauth/features/verify_account.rb 175 def verify_account_email_resend 176 if @verify_account_key_value = get_verify_account_key(account_id) 177 set_verify_account_email_last_sent 178 send_verify_account_email 179 true 180 end 181 end
# File lib/rodauth/features/verify_account.rb 299 def verify_account_key_insert_hash 300 {verify_account_id_column=>account_id, verify_account_key_column=>verify_account_key_value} 301 end
# File lib/rodauth/features/verify_account_grace_period.rb 29 def verify_account_set_password? 30 false 31 end
# File lib/rodauth/features/webauthn_verify_account.rb 7 def verify_account_view 8 webauthn_setup_view 9 end
# File lib/rodauth/features/verify_login_change.rb 112 def verify_login_change 113 unless res = _update_login(verify_login_change_new_login) 114 remove_verify_login_change_key 115 end 116 117 res 118 end
# File lib/rodauth/features/verify_login_change.rb 206 def verify_login_change_ds(id=account_id) 207 db[verify_login_change_table].where(verify_login_change_id_column=>id) 208 end
# File lib/rodauth/features/verify_login_change.rb 202 def verify_login_change_email_body 203 render('verify-login-change-email') 204 end
# File lib/rodauth/features/verify_login_change.rb 128 def verify_login_change_email_link 129 token_link(verify_login_change_route, verify_login_change_key_param, verify_login_change_key_value) 130 end
# File lib/rodauth/features/verify_login_change.rb 192 def verify_login_change_key_insert_hash(login) 193 hash = {verify_login_change_id_column=>account_id, verify_login_change_key_column=>verify_login_change_key_value, verify_login_change_login_column=>login} 194 set_deadline_value(hash, verify_login_change_deadline_column, verify_login_change_deadline_interval) 195 hash 196 end
# File lib/rodauth/features/verify_login_change.rb 140 def verify_login_change_old_login 141 account_ds.get(login_column) 142 end
# File lib/rodauth/features/base.rb 428 def view(page, title) 429 set_title(title) 430 _view(:view, page) 431 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 43 def webauthn_auth_additional_form_tags 44 if @webauthn_login 45 super.to_s + login_hidden_field 46 else 47 super 48 end 49 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 70 def webauthn_login_verification_factor?(webauthn_credential) 71 webauthn_login_user_verification_additional_factor? && 72 webauthn_credential.response.authenticator_data.user_verified? && 73 uses_two_factor_authentication? 74 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