Skip to content

Commit 7d5a7e8

Browse files
authored
✨ FE: 2FA 2nd iteration (ITISFoundation#3679)
1 parent 4744774 commit 7d5a7e8

File tree

15 files changed

+202
-70
lines changed

15 files changed

+202
-70
lines changed

services/static-webserver/client/source/class/osparc/Application.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,8 @@ qx.Class.define("osparc.Application", {
426426
* Resets session and restarts
427427
*/
428428
logout: function() {
429+
osparc.component.message.FlashMessenger.getInstance().logAs(this.tr("You are logged out"));
430+
429431
osparc.data.PollTasks.getInstance().removeTasks();
430432
osparc.auth.Manager.getInstance().logout();
431433
if (this.__mainPage) {

services/static-webserver/client/source/class/osparc/auth/LoginPage.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,14 @@ qx.Class.define("osparc.auth.LoginPage", {
8787
const verifyPhoneNumber = new osparc.auth.ui.VerifyPhoneNumberView();
8888
const resetRequest = new osparc.auth.ui.ResetPassRequestView();
8989
const reset = new osparc.auth.ui.ResetPassView();
90-
const loginSMSCode = new osparc.auth.ui.LoginSMSCodeView();
90+
const login2FAValidationCode = new osparc.auth.ui.Login2FAValidationCodeView();
9191

9292
pages.add(login);
9393
pages.add(register);
9494
pages.add(verifyPhoneNumber);
9595
pages.add(resetRequest);
9696
pages.add(reset);
97-
pages.add(loginSMSCode);
97+
pages.add(login2FAValidationCode);
9898

9999
const page = osparc.auth.core.Utils.findParameterInFragment("page");
100100
const code = osparc.auth.core.Utils.findParameterInFragment("code");
@@ -137,18 +137,27 @@ qx.Class.define("osparc.auth.LoginPage", {
137137
login.resetValues();
138138
}, this);
139139

140-
login.addListener("toSMSCode", e => {
140+
login.addListener("to2FAValidationCode", e => {
141141
const msg = e.getData();
142142
const startIdx = msg.indexOf("+");
143-
loginSMSCode.set({
143+
login2FAValidationCode.set({
144144
userEmail: login.getEmail(),
145145
userPhoneNumber: msg.substring(startIdx, msg.length)
146146
});
147-
pages.setSelection([loginSMSCode]);
147+
pages.setSelection([login2FAValidationCode]);
148148
login.resetValues();
149149
}, this);
150150

151-
loginSMSCode.addListener("done", msg => {
151+
verifyPhoneNumber.addListener("skipPhoneRegistration", e => {
152+
login2FAValidationCode.set({
153+
userEmail: e.getData(),
154+
userPhoneNumber: null
155+
});
156+
pages.setSelection([login2FAValidationCode]);
157+
login.resetValues();
158+
}, this);
159+
160+
login2FAValidationCode.addListener("done", msg => {
152161
login.resetValues();
153162
this.fireDataEvent("done", msg);
154163
}, this);

services/static-webserver/client/source/class/osparc/auth/Manager.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ qx.Class.define("osparc.auth.Manager", {
5858
phone: phoneNumber
5959
}
6060
};
61-
return osparc.data.Resources.fetch("auth", "postVerifyPhoneNumber", params);
61+
return osparc.data.Resources.fetch("auth", "verifyPhoneNumber", params);
6262
},
6363

6464
validateCodeRegister: function(email, phone, code, loginCbk, failCbk, context) {
@@ -69,7 +69,7 @@ qx.Class.define("osparc.auth.Manager", {
6969
code
7070
}
7171
};
72-
osparc.data.Resources.fetch("auth", "postValidationCodeRegister", params)
72+
osparc.data.Resources.fetch("auth", "validateCodeRegister", params)
7373
.then(data => {
7474
osparc.data.Resources.getOne("profile", {}, null, false)
7575
.then(profile => {
@@ -88,7 +88,7 @@ qx.Class.define("osparc.auth.Manager", {
8888
code
8989
}
9090
};
91-
osparc.data.Resources.fetch("auth", "postValidationCodeLogin", params)
91+
osparc.data.Resources.fetch("auth", "validateCodeLogin", params)
9292
.then(data => {
9393
osparc.data.Resources.getOne("profile", {}, null, false)
9494
.then(profile => {
@@ -100,6 +100,26 @@ qx.Class.define("osparc.auth.Manager", {
100100
.catch(err => failCbk.call(context, err.message));
101101
},
102102

103+
resendCodeViaSMS: function(email) {
104+
const params = {
105+
data: {
106+
email,
107+
via: "SMS"
108+
}
109+
};
110+
return osparc.data.Resources.fetch("auth", "resendCode", params);
111+
},
112+
113+
resendCodeViaEmail: function(email) {
114+
const params = {
115+
data: {
116+
email,
117+
via: "Email"
118+
}
119+
};
120+
return osparc.data.Resources.fetch("auth", "resendCode", params);
121+
},
122+
103123
isLoggedIn: function() {
104124
// TODO: how to store this localy?? See http://www.qooxdoo.org/devel/pages/data_binding/stores.html#offline-store
105125
// TODO: check if expired??

services/static-webserver/client/source/class/osparc/auth/core/BaseAuthPage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ qx.Class.define("osparc.auth.core.BaseAuthPage", {
3838
this.set({
3939
layout: new qx.ui.layout.VBox(20),
4040
width: 300,
41-
height: 250
41+
height: 350
4242
});
4343
// at least chrome hates it when a password input field exists
4444
// outside a form, so lets accomodate him

services/static-webserver/client/source/class/osparc/auth/core/Utils.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@
1818
qx.Class.define("osparc.auth.core.Utils", {
1919
type: "static",
2020

21-
statics:
22-
{
21+
statics: {
2322
passwordLengthValidator: function(value, item) {
2423
const valid = value != null && value.length > 3;
2524
if (!valid) {
@@ -83,6 +82,20 @@ qx.Class.define("osparc.auth.core.Utils", {
8382
}
8483
window.history.replaceState("", document.title, url);
8584
}
85+
},
86+
87+
restartResendTimer: function(button, buttonText, count = 60) {
88+
const refreshIntervalId = setInterval(() => {
89+
if (count > 0) {
90+
count--;
91+
} else {
92+
clearInterval(refreshIntervalId);
93+
}
94+
button.set({
95+
label: count > 0 ? buttonText + ` (${count})` : buttonText,
96+
enabled: count === 0
97+
});
98+
}, 1000);
8699
}
87100
}
88101
});

services/static-webserver/client/source/class/osparc/auth/ui/LoginSMSCodeView.js renamed to services/static-webserver/client/source/class/osparc/auth/ui/Login2FAValidationCodeView.js

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
************************************************************************ */
1717

1818

19-
qx.Class.define("osparc.auth.ui.LoginSMSCodeView", {
19+
qx.Class.define("osparc.auth.ui.Login2FAValidationCodeView", {
2020
extend: osparc.auth.core.BaseAuthPage,
2121

2222
properties: {
2323
userPhoneNumber: {
2424
check: "String",
25-
init: "+41-XXXXXXXXX",
26-
nullable: false,
25+
init: null,
26+
nullable: true,
2727
event: "changeUserPhoneNumber"
2828
},
2929

@@ -37,14 +37,16 @@ qx.Class.define("osparc.auth.ui.LoginSMSCodeView", {
3737
members: {
3838
__validateCodeTF: null,
3939
__validateCodeBtn: null,
40-
__resendCodeBtn: null,
40+
__resendCodeSMSBtn: null,
41+
__resendCodeEmailBtn: null,
4142

4243
_buildPage: function() {
43-
const smsCodeDesc = new qx.ui.basic.Label();
44-
this.bind("userPhoneNumber", smsCodeDesc, "value", {
45-
converter: pNumber => this.tr("We just sent a 4-digit code to ") + pNumber
44+
const introText = new qx.ui.basic.Label();
45+
const justSentText = this.tr("We just sent a 4-digit code to ");
46+
this.bind("userPhoneNumber", introText, "value", {
47+
converter: pNumber => justSentText + (pNumber ? pNumber : this.getUserEmail())
4648
});
47-
this.add(smsCodeDesc);
49+
this.add(introText);
4850

4951
const validateCodeTF = this.__validateCodeTF = new qx.ui.form.TextField().set({
5052
placeholder: this.tr("Type code"),
@@ -54,7 +56,7 @@ qx.Class.define("osparc.auth.ui.LoginSMSCodeView", {
5456
this.addListener("appear", () => {
5557
validateCodeTF.focus();
5658
validateCodeTF.activate();
57-
osparc.auth.ui.VerifyPhoneNumberView.restartResendTimer(this.__resendCodeBtn, this.tr("Resend code"));
59+
this.__restartTimers();
5860
});
5961

6062
const validateCodeBtn = this.__validateCodeBtn = new osparc.ui.form.FetchButton(this.tr("Validate")).set({
@@ -64,32 +66,65 @@ qx.Class.define("osparc.auth.ui.LoginSMSCodeView", {
6466
validateCodeBtn.addListener("execute", () => this.__validateCodeLogin(), this);
6567
this.add(validateCodeBtn);
6668

67-
const resendLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(5).set({
69+
const resendLayout = new qx.ui.container.Composite(new qx.ui.layout.VBox(5).set({
6870
alignY: "middle"
6971
}));
7072
const resendCodeDesc = new qx.ui.basic.Label().set({
71-
value: this.tr("Didn't receive the code?")
73+
value: this.tr("Didn't receive the code? Resend code")
7274
});
73-
resendLayout.add(resendCodeDesc, {
75+
resendLayout.add(resendCodeDesc);
76+
77+
const resendBtnsLayout = new qx.ui.container.Composite(new qx.ui.layout.HBox(5).set({
78+
alignY: "middle"
79+
}));
80+
resendLayout.add(resendBtnsLayout);
81+
82+
const resendCodeSMSBtn = this.__resendCodeSMSBtn = new qx.ui.form.Button().set({
83+
label: this.tr("Via SMS") + ` (60)`,
84+
enabled: false
85+
});
86+
this.bind("userPhoneNumber", resendCodeSMSBtn, "visibility", {
87+
converter: pNumber => pNumber ? "visible" : "excluded"
88+
});
89+
resendBtnsLayout.add(resendCodeSMSBtn, {
7490
flex: 1
7591
});
92+
resendCodeSMSBtn.addListener("execute", () => {
93+
osparc.auth.Manager.getInstance().resendCodeViaSMS(this.getUserEmail())
94+
.then(data => {
95+
osparc.component.message.FlashMessenger.logAs(data.reason, "INFO");
96+
introText.setValue(justSentText + this.getUserPhoneNumber());
97+
this.__restartTimers();
98+
})
99+
.catch(err => osparc.component.message.FlashMessenger.logAs(err.message, "ERROR"));
100+
}, this);
76101

77-
this.add(new qx.ui.core.Spacer(null, 20));
78-
const resendCodeBtn = this.__resendCodeBtn = new qx.ui.form.Button().set({
79-
label: this.tr("Resend code") + ` (60)`,
102+
const resendCodeEmailBtn = this.__resendCodeEmailBtn = new qx.ui.form.Button().set({
103+
label: this.tr("Via email") + ` (60)`,
80104
enabled: false
81105
});
82-
resendLayout.add(resendCodeBtn, {
106+
resendBtnsLayout.add(resendCodeEmailBtn, {
83107
flex: 1
84108
});
85-
resendCodeBtn.addListener("execute", () => {
86-
const msg = this.tr("Not yet implemented. Please, reload the page instead");
87-
osparc.component.message.FlashMessenger.getInstance().logAs(msg, "WARNING");
88-
// osparc.auth.ui.VerifyPhoneNumberView.restartResendTimer(this.__resendCodeBtn, this.tr("Resend code"));
109+
resendCodeEmailBtn.addListener("execute", () => {
110+
osparc.auth.Manager.getInstance().resendCodeViaEmail(this.getUserEmail())
111+
.then(data => {
112+
osparc.component.message.FlashMessenger.logAs(data.reason, "INFO");
113+
introText.setValue(justSentText + this.getUserEmail());
114+
this.__restartTimers();
115+
})
116+
.catch(err => osparc.component.message.FlashMessenger.logAs(err.message, "ERROR"));
89117
}, this);
90118
this.add(resendLayout);
91119
},
92120

121+
__restartTimers: function() {
122+
if (this.getUserPhoneNumber()) {
123+
osparc.auth.core.Utils.restartResendTimer(this.__resendCodeSMSBtn, this.tr("Via SMS"));
124+
}
125+
osparc.auth.core.Utils.restartResendTimer(this.__resendCodeEmailBtn, this.tr("Via email"));
126+
},
127+
93128
__validateCodeLogin: function() {
94129
this.__validateCodeBtn.setFetching(true);
95130

services/static-webserver/client/source/class/osparc/auth/ui/LoginView.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ qx.Class.define("osparc.auth.ui.LoginView", {
4040
"toRegister": "qx.event.type.Event",
4141
"toReset": "qx.event.type.Event",
4242
"toVerifyPhone": "qx.event.type.Data",
43-
"toSMSCode": "qx.event.type.Data"
43+
"to2FAValidationCode": "qx.event.type.Data"
4444
},
4545

4646
/*
@@ -91,21 +91,24 @@ qx.Class.define("osparc.auth.ui.LoginView", {
9191
const grp = new qx.ui.container.Composite(new qx.ui.layout.HBox(20));
9292

9393
const registerBtn = this.createLinkButton(this.tr("Create Account"), () => {
94+
registerBtn.setEnabled(false);
9495
osparc.data.Resources.getOne("config")
9596
.then(config => {
9697
if (config["invitation_required"]) {
9798
let text = this.tr("Registration is currently only available with an invitation.");
9899
text += "<br>";
99-
text += this.tr("Please contact [email protected]");
100-
osparc.component.message.FlashMessenger.getInstance().logAs(text, "INFO");
100+
text += this.tr("Please contact ");
101+
osparc.store.StaticInfo.getInstance().getSupportEmail()
102+
.then(supportEmail => {
103+
text += supportEmail;
104+
osparc.component.message.FlashMessenger.getInstance().logAs(text, "INFO");
105+
});
101106
} else {
102107
this.fireEvent("toRegister");
103108
}
104109
})
105-
.catch(err => {
106-
console.error(err);
107-
this.fireEvent("toRegister");
108-
});
110+
.catch(err => console.error(err));
111+
registerBtn.setEnabled(true);
109112
}, this);
110113
osparc.utils.Utils.setIdToWidget(registerBtn, "loginCreateAccountBtn");
111114

@@ -179,10 +182,10 @@ qx.Class.define("osparc.auth.ui.LoginView", {
179182
window.history.replaceState(null, window.document.title, window.location.pathname);
180183
};
181184

182-
const twoFactorAuthCbk = log => {
185+
const twoFactorAuthCbk = msg => {
183186
this.__loginBtn.setFetching(false);
184-
osparc.component.message.FlashMessenger.getInstance().logAs(log, "INFO");
185-
this.fireDataEvent("toSMSCode", log);
187+
osparc.component.message.FlashMessenger.getInstance().logAs(msg, "INFO");
188+
this.fireDataEvent("to2FAValidationCode", msg);
186189
// we don't need the form any more, so remove it and mock-navigate-away
187190
// and thus tell the password manager to save the content
188191
this._formElement.dispose();

0 commit comments

Comments
 (0)