TwoFactorPage.java
/*
* Copyright (C) 2000 - 2025 Silverpeas
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* As a special exception to the terms and conditions of version 3.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* Open Source Software ("FLOSS") applications as described in Silverpeas's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* "https://www.silverpeas.org/legal/floss_exception.html"
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.silverpeas.mobile.client.pages.connexion;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.PasswordTextBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.TextBoxBase;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import org.silverpeas.mobile.client.common.AuthentificationManager;
import org.silverpeas.mobile.client.common.resources.ResourcesManager;
import org.silverpeas.mobile.client.components.base.PageContent;
import org.silverpeas.mobile.client.resources.ApplicationMessages;
public class TwoFactorPage extends PageContent {
private static TwoFactorPageUiBinder uiBinder = GWT.create(TwoFactorPageUiBinder.class);
private String login, password, domainId;
@UiField(provided = true)
protected ApplicationMessages msg = null;
@UiField
Anchor go;
@UiField
PasswordTextBox codeField;
@UiField
CheckBox trustDevice;
@UiField
TextBox recoveryCodeField;
@UiField
Anchor recoveryToggle;
@UiField
DivElement totpSection;
@UiField
DivElement recoverySection;
@UiField
Label codeError;
private boolean recoveryMode = false;
@UiField
FormPanel form;
@UiField
DivElement version;
public void setIds(String login, String password, String domainId) {
this.login = login;
this.password = password;
this.domainId = domainId;
}
interface TwoFactorPageUiBinder extends UiBinder<Widget, TwoFactorPage> {}
public TwoFactorPage() {
msg = GWT.create(ApplicationMessages.class);
initWidget(uiBinder.createAndBindUi(this));
trustDevice.setVisible(Boolean.parseBoolean(
ResourcesManager.getParam("twoFactorTrustedDeviceEnabled")));
codeField.getElement().setId("totp-code");
codeField.getElement().setAttribute("inputmode", "numeric");
codeField.getElement().setAttribute("pattern", "[0-9]*");
codeField.getElement().setAttribute("autocomplete", "one-time-code");
codeField.getElement().setAttribute("autocapitalize", "none");
codeField.getElement().setAttribute("autocorrect", "off");
codeField.getElement().setAttribute("spellcheck", "off");
codeField.getElement().setAttribute("placeholder", msg.codeLabel().asString());
recoveryCodeField.getElement().setAttribute("autocomplete", "off");
recoveryCodeField.getElement().setAttribute("autocapitalize", "none");
recoveryCodeField.getElement().setAttribute("autocorrect", "off");
recoveryCodeField.getElement().setAttribute("spellcheck", "false");
recoveryCodeField.getElement().setAttribute("placeholder", msg.recoveryCodePlaceholder());
codeError.getElement().setId("two-factor-error");
codeError.getElement().setAttribute("role", "alert");
codeField.getElement().setAttribute("aria-describedby", "two-factor-error");
recoveryCodeField.getElement().setAttribute("aria-describedby", "two-factor-error");
form.getElement().setId("formLogin");
form.getElement().setAttribute("autocomplete", "off");
version.setId("version");
version.setInnerText(msg.version() + " " + ResourcesManager.getVersion());
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
DOM.getElementById("page-login-title")
.setInnerHTML(ResourcesManager.getLabel("login.title"));
}
});
}
@UiHandler({"codeField", "recoveryCodeField"})
void codeChange(KeyUpEvent event) {
clearCodeError();
}
private void clearCodeError() {
codeError.setText("");
codeField.getElement().getStyle().clearBackgroundColor();
recoveryCodeField.getElement().getStyle().clearBackgroundColor();
codeField.getElement().removeAttribute("aria-invalid");
recoveryCodeField.getElement().removeAttribute("aria-invalid");
}
@UiHandler("recoveryToggle")
void toggleRecoveryMode(ClickEvent event) {
event.preventDefault();
clearCodeError();
recoveryMode = !recoveryMode;
totpSection.getStyle().setProperty("display", recoveryMode ? "none" : "block");
recoverySection.getStyle().setProperty("display", recoveryMode ? "block" : "none");
recoveryToggle.setText(recoveryMode ? msg.useAuthenticatorCode() : msg.useRecoveryCode());
}
/**
* Gestion du clique sur le bouton go.
*/
@UiHandler("go")
void connexion(ClickEvent e) {
String code = recoveryMode ? recoveryCodeField.getText() : codeField.getText();
if (code == null || code.trim().isEmpty()) {
(recoveryMode ? recoveryCodeField : codeField).getElement().getStyle().setBackgroundColor("#ec9c01");
return;
}
clearCodeError();
final boolean submittedRecoveryCode = recoveryMode;
final TextBoxBase submittedField = recoveryMode ? recoveryCodeField : codeField;
AuthentificationManager.getInstance()
.authenticateTwoFactor(login, password, domainId, code.trim(), trustDevice.getValue(), null,
() -> {
submittedField.getElement().getStyle().setBackgroundColor("#ec9c01");
submittedField.getElement().setAttribute("aria-invalid", "true");
codeError.setText(submittedRecoveryCode
? msg.recoveryCodeRejected() : msg.twoFactorCodeRejected());
});
}
}