BiometricAuthHelper.java
/*
* Copyright (C) 2000 - 2026 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.common.auth;
import com.google.gwt.core.client.GWT;
import org.silverpeas.mobile.client.common.auth.resources.BiometricMessages;
public class BiometricAuthHelper {
private static final BiometricMessages MESSAGES = GWT.create(BiometricMessages.class);
/** WebAuthn must be available before requesting local biometric authentication. */
public static native boolean isSupported() /*-{
return $wnd.isSecureContext === true
&& !!$wnd.PublicKeyCredential
&& !!$wnd.navigator.credentials
&& typeof $wnd.navigator.credentials.create === "function"
&& typeof $wnd.navigator.credentials.get === "function"
&& !!$wnd.crypto
&& typeof $wnd.crypto.getRandomValues === "function";
}-*/;
public static native void loginLocal(AuthCallback callback) /*-{
if (!@org.silverpeas.mobile.client.common.auth.BiometricAuthHelper::isSupported()()) {
callback.@org.silverpeas.mobile.client.common.auth.AuthCallback::onResult(Z)(false);
return;
}
function base64ToBuffer(base64) {
var str = atob(base64);
var buf = new ArrayBuffer(str.length);
var bufView = new Uint8Array(buf);
for (var i = 0; i < str.length; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
var stored = JSON.parse($wnd.localStorage.getItem("localCredential"));
if (!stored) {
callback.@org.silverpeas.mobile.client.common.auth.AuthCallback::onResult(Z)(false);
return;
}
var publicKey = {
challenge: new Uint8Array(32),
allowCredentials: [{
id: base64ToBuffer(stored.rawId),
type: "public-key"
}],
userVerification: "required",
timeout: 60000
};
$wnd.crypto.getRandomValues(publicKey.challenge);
$wnd.navigator.credentials.get({ publicKey: publicKey })
.then(function(assertion) {
callback.@org.silverpeas.mobile.client.common.auth.AuthCallback::onResult(Z)(true);
}, function(err) {
console.error(err);
callback.@org.silverpeas.mobile.client.common.auth.AuthCallback::onResult(Z)(false);
});
}-*/;
public static native void registerLocal(
String appName,
String successMessage,
String errorMessage
) /*-{
if (!@org.silverpeas.mobile.client.common.auth.BiometricAuthHelper::isSupported()()) {
$wnd.alert(errorMessage);
return;
}
function bufferToBase64(buf) {
return btoa(String.fromCharCode.apply(null, new Uint8Array(buf)));
}
var publicKey = {
challenge: new Uint8Array(32),
rp: { name: appName },
user: {
id: new Uint8Array(16),
name: "user_local",
displayName: "Utilisateur Local"
},
pubKeyCredParams: [{ type: "public-key", alg: -7 }, { type: "public-key", alg: -257 }],
authenticatorSelection: { userVerification: "required" },
timeout: 60000
};
$wnd.crypto.getRandomValues(publicKey.challenge);
$wnd.crypto.getRandomValues(publicKey.user.id);
$wnd.navigator.credentials.create({ publicKey: publicKey })
.then(function(credential) {
var stored = {
id: credential.id,
rawId: bufferToBase64(credential.rawId),
response: {
clientDataJSON: bufferToBase64(credential.response.clientDataJSON),
attestationObject: bufferToBase64(credential.response.attestationObject)
}
};
$wnd.localStorage.setItem("localCredential", JSON.stringify(stored));
$wnd.alert(successMessage);
}, function(err) {
console.error(err);
$wnd.alert(errorMessage);
});
}-*/;
public static void register() {
if (!isSupported()) {
com.google.gwt.user.client.Window.alert(MESSAGES.unavailable());
return;
}
registerLocal(
MESSAGES.appName(),
MESSAGES.successMessage(),
MESSAGES.errorMessage()
);
}
public static native boolean isRegistered() /*-{
var stored = $wnd.localStorage.getItem("localCredential");
return stored != null;
}-*/;
}