Commit 51d541d9 authored by o@immerda.ch's avatar o@immerda.ch
Browse files

Merge branch '2faDelay' into 'master'

authentication convenience

See merge request !54
parents 291adf17 428e2d83
Pipeline #8608 passed with stage
in 2 minutes and 31 seconds
......@@ -150,7 +150,6 @@ class Authenticator
'/jabber/create',
'/jabber/reset_password',
'/jabber/delete',
'/auth/master',
'/auth/handoff',
'/invites/generate',
'/resource/self_create',
......
......@@ -103,6 +103,10 @@ class IApiConf
end
end
def tfa_validity
18.hours
end
# Example Ltd.
def webauthn_rp_name
required_key(config('webauthn',true),'rp_name')
......
......@@ -15,8 +15,9 @@ module ApiTokenHelpers
end
end
def api_token_age(email, token)
return Float::INFINITY unless token
def api_token_age(email)
token = parsed_body['token']
return nil unless token.present?
catch :secretbox_open_err do
ticket = Base64.decode64(token)
return false unless ticket
......@@ -31,7 +32,7 @@ module ApiTokenHelpers
end
end
end
Float::INFINITY
nil
end
def self.valid_api_token?(token, email, kind)
......
......@@ -93,6 +93,7 @@ class AuthManager
# Those variables control how the rest of auth will behave
need_2fa = options[:supports_2fa]
skip_2fa = options[:skip_2fa]
allow_master_pw = options[:allow_master_pw] || !user.has_2fa? || need_2fa
need_master_pw = options[:master_pw]
has_app_pw = user.mail_crypt_enabled? &&
......@@ -131,7 +132,7 @@ class AuthManager
end
if user.has_2fa?
if need_2fa
if need_2fa && !skip_2fa
res = {state: 'missing' }
if options[:webauthn].present?
res = validate_webauthn(user, options[:webauthn])
......
......@@ -3,39 +3,6 @@ class IApi < Sinatra::Base
helpers ApiTokenHelpers
namespace '/auth' do
# deprecated
post '/master' do
email = validate_email(parsed_body['email'].downcase)
user = AuthManager.auth(email, parsed_body,
false,
unlock: parsed_body['unlock'],
master_pw: true,
trusted: true,
totp: parsed_body['totp'],
webauthn: parsed_body['webauthn'],
supports_2fa: true)
if user[:state] == 'success'
IApiLog.info("successful #{user[:log]}")
return json({
email: email,
result: 'success',
locked: user[:locked],
recovery_email_set: user[:recovery_email_set],
requires_2fa: user[:requires_2fa],
mail_crypt_enabled: user[:mail_crypt_enabled],
mail_crypt_recovery_token_present: user[:mail_crypt_recovery_token_present],
possible_resources: user[:possible_resources],
})
else
if user[:msg] == 'missing_2fa'
client_error('missing_2fa')
else
IApiLog.info("master auth failed #{user[:log]}")
client_error('auth_fail')
end
end
end
def application_specific_secret(info)
return unless info
if info[:api_access]
......@@ -51,6 +18,8 @@ class IApi < Sinatra::Base
saml_req = parsed_body['saml_request']
info = SamlManager.sp_info(saml_req)
sps = application_specific_secret(info)
skip_2fa = api_token_age(email) &&
api_token_age(email) < IApiConf.tfa_validity
user = AuthManager.auth(email, parsed_body,
false,
......@@ -60,6 +29,7 @@ class IApi < Sinatra::Base
totp: parsed_body['totp'],
webauthn: parsed_body['webauthn'],
supports_2fa: true,
skip_2fa: skip_2fa,
get_application_specific_secret: sps)
saml = SamlManager.process_saml_request(saml_req, email, user[:application_specific_secret])
......@@ -116,13 +86,16 @@ class IApi < Sinatra::Base
allowed = IApiConf.acl.is_allowed_to_access?(email, saml[:plain_issuer])
end
if saml && allowed
token_age = api_token_age(email, parsed_body['token'])
valid = token_age < saml[:max_reissue_time]
if saml && allowed && api_token_age(email)
valid = api_token_age(email) < saml[:max_reissue_time]
valid_with_tfa =
api_token_age(email) < saml[:max_reissue_time_2fa]
if !valid && token_age < saml[:max_reissue_time_2fa]
# if the user has webauthn and we are within the :max_reissue_time_2fa
# then we only ask for a challenge and not for the full password.
if !valid && valid_with_tfa
user = EmailUser.active_by_email(email)
if user.has_2fa?
if user.web_authn_credentials.present?
if AuthManager.auth_2fa(user,
totp: parsed_body['totp'],
webauthn: parsed_body['webauthn'])
......@@ -174,6 +147,8 @@ class IApi < Sinatra::Base
post '/handoff' do
email = validate_email(parsed_body['email'].downcase)
user = nil
skip_2fa = api_token_age(email) &&
api_token_age(email) < IApiConf.tfa_validity
do_auth = -> do
user = AuthManager.auth(email, parsed_body,
false,
......@@ -182,7 +157,9 @@ class IApi < Sinatra::Base
generate_temp_pw: true,
totp: parsed_body['totp'],
webauthn: parsed_body['webauthn'],
supports_2fa: true)
supports_2fa: true,
skip_2fa: skip_2fa,
)
end
do_auth.call
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment