# frozen-string-literal: truemoduleRodauthFeature.define(:email_auth,:EmailAuth)dodepends:login,:email_basenotice_flash"An email has been sent to you with a link to login to your account",'email_auth_email_sent'error_flash"There was an error logging you in"error_flash"There was an error requesting an email link to authenticate",'email_auth_request'error_flash"An email has recently been sent to you with a link to login",'email_auth_email_recently_sent'error_flash"There was an error logging you in: invalid email authentication key",'no_matching_email_auth_key'loaded_templates%w'email-auth email-auth-request-form email-auth-email'view'email-auth','Login'additional_form_tagsadditional_form_tags'email_auth_request'before'email_auth_request'after'email_auth_request'button'Send Login Link Via Email','email_auth_request'redirect(:email_auth_email_sent){default_post_email_redirect}redirect(:email_auth_email_recently_sent){default_post_email_redirect}email:email_auth,'Login Link'auth_value_method:email_auth_deadline_column,:deadlineauth_value_method:email_auth_deadline_interval,{:days=>1}.freezeauth_value_method:email_auth_id_column,:idauth_value_method:email_auth_key_column,:keyauth_value_method:email_auth_key_param,'key'auth_value_method:email_auth_email_last_sent_column,:email_last_sentauth_value_method:email_auth_skip_resend_email_within,300auth_value_method:email_auth_table,:account_email_auth_keysauth_value_method:force_email_auth?,falsesession_key:email_auth_session_key,:email_auth_keyauth_methods(:create_email_auth_key,:email_auth_email_link,:email_auth_key_insert_hash,:email_auth_key_value,:email_auth_request_form,:get_email_auth_key,:get_email_auth_email_last_sent,:remove_email_auth_key,:set_email_auth_email_last_sent)auth_private_methods:account_from_email_auth_keyinternal_request_methodinternal_request_method:email_auth_requestinternal_request_method:valid_email_auth?route(:email_auth_request)do|r|check_already_logged_inbefore_email_auth_request_router.postdoifaccount_from_login(param(login_param))&&open_account?_email_auth_requestelseset_redirect_error_status(no_matching_login_error_status)set_error_reason:no_matching_loginset_redirect_error_flashemail_auth_request_error_flashendredirectemail_auth_email_sent_redirectendendroutedo|r|check_already_logged_inbefore_email_auth_router.getdoifkey=param_or_nil(email_auth_key_param)set_session_value(email_auth_session_key,key)redirect(r.path)endifkey=session[email_auth_session_key]ifaccount_from_email_auth_key(key)email_auth_viewelseremove_session_value(email_auth_session_key)set_redirect_error_flashno_matching_email_auth_key_error_flashredirectrequire_login_redirectendendendr.postdokey=session[email_auth_session_key]||param(email_auth_key_param)unlessaccount_from_email_auth_key(key)set_redirect_error_status(invalid_key_error_status)set_error_reason:invalid_email_auth_keyset_redirect_error_flashemail_auth_error_flashredirectemail_auth_email_sent_redirectendlogin('email_auth')endenddefcreate_email_auth_keytransactiondoifemail_auth_key_value=get_email_auth_key(account_id)set_email_auth_email_last_sent@email_auth_key_value=email_auth_key_valueelsife=raised_uniqueness_violation{email_auth_ds.insert(email_auth_key_insert_hash)}# If inserting into the email auth table causes a violation, we can pull the # existing email auth key from the table, or reraise.raiseeunless@email_auth_key_value=get_email_auth_key(account_id)endendenddefset_email_auth_email_last_sentemail_auth_ds.update(email_auth_email_last_sent_column=>Sequel::CURRENT_TIMESTAMP)ifemail_auth_email_last_sent_columnenddefget_email_auth_email_last_sentifcolumn=email_auth_email_last_sent_columnifts=email_auth_ds.get(column)convert_timestamp(ts)endendenddefremove_email_auth_keyemail_auth_ds.deleteenddefaccount_from_email_auth_key(key)@account=_account_from_email_auth_key(key)enddefemail_auth_email_linktoken_link(email_auth_route,email_auth_key_param,email_auth_key_value)enddefget_email_auth_key(id)ds=email_auth_ds(id)ds.where(Sequel::CURRENT_TIMESTAMP>email_auth_deadline_column).deleteds.get(email_auth_key_column)enddefemail_auth_request_formrender('email-auth-request-form')enddefafter_login_entered_during_multi_phase_login# If forcing email auth, just send the email link._email_auth_request_and_redirectifforce_email_auth?superenddefuse_multi_phase_login?trueenddefpossible_authentication_methodsmethods=supermethods<<'email_auth'if!methods.include?('password')&&allow_email_auth?methodsendprivatedef_multi_phase_login_formsforms=superforms<<[30,email_auth_request_form,:_email_auth_request_and_redirect]ifvalid_login_entered?&&allow_email_auth?formsenddefemail_auth_email_recently_sent?(email_last_sent=get_email_auth_email_last_sent)&&(Time.now-email_last_sent<email_auth_skip_resend_email_within)enddef_email_auth_request_and_redirect_email_auth_requestredirectemail_auth_email_sent_redirectenddef_email_auth_requestifemail_auth_email_recently_sent?set_redirect_error_flashemail_auth_email_recently_sent_error_flashredirectemail_auth_email_recently_sent_redirectendgenerate_email_auth_key_valuetransactiondobefore_email_auth_requestcreate_email_auth_keysend_email_auth_emailafter_email_auth_requestendset_notice_flashemail_auth_email_sent_notice_flashendattr_reader:email_auth_key_valuedefallow_email_auth?defined?(super)?super:trueenddefafter_login# Remove the email auth key after any login, even if# it is a password login. This is done to invalidate# the email login when a user has a password and requests# email authentication, but then remembers their password# and doesn't need the link. At that point, the link# that allows login access to the account becomes a# security liability, and it is best to remove it.remove_email_auth_keysuperenddefafter_close_accountremove_email_auth_keysuperifdefined?(super)enddefgenerate_email_auth_key_value@email_auth_key_value=random_keyenddefuse_date_arithmetic?super||db.database_type==:mysqlenddefemail_auth_key_insert_hashhash={email_auth_id_column=>account_id,email_auth_key_column=>email_auth_key_value}set_deadline_value(hash,email_auth_deadline_column,email_auth_deadline_interval)hashenddefemail_auth_ds(id=account_id)db[email_auth_table].where(email_auth_id_column=>id)enddef_account_from_email_auth_key(token)account_from_key(token,account_open_status_value){|id|get_email_auth_key(id)}endendend