-
Notifications
You must be signed in to change notification settings - Fork 106
Open
Description
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
}
}
Metadata
Metadata
Assignees
Labels
No labels