Lock auth provider changes behind `ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH=true`

glitch-soc/security/8c76a208ed30cc1bd54262302b2aed27ae142509
Claire 2024-02-09 15:06:07 +01:00
parent a2a0ea2e1a
commit d0b66f596a
1 changed files with 33 additions and 9 deletions

View File

@ -29,6 +29,7 @@ module User::Omniauthable
# Note that this may leave zombie accounts (with no associated identity) which # Note that this may leave zombie accounts (with no associated identity) which
# can be cleaned up at a later date. # can be cleaned up at a later date.
user = signed_in_resource || identity.user user = signed_in_resource || identity.user
user ||= reattach_for_oauth(auth)
user ||= create_for_oauth(auth) user ||= create_for_oauth(auth)
if identity.user.nil? if identity.user.nil?
@ -39,19 +40,33 @@ module User::Omniauthable
user user
end end
def reattach_for_oauth(auth)
# If allowed, check if a user exists with the provided email address,
# and return it if they does not have an associated identity with the
# current authentication provider.
# This can be used to provide a choice of alternative auth providers
# or provide smooth gradual transition between multiple auth providers,
# but this is discouraged because any insecure provider will put *all*
# local users at risk, regardless of which provider they registered with.
return unless ENV['ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH'] == 'true'
email, email_is_verified = email_from_oauth(auth)
return unless email_is_verified
user = User.find_by(email: email)
return if user.nil? || Identity.exists?(provider: auth.provider, user_id: user.id)
user
end
def create_for_oauth(auth) def create_for_oauth(auth)
# Check if the user exists with provided email. If no email was provided, # Create a user for the given auth params. If no email was provided,
# we assign a temporary email and ask the user to verify it on # we assign a temporary email and ask the user to verify it on
# the next step via Auth::SetupController.show # the next step via Auth::SetupController.show
strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy email, email_is_verified = email_from_oauth(auth)
assume_verified = strategy&.security&.assume_email_is_verified
email_is_verified = auth.info.verified || auth.info.verified_email || auth.info.email_verified || assume_verified
email = auth.info.verified_email || auth.info.email
user = User.find_by(email: email) if email_is_verified
return user unless user.nil? && !Identity.exists?(provider: auth.provider, user_id: user.id)
user = User.new(user_params_from_auth(email, auth)) user = User.new(user_params_from_auth(email, auth))
@ -68,6 +83,15 @@ module User::Omniauthable
private private
def email_from_oauth(auth)
strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy
assume_verified = strategy&.security&.assume_email_is_verified
email_is_verified = auth.info.verified || auth.info.verified_email || auth.info.email_verified || assume_verified
email = auth.info.verified_email || auth.info.email
[email, email_is_verified]
end
def user_params_from_auth(email, auth) def user_params_from_auth(email, auth)
{ {
email: email || "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com", email: email || "#{TEMP_EMAIL_PREFIX}-#{auth.uid}-#{auth.provider}.com",