Skip to content

Commit

Permalink
Merge pull request #2 from odeimaiz/odeimaiz-is1315/2fa-improvements
Browse files Browse the repository at this point in the history
✨ Frontend: 2FA improvements
  • Loading branch information
matusdrobuliak66 authored Apr 16, 2024
2 parents e771a2b + e250e96 commit d911fc1
Show file tree
Hide file tree
Showing 13 changed files with 337 additions and 202 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ qx.Class.define("osparc.Preferences", {
apply: "__patchPreference"
},

twoFAPreference: {
nullable: true,
check: ["SMS", "EMAIL", "DISABLED"],
init: "SMS",
event: "changeTwoFAPreference",
apply: "__patchPreference"
},

billingCenterUsageColumnOrder: {
nullable: true,
check: "Array",
Expand All @@ -151,6 +159,25 @@ qx.Class.define("osparc.Preferences", {
}
};
return osparc.data.Resources.fetch("preferences", "patch", params);
},

patchPreferenceField: function(preferenceId, preferenceField, newValue) {
const preferencesSettings = osparc.Preferences.getInstance();

const oldValue = preferencesSettings.get(preferenceId);
if (newValue === oldValue) {
return;
}

preferenceField.setEnabled(false);
osparc.Preferences.patchPreference(preferenceId, newValue)
.then(() => preferencesSettings.set(preferenceId, newValue))
.catch(err => {
console.error(err);
osparc.FlashMessenger.logAs(err.message, "ERROR");
preferenceField.setValue(oldValue);
})
.finally(() => preferenceField.setEnabled(true));
}
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,23 +265,30 @@ qx.Class.define("osparc.auth.LoginPage", {
}, this);

login.addListener("to2FAValidationCode", e => {
const msg = e.getData();
const startIdx = msg.indexOf("+");
const data = e.getData();
login2FAValidationCode.set({
userEmail: login.getEmail(),
userPhoneNumber: msg.substring(startIdx, msg.length)
userEmail: data.userEmail,
smsEnabled: true,
message: data.message
});
if (data.nextStep === "SMS_CODE_REQUIRED") {
login2FAValidationCode.restartSMSButton(data.retryAfter);
} else if (data.nextStep === "EMAIL_CODE_REQUIRED") {
login2FAValidationCode.restartEmailButton(data.retryAfter);
}
pages.setSelection([login2FAValidationCode]);
login.resetValues();
}, this);

verifyPhoneNumber.addListener("skipPhoneRegistration", e => {
const data = e.getData();
login2FAValidationCode.set({
userEmail: e.getData(),
userPhoneNumber: null
userEmail: data.userEmail,
smsEnabled: false,
message: data.message
});
login2FAValidationCode.restartEmailButton(data.retryAfter);
pages.setSelection([login2FAValidationCode]);
login.resetValues();
}, this);

login2FAValidationCode.addListener("done", msg => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,45 +157,53 @@ qx.Class.define("osparc.auth.Manager", {
});
},

login: function(email, password, loginCbk, verifyPhoneCbk, twoFactorAuthCbk, failCbk, context) {
const params = {
email,
password
};
const url = osparc.data.Resources.resources["auth"].endpoints["postLogin"].url;
const xhr = new XMLHttpRequest();
xhr.onload = () => {
if (xhr.status === 202) {
const resp = JSON.parse(xhr.responseText);
const data = resp.data;
if (data["code"] === "PHONE_NUMBER_REQUIRED") {
verifyPhoneCbk.call(context);
} else if (data["code"] === "SMS_CODE_REQUIRED") {
twoFactorAuthCbk.call(context, data["reason"]);
}
} else if (xhr.status === 200) {
const resp = JSON.parse(xhr.responseText);
osparc.data.Resources.getOne("profile", {}, null, false)
.then(profile => {
this.__loginUser(profile);
loginCbk.call(context, resp.data);
})
.catch(err => failCbk.call(context, err.message));
} else {
const resp = JSON.parse(xhr.responseText);
if ("error" in resp && resp["error"]) {
failCbk.call(context, resp["error"]["message"]);
login: function(email, password) {
return new Promise((resolve, reject) => {
const params = {
email,
password
};
const url = osparc.data.Resources.resources["auth"].endpoints["postLogin"].url;
const xhr = new XMLHttpRequest();
xhr.onload = () => {
if (xhr.status === 202) {
const resp = JSON.parse(xhr.responseText);
const data = resp.data;
const message = osparc.auth.core.Utils.extractMessage(data);
const retryAfter = osparc.auth.core.Utils.extractRetryAfter(data)
resolve({
status: xhr.status,
message,
retryAfter,
nextStep: data["name"]
});
} else if (xhr.status === 200) {
const resp = JSON.parse(xhr.responseText);
osparc.data.Resources.getOne("profile", {}, null, false)
.then(profile => {
this.__loginUser(profile);
const data = resp.data;
const message = osparc.auth.core.Utils.extractMessage(data);
resolve({
status: xhr.status,
message
});
})
.catch(err => reject(err.message));
} else {
failCbk.call(context, this.tr("Login failed"));
const resp = JSON.parse(xhr.responseText);
if ("error" in resp && resp["error"]) {
reject(resp["error"]["message"]);
} else {
reject(this.tr("Login failed"));
}
}
}
};
xhr.onerror = () => {
failCbk.call(context, this.tr("Login failed"));
};
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify(params));
};
xhr.onerror = () => reject(this.tr("Login failed"));
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify(params));
});
},

logout: function() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ qx.Class.define("osparc.auth.core.Utils", {
const value = osparc.auth.core.Utils.findParameterInFragment(parameterName);
if (value) {
const removeMe = parameterName + "=" + value;
// In case the parameterhas an ampersand in front
// In case the parameter has an ampersand in front
url = url.replace(";" + removeMe, "");
url = url.replace(removeMe, "");
if (url.slice(-1) === "#") {
Expand All @@ -84,18 +84,16 @@ qx.Class.define("osparc.auth.core.Utils", {
}
},

restartResendTimer: function(button, buttonText, count = 60) {
const refreshIntervalId = setInterval(() => {
if (count > 0) {
count--;
} else {
clearInterval(refreshIntervalId);
}
button.set({
label: count > 0 ? buttonText + ` (${count})` : buttonText,
enabled: count === 0
});
}, 1000);
extractMessage: function(resp) {
const defaultMessage = "";
const retry = "parameters" in resp && "message" in resp["parameters"] ? resp["parameters"]["message"] : defaultMessage;
return retry;
},

extractRetryAfter: function(resp) {
const defaultRetry = 60;
const retry = "parameters" in resp && "retry_2fa_after" in resp["parameters"] ? resp["parameters"]["retry_2fa_after"] : defaultRetry;
return retry;
}
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,40 @@ qx.Class.define("osparc.auth.ui.Login2FAValidationCodeView", {
extend: osparc.auth.core.BaseAuthPage,

properties: {
userPhoneNumber: {
userEmail: {
check: "String",
init: null,
init: "[email protected]",
nullable: false
},

smsEnabled: {
check: "Boolean",
init: false,
nullable: true,
event: "changeUserPhoneNumber"
event: "changeSmsEnabled"
},

userEmail: {
message: {
check: "String",
init: "[email protected]",
nullable: false
init: "We just sent a 6-digit code",
nullable: false,
event: "changeMessage"
}
},

statics: {
restartResendTimer: function(button, buttonText, count = 60) {
const refreshIntervalId = setInterval(() => {
if (count > 0) {
count--;
} else {
clearInterval(refreshIntervalId);
}
button.set({
label: count > 0 ? buttonText + ` (${count})` : buttonText,
enabled: count === 0
});
}, 1000);
}
},

Expand All @@ -40,11 +63,10 @@ qx.Class.define("osparc.auth.ui.Login2FAValidationCodeView", {
__resendCodeEmailBtn: null,

_buildPage: function() {
const introText = new qx.ui.basic.Label();
const justSentText = this.tr("We just sent a 6-digit code to ");
this.bind("userPhoneNumber", introText, "value", {
converter: pNumber => justSentText + (pNumber ? pNumber : this.getUserEmail())
const introText = new qx.ui.basic.Label().set({
rich: true
});
this.bind("message", introText, "value");
this.add(introText);

// form
Expand All @@ -55,7 +77,6 @@ qx.Class.define("osparc.auth.ui.Login2FAValidationCodeView", {
this.addListener("appear", () => {
validateCodeTF.focus();
validateCodeTF.activate();
this.__restartTimers();
});

this.beautifyFormFields();
Expand Down Expand Up @@ -85,60 +106,69 @@ qx.Class.define("osparc.auth.ui.Login2FAValidationCodeView", {
}));
resendLayout.add(resendButtonsLayout);

const resendCodeSMSBtn = this.__resendCodeSMSBtn = new osparc.ui.form.FetchButton().set({
label: this.tr("Via SMS") + ` (60)`,
enabled: false
});
this.bind("userPhoneNumber", resendCodeSMSBtn, "visibility", {
converter: pNumber => pNumber ? "visible" : "excluded"
const resendCodeSMSBtn = this.__resendCodeSMSBtn = new osparc.ui.form.FetchButton(this.tr("Via SMS"));
this.bind("smsEnabled", resendCodeSMSBtn, "visibility", {
converter: smsEnabled => smsEnabled ? "visible" : "excluded"
});
resendButtonsLayout.add(resendCodeSMSBtn, {
flex: 1
});
resendCodeSMSBtn.addListener("execute", () => {
resendCodeSMSBtn.setFetching(true);
osparc.auth.Manager.getInstance().resendCodeViaSMS(this.getUserEmail())
.then(data => {
resendCodeSMSBtn.setFetching(false);
osparc.FlashMessenger.logAs(data.reason, "INFO");
introText.setValue(justSentText + this.getUserPhoneNumber());
this.__restartTimers();
.then(resp => {
const message = osparc.auth.core.Utils.extractMessage(resp);
const retryAfter = osparc.auth.core.Utils.extractRetryAfter(resp);
osparc.FlashMessenger.logAs(message, "INFO");
this.set({
message
});
this.restartSMSButton(retryAfter);
})
.catch(err => {
resendCodeSMSBtn.setFetching(false);
osparc.FlashMessenger.logAs(err.message, "ERROR");
});
.catch(err => osparc.FlashMessenger.logAs(err.message, "ERROR"))
.finally(() => resendCodeSMSBtn.setFetching(false));
}, this);

const resendCodeEmailBtn = this.__resendCodeEmailBtn = new osparc.ui.form.FetchButton().set({
label: this.tr("Via email") + ` (60)`,
enabled: false
});
const resendCodeEmailBtn = this.__resendCodeEmailBtn = new osparc.ui.form.FetchButton(this.tr("Via email"));
resendButtonsLayout.add(resendCodeEmailBtn, {
flex: 1
});
resendCodeEmailBtn.addListener("execute", () => {
resendCodeEmailBtn.setFetching(true);
osparc.auth.Manager.getInstance().resendCodeViaEmail(this.getUserEmail())
.then(data => {
resendCodeEmailBtn.setFetching(false);
osparc.FlashMessenger.logAs(data.reason, "INFO");
introText.setValue(justSentText + this.getUserEmail());
this.__restartTimers();
.then(resp => {
const message = osparc.auth.core.Utils.extractMessage(resp);
const retryAfter = osparc.auth.core.Utils.extractRetryAfter(resp);
osparc.FlashMessenger.logAs(message, "INFO");
this.set({
message
});
this.restartEmailButton(retryAfter);
})
.catch(err => {
resendCodeEmailBtn.setFetching(false);
osparc.FlashMessenger.logAs(err.message, "ERROR");
});
.catch(err => osparc.FlashMessenger.logAs(err.message, "ERROR"))
.finally(() => resendCodeEmailBtn.setFetching(false));
}, this);
this.add(resendLayout);
},

__restartTimers: function() {
if (this.getUserPhoneNumber()) {
osparc.auth.core.Utils.restartResendTimer(this.__resendCodeSMSBtn, this.tr("Via SMS"));
}
osparc.auth.core.Utils.restartResendTimer(this.__resendCodeEmailBtn, this.tr("Via email"));
restartSMSButton: function(retryAfter) {
// start SMS timer button
this.self().restartResendTimer(this.__resendCodeSMSBtn, this.tr("Via SMS"), retryAfter);
// and reset email button
this.__resendCodeEmailBtn.set({
label: this.tr("Via email"),
enabled: true
})
},

restartEmailButton: function(retryAfter) {
// start Email timer button
this.self().restartResendTimer(this.__resendCodeEmailBtn, this.tr("Via email"), retryAfter);
// and reset SMS button
this.__resendCodeSMSBtn.set({
label: this.tr("Via SMS"),
enabled: true
})
},

__validateCodeLogin: function() {
Expand Down
Loading

0 comments on commit d911fc1

Please sign in to comment.