[idea] SourceAFIS as Keycloak plugin
Hi,
In my work (NGO) I got the feeling that there is few or none opensource fingerprint management platform
while looking for one I got the idea that SourceAFIS might be a solution if turned into a keycloak plugin, not knowing that field it might be a silly idea, so here I am asking you if there is obvious reason not to do it.
Is there any reason not to do it ? If not I will try to have a closer llok if time premits.
here LLM code proposed after my prompt:
""" would it be possible to create a keyclaok plugin that implement SourceAFIS and save the template in such way that it would be linked to the user ? """
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import com.machinezoo.sourceafis.FingerprintTemplate;
import com.machinezoo.sourceafis.FingerprintMatcher;
import javax.ws.rs.core.Response;
import java.util.Base64;
public class FingerprintAuthenticator implements Authenticator {
private static final String TEMPLATE_ATTRIBUTE = "fingerprint_template";
@Override
public void authenticate(AuthenticatorContext context) {
KeycloakSession session = context.getSession();
UserModel user = context.getUser();
RealmModel realm = context.getRealm();
// Get fingerprint data from client (e.g., via form or API)
String fingerprintData = context.getHttpRequest().getFormParameters().getFirst("fingerprint_data");
if (fingerprintData == null) {
context.failureChallenge(Response.status(400).entity("Fingerprint data missing").build());
return;
}
try {
// Convert base64 fingerprint data to byte array
byte[] probeImage = Base64.getDecoder().decode(fingerprintData);
FingerprintTemplate probe = new FingerprintTemplate(probeImage);
// Retrieve stored template from user attributes
String storedTemplateBase64 = user.getFirstAttribute(TEMPLATE_ATTRIBUTE);
if (storedTemplateBase64 == null) {
context.failureChallenge(Response.status(400).entity("No fingerprint template enrolled").build());
return;
}
byte[] storedTemplateBytes = Base64.getDecoder().decode(storedTemplateBase64);
FingerprintTemplate candidate = new FingerprintTemplate(storedTemplateBytes);
// Match fingerprints using SourceAFIS
FingerprintMatcher matcher = new FingerprintMatcher(probe);
double score = matcher.match(candidate);
// Threshold for successful match (adjust as needed)
double threshold = 40.0; // SourceAFIS recommends ~40 for 1:1 verification
if (score >= threshold) {
context.success();
} else {
context.failureChallenge(Response.status(401).entity("Fingerprint verification failed").build());
}
} catch (Exception e) {
context.failureChallenge(Response.status(500).entity("Error processing fingerprint: " + e.getMessage()).build());
}
}
@Override
public void action(AuthenticatorContext context) {
// Handle enrollment action
String fingerprintData = context.getHttpRequest().getFormParameters().getFirst("fingerprint_data");
if (fingerprintData != null) {
try {
// Validate and store fingerprint template
byte[] templateBytes = Base64.getDecoder().decode(fingerprintData);
FingerprintTemplate template = new FingerprintTemplate(templateBytes);
String templateBase64 = Base64.getEncoder().encodeToString(templateBytes);
// Store template in user attributes (encrypt in production)
UserModel user = context.getUser();
user.setSingleAttribute(TEMPLATE_ATTRIBUTE, templateBase64);
context.success();
} catch (Exception e) {
context.failureChallenge(Response.status(500).entity("Error enrolling fingerprint: " + e.getMessage()).build());
}
}
}
@Override
public boolean requiresUser() {
return true;
}
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return user.getFirstAttribute(TEMPLATE_ATTRIBUTE) != null;
}
@Override
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
// Require fingerprint enrollment if not configured
user.addRequiredAction("ENROLL_FINGERPRINT");
}
@Override
public void close() {
// No resources to close
}
}
I don't know anything about Keycloak, so pardon my ignorance if I say something stupid. The way I see this is that there should be core "engine" libraries implementing the algorithm in popular languages and this engine core should be surrounded by tools and libraries that integrate the engine with other software or provide additional functionality. So I think there should be a sourceafis-keycloak repository (or keycloak-sourceafis) that has a dependency on sourceafis-java. I currently have other priorities, but you are free to develop and publish it yourself.