Skip to content

[idea] SourceAFIS as Keycloak plugin #73

Description

@delcroip

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
}
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions