FEATURE: Disallow login via omniauth when user has 2FA enabled.

This commit is contained in:
Guo Xiang Tan 2018-03-01 15:47:07 +08:00
parent 0fabf80dca
commit fb75f188ba
7 changed files with 77 additions and 25 deletions
app
assets/javascripts/discourse
controllers
templates
mobile/modal
modal
controllers/users
config/locales
lib/auth
spec/requests

@ -27,16 +27,20 @@ export default Ember.Controller.extend(ModalFunctionality, {
loggingIn: false,
loggedIn: false,
processingEmailLink: false,
showLoginButtons: true,
canLoginLocal: setting('enable_local_logins'),
canLoginLocalWithEmail: setting('enable_local_logins_via_email'),
loginRequired: Em.computed.alias('application.loginRequired'),
resetForm: function() {
this.set('authenticate', null);
this.set('loggingIn', false);
this.set('loggedIn', false);
this.set('secondFactorRequired', false);
this.setProperties({
'authenticate': null,
'loggingIn': false,
'loggedIn': false,
'secondFactorRequired': false,
'showLoginButtons': true,
});
$("#credentials").show();
$("#second-factor").hide();
},
@ -250,16 +254,28 @@ export default Ember.Controller.extend(ModalFunctionality, {
}).property('authenticate'),
authenticationComplete(options) {
const self = this;
function loginError(errorMsg, className) {
function loginError(errorMsg, className, callback) {
showModal('login');
Ember.run.next(function() {
Ember.run.next(() => {
callback();
self.flash(errorMsg, className || 'success');
self.set('authenticate', null);
});
}
if (options.omniauth_disallow_totp) {
return loginError(I18n.t('login.omniauth_disallow_totp'), 'error', () => {
this.setProperties({
'loginName': options.email,
'showLoginButtons': false,
});
$('#login-account-password').focus();
});
}
for (let i=0; i<AuthErrors.length; i++) {
const cond = AuthErrors[i];
if (options[cond]) {

@ -1,10 +1,12 @@
{{#login-modal screenX=lastX screenY=lastY loginName=loginName loginPassword=loginPassword loginSecondFactor=loginSecondFactor action="login"}}
{{#d-modal-body title="login.title" class="login-modal"}}
{{login-buttons
canLoginLocalWithEmail=canLoginLocalWithEmail
processingEmailLink=processingEmailLink
emailLogin='emailLogin'
externalLogin='externalLogin'}}
{{#if showLoginButtons}}
{{login-buttons
canLoginLocalWithEmail=canLoginLocalWithEmail
processingEmailLink=processingEmailLink
emailLogin='emailLogin'
externalLogin='externalLogin'}}
{{/if}}
{{#if canLoginLocal}}
<form id='login-form' method='post'>

@ -1,10 +1,12 @@
{{#login-modal screenX=lastX screenY=lastY loginName=loginName loginPassword=loginPassword loginSecondFactor=loginSecondFactor action="login"}}
{{#d-modal-body title="login.title" class="login-modal"}}
{{login-buttons
canLoginLocalWithEmail=canLoginLocalWithEmail
processingEmailLink=processingEmailLink
emailLogin='emailLogin'
externalLogin='externalLogin'}}
{{#if showLoginButtons}}
{{login-buttons
canLoginLocalWithEmail=canLoginLocalWithEmail
processingEmailLink=processingEmailLink
emailLogin='emailLogin'
externalLogin='externalLogin'}}
{{/if}}
{{#if canLoginLocal}}
<form id='login-form' method='post'>

@ -114,6 +114,11 @@ class Users::OmniauthCallbacksController < ApplicationController
end
def user_found(user)
if user.totp_enabled?
@auth_result.omniauth_disallow_totp = true
return
end
# automatically activate/unstage any account if a provider marked the email valid
if @auth_result.email_valid && @auth_result.email == user.email
user.update!(staged: false)

@ -1140,6 +1140,7 @@ en:
not_allowed_from_ip_address: "You can't login from that IP address."
admin_not_allowed_from_ip_address: "You can't log in as admin from that IP address."
resend_activation_email: "Click here to send the activation email again."
omniauth_disallow_totp: "Your account has 2FA enabled. Please login with your password."
resend_title: "Resend Activation Email"
change_email: "Change Email Address"

@ -4,7 +4,7 @@ class Auth::Result
:awaiting_approval, :authenticated, :authenticator_name,
:requires_invite, :not_allowed_from_ip_address,
:admin_not_allowed_from_ip_address, :omit_username,
:skip_email_validation, :destination_url
:skip_email_validation, :destination_url, :omniauth_disallow_totp
attr_accessor(
:failed,
@ -42,13 +42,22 @@ class Auth::Result
date: I18n.l(user.suspended_till, format: :date_only), reason: user.suspend_reason)
}
else
result = {
authenticated: !!authenticated,
awaiting_activation: !!awaiting_activation,
awaiting_approval: !!awaiting_approval,
not_allowed_from_ip_address: !!not_allowed_from_ip_address,
admin_not_allowed_from_ip_address: !!admin_not_allowed_from_ip_address
}
result =
if omniauth_disallow_totp
{
omniauth_disallow_totp: !!omniauth_disallow_totp,
email: email
}
else
{
authenticated: !!authenticated,
awaiting_activation: !!awaiting_activation,
awaiting_approval: !!awaiting_approval,
not_allowed_from_ip_address: !!not_allowed_from_ip_address,
admin_not_allowed_from_ip_address: !!admin_not_allowed_from_ip_address
}
end
result[:destination_url] = destination_url if authenticated && destination_url.present?
result
end

@ -132,6 +132,23 @@ RSpec.describe Users::OmniauthCallbacksController do
expect(user.registration_ip_address).to be_present
end
context 'when user has second factor enabled' do
before do
user.create_totp(enabled: true)
end
it 'should return the right response' do
get "/auth/google_oauth2/callback.json"
expect(response).to be_success
response_body = JSON.parse(response.body)
expect(response_body["email"]).to eq(user.email)
expect(response_body["omniauth_disallow_totp"]).to eq(true)
end
end
context 'when user has not verified his email' do
before do
GoogleUserInfo.create!(google_user_id: '12345', user: user)