Repository navigation
Add facial login authentication support for Omarchy #4982
Replies: 8 comments 19 replies
|
the new dell xps panther lake laptops removed fingerprint reader so the only biometric option is face unlock for these laptops |
|
https://github.com/tyvsmith/facelock is a pam module like fprint, so it should be easy to integrate with existing stuff. and it works well with windows hello cameras found in many modern laptops |
|
I had a agent do it, and asked it for precise instruction on how to replicate: Face Unlock (facelock) for Omarchy — Implementation Notes
1. Feature summaryOmarchy's lock screen ships password and fingerprint unlock flows, driven by PAM services This change adds a third unlock method, face unlock, to two surfaces:
2. How it works (architecture)3. Prerequisites
4. Lock screen implementation4.1 PAM serviceFile: 4.2 Shell plugin (cloned)The stock lock plugin lives at omarchy plugin clone omarchy.lock # → ~/.config/omarchy/plugins/storm.lock/Service.qml — new propertiesproperty bool faceAuthenticating: false
property bool faceConfigured: false
readonly property bool authenticating: authenticatingPassword || fingerprintAuthenticating || faceAuthenticatingService.qml — detect facelock (mirrors the fingerprint check)function refreshFaceStatus() {
if (!faceCheckProc.running) faceCheckProc.running = true
}
Process {
id: faceCheckProc
command: ["bash", "-c", "if [[ -f /etc/pam.d/omarchy-lock-face ]] && command -v facelock >/dev/null 2>&1 && facelock list 2>/dev/null | grep -q '\\.onnx'; then echo yes; else echo no; fi"]
stdout: StdioCollector { id: faceCheckStdout; waitForEnd: true }
onExited: {
root.faceConfigured = String(faceCheckStdout.text || "").trim() === "yes"
if (!root.faceConfigured && facePam.active) facePam.abort()
}
}Called from Service.qml — the face flow// Face unlock only runs when explicitly requested — the user presses Enter
// on an empty password field. It never auto-starts on lock.
function startFace() {
if (!lockRequested || !sessionLock.secure || !faceConfigured) return
if (facePam.active || faceAuthenticating) return
faceAuthenticating = true
if (!facePam.start()) {
faceAuthenticating = false
}
}
function handleFaceFinished(result) {
faceAuthenticating = false
if (!lockRequested) return
if (result === PamResult.Success) {
finishUnlock()
} else if (faceConfigured) {
failureMessage = "Face not recognized"
logEvent("face-denied")
runWake()
}
}PamContext {
id: facePam
config: "omarchy-lock-face"
user: root.userName
onCompleted: function(result) {
root.handleFaceFinished(result)
}
onError: function(error) {
root.faceAuthenticating = false
if (root.lockRequested && root.faceConfigured) {
root.failureMessage = "Face not recognized"
root.logEvent("face-denied")
root.runWake()
}
}
}
The LockView wiring in LockView {
...
faceConfigured: root.faceConfigured
onFaceRequested: root.startFace()
...
}LockView.qml — explicit trigger + face iconNew property and signal: property bool faceConfigured: false
signal faceRequested()Enter on an empty field fires onAccepted: {
var submitted = root.passwordText
root.passwordTextEdited("")
if (submitted.length > 0) root.submitPassword(submitted)
else if (root.faceConfigured) root.faceRequested()
}Placeholder hint changes when face is available: readonly property string placeholderText: faceConfigured ? "Password, or press Enter" : "Enter Password"Face icon pinned inside the field's left edge (glyph Text {
id: faceIcon
objectName: "faceIndicator"
anchors.left: parent.left
anchors.leftMargin: inputField.borderLeft + 18
anchors.verticalCenter: parent.verticalCenter
visible: root.faceConfigured
text: ""
color: Color.lock.placeholder
font.family: Style.font.family
font.pixelSize: Math.round(root.fieldFontSize * 1.1)
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}The existing 5. Polkit dialog implementation5.1 PAM overridepolkitd authenticates with the PAM service Semantics (identical to the fingerprint convention):
5.2 Shell plugin (cloned)omarchy plugin clone omarchy.polkit # → ~/.config/omarchy/plugins/storm.polkit/PolkitModel.js — face detectionNew function fingerprintConfiguredFromPamConfig(raw) {
return pamModulePresent(raw, "pam_fprintd.so")
}
function faceConfiguredFromPamConfig(raw) {
// Face unlock is available whenever pam_facelock appears in the auth stack,
// with the same lid-gate allowance as the fingerprint stack.
return pamModulePresent(raw, "pam_facelock.so")
}
function pamModulePresent(raw, moduleName) {
var lines = String(raw || "").split("\n")
for (var i = 0; i < lines.length; i++) {
var line = lines[i].replace(/^\s+|\s+$/g, "")
if (!line || line.charAt(0) === "#") continue
if (!line.match(/^auth\s+/)) continue
if (line.indexOf(moduleName) !== -1) return true
}
return false
}(Exports: PolkitAgent.qml — face modeNew state and a combined biometric mode: property bool fingerprintConfigured: false
property bool faceConfigured: false
readonly property bool fingerprintMode: fingerprintConfigured && !laptopClosed && dialogVisible && !responseRequired && !submitted && !errorFlash
readonly property bool faceMode: faceConfigured && !laptopClosed && dialogVisible && !responseRequired && !submitted && !errorFlash
readonly property bool biometricMode: fingerprintMode || faceModeThe FileView {
path: "/etc/pam.d/polkit-1"
watchChanges: true
printErrors: false
onLoaded: root.loadPamConfig(text())
onLoadFailed: {
root.fingerprintConfigured = false
root.faceConfigured = false
}
onFileChanged: reload()
}In biometric mode the card collapses to a square with a centered glyph — fingerprint OpticalGlyph {
anchors.centerIn: parent
width: Math.round(root.fieldHeight * 0.7)
height: width
visible: root.biometricMode
// nf-md-fingerprint while the reader is listening, custom face glyph
// while the camera is looking for a face.
text: root.fingerprintMode ? "\udb80\ude37" : "\udb83\udc7b"
fontFamily: root.fontFamily
fontSize: Math.round(root.fieldHeight * 0.7)
color: root.errorFlash ? Color.polkit.textError : root.accent
}(
6. Step-by-step reproduction# 1. Install & set up facelock (AUR)
yay -S facelock-bin
sudo systemctl enable --now facelock
sudo usermod -aG facelock $USER # then log out/in
sudo facelock add # enroll a face
# 2. Clone the plugins to customize
omarchy plugin clone omarchy.lock
omarchy plugin clone omarchy.polkit
# 3. Write the PAM files (as root)
# /etc/pam.d/omarchy-lock-face → see §4.1
# /etc/pam.d/polkit-1 → see §5.1
# 4. Apply the code from §4.2/§4.3 and §5.2 to the cloned plugins
# 5. Restart the shell and verify
omarchy restart shell
omarchy-shell lock status # expect "face":true7. Verification (real logs)Lock screen — face recognized in ~2.4 s, no password: Polkit ( 8. Caveats & design decisions
9. Proposed upstream integration points
|
|
Hi everyone! I created this PR — it already integrates face unlock with the Omarchy lock screen, but we still need to implement the setup process and polkit integration. For now, I’ve focused mainly on making the experience as smooth as possible, so, among other things:
We still need to add the setup process to install and configure the backend (most likely Facelock), but there’s only one integration point required: So if you want to give it a try already, it’s just a matter of checking out the PR, installing Facelock (or Howdy), and adding a PAM config like the one mentioned here in Backend contract section. @tyvsmith — if you’re willing to help with testing, or even with implementing the setup part, I’d really appreciate it 🙏 |
|
@mateuszkowalczyk Started a prototype and did some testing on my live quattro system here Also main of facelock has addressed your camera timeout issue. Now defaults to immediately closing the camera after a success. |
|
Hi guys! I think we need a little sync here, as there are currently two parallel implementations:
I think both still need some work (1 seems closer to completion IMO as There's also the question of which face unlock backend to use. 1 uses Howdy, while 2 uses Facelock but makes the backend easily replaceable. I've tried both, and my thoughts are:
There's also howdy-next, suggested by @psynyde, but I haven't tested it myself. Having said that, both implementations and both backends work reasonably well IMO. I'm leaning toward Facelock mainly because of that bright-light issue I had with Howdy. No matter which implementation/backend we choose, I'd suggest making the shell widgets backend-independent. It shouldn't be difficult — we just need to avoid checking for a specific PAM module (like Any thoughts on how we should proceed? |
|
Just thought I'd share, I made a plugin that implements howdy that auto fires on system wake https://github.com/tslove923/omarchy-howdy-face-unlock. HANCORE had some great feedback about keeping the QML safe omacom/omarchy-plugin-marketplace#2277. Just in case there's something there you want to pull in upstream. |
|
Here is my lock screen and polkit plugins. They have been working very well for me for about a month now: they both need facelock to be configured manually. |
Uh oh!
There was an error while loading. Please reload this page.
Summary
Would Omarchy be open to supporting optional facial authentication, similar to the existing fingerprint and FIDO2 setup flows?
Why I’m asking
From what I can tell, Omarchy already has support around:
That makes facial authentication feel like a possible fit in the same security/setup area, at least as an optional feature.
Possible direction
I’m not suggesting this as a default. I’m thinking of an optional setup flow, for example:
omarchy-setup-faceQuestions
sudo/polkit, instead of full login/unlock flows?Note
I searched the repository and found existing fingerprint/FIDO2-related support, but I didn’t find an obvious facial-auth implementation.
All reactions