[Bug 39601] New: Add passkey support to Koha as an authentication mechanism
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Bug ID: 39601 Summary: Add passkey support to Koha as an authentication mechanism Change sponsored?: --- Product: Koha Version: Main Hardware: All OS: All Status: NEW Severity: enhancement Priority: P5 - low Component: Authentication Assignee: koha-bugs@lists.koha-community.org Reporter: david@davidnind.com QA Contact: testopia@bugs.koha-community.org CC: dpavlin@rot13.org Consider adding passkey support to Koha as an authentication mechanism. I have seen this method as an option more frequently now with all the various online services I use. Some articles: 1. Bitwarden article - What are passkeys? https://bitwarden.com/blog/what-are-passkeys-and-passkey-login/ 2. tom's guide - What are passkeys? Everything you need to know about the death of passwords https://www.tomsguide.com/news/what-are-passkeys -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #1 from David Nind <david@davidnind.com> --- See Koha Community Chat thread https://chat.koha-community.org/koha-community/pl/4irec4ocjtyy8pgezeaz9c9nwo Includes "It could also be a gateway to finally moving "password" out of the borrowers table and into a "credentials" table...". -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Paul Derscheid <paul.derscheid@lmscloud.de> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |paul.derscheid@lmscloud.de --- Comment #2 from Paul Derscheid <paul.derscheid@lmscloud.de> --- This is properly the best guidance on how to incorporate it: https://www.passkeys.com/guide What we also should do beforehand is to decide on how we'd like to do JSON-RPC in Koha :) See bugs: - 30652: RPC style action endpoint for choosing matches - 34784: Add ability to populate empty item call numbers for a record based on the itemcallnumber system preference - 38224: Move populate_empty_callnumbers logic into Koha::Biblio for reusability - 38226: Add RPC endpoint for batch filling empty callnumbers - 38745: Add a proof of concept JSON-RPC driven endpoint for RPC style calls that don't fit REST ideals Can't yet declare one of them as a dependency for this one. -- You are receiving this mail because: You are the assignee for the bug. You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 David Cook <dcook@prosentient.com.au> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |dcook@prosentient.com.au -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #3 from David Nind <david@davidnind.com> --- An article from Troy Hunt attempting to explain passkeys and why they are needed: Passkeys for Normal People (5 May 2025), https://www.troyhunt.com/passkeys-for-normal-people/ -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Paul Derscheid <paul.derscheid@lmscloud.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|NEW |Needs Signoff -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #4 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Created attachment 185295 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=185295&action=edit Bug 39601: Atomic update -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #5 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Created attachment 185296 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=185296&action=edit [DO NOT PUSH] Bug 39601: DBIC changes -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #6 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Created attachment 185297 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=185297&action=edit Bug 39601: Add staff passkeys (WebAuthn) support Introduce staff passkey (WebAuthn) registration and authentication in Koha. Provides REST endpoints, persistence, UI hooks, and session integration so staff can register a passkey against a patron record and authenticate on the staff login screen using platform authenticators. Implementation: - New controller Koha::REST::V1::Webauthn with endpoints: - POST /api/v1/webauthn/register/challenge - POST /api/v1/webauthn/register - POST /api/v1/webauthn/authenticate/challenge - POST /api/v1/webauthn/authenticate - Use Authen::WebAuthn validate_registration/validate_assertion - Generate cryptographically secure challenges; store challenge and patron_id in the session - Handle base64url consistently for WebAuthn fields; convert to/from standard base64 at the API boundary - Derive origin and rp_id from StaffClientBaseURL (or the request URL) to enforce correct WebAuthn origins - Persist credentials (credential_id, public_key as raw bytes) in the new webauthn_credentials table; set created_on as DATETIME; update sign_count and last_used after successful authentication - Build allowCredentials from stored credential IDs for authentication challenges - On successful authentication, issue a staff session and set the CGISESSID cookie so the user is logged in to the staff interface API: - Add api/v1/swagger/paths/webauthn.yaml defining the WebAuthn endpoints, request/response schemas (including allowCredentials), and x-koha- authorization requiring the catalogue permission DB: - Add webauthn_credentials table via installer/data/mysql/atomicupdate/ bug_39601_add_passkey_support.pl - Add Koha::Schema::Result::WebauthnCredential and Koha::WebauthnCredential(s) object classes UI: - Staff login: add JS helper (auth-webauthn.inc) to request a challenge, convert base64url to bytes, call navigator.credentials.get, send results with credentials: "same-origin", and redirect to mainpage on success - Patron page: add JS helper (passkey-register.inc) to request a registration challenge, include RS256 in pubKeyCredParams for compatibility, convert base64url to bytes, and submit attestation for storage Tests: - t/db_dependent/api/v1/webauthn.t: verify challenge endpoints accept patron_id and userid; return 404 when no credentials; include a mocked registration negative path; follow Koha testing conventions - t/db_dependent/Koha/WebauthnCredentials.t: cover ORM add/search/update/delete Documentation notes: - StaffClientBaseURL must match the scheme/host used by the browser (typically https). Ensure staff is served over the same origin for WebAuthn. - New routes under /api/v1/webauthn and a new table webauthn_credentials. Test plan: 1) Apply patches 2) Run database updates to create webauthn_credentials (updatedatabase). 3) Set StaffClientBaseURL to your staff URL (e.g., http://<name>-intra.localhost when using ktd_proxy, haven't tested with unproxied ktd) and ensure the staff interface is served over the same origin. 4) As a staff user, open a patron record and click Register Passkey from More. Complete the OS-native passkey dialog. Verify a row is stored in webauthn_credentials. - This worked well in Zen (Firefox under the hood), less so with Chromium. - Unsure whether ungoogled Chromium supports using the system password manager, worked with a browser-based password manager, though. - Best to test with many browsers! 5) Navigate to the staff login page and choose Sign in with passkey. Verify a challenge is returned, the browser prompts, and you are logged into the staff interface (redirect to mainpage). 6) Call authenticate_challenge for a patron without credentials and verify a 404 response. 7) Run: - prove t/db_dependent/Koha/WebauthnCredential.t - prove t/db_dependent/api/v1/webauthn.t 8) Sign off or review and FQA. NOTE: I think the controllers still need refactoring, this is more a POC regarding the backend. -- You are receiving this mail because: You are the assignee for the bug. You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #7 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Forgot something: you need to install libauthen-authn-perl via apt. -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #8 from David Cook <dcook@prosentient.com.au> --- (In reply to Paul Derscheid from comment #7)
Forgot something: you need to install libauthen-authn-perl via apt.
I think that should be libauthen-webauthn-perl -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #9 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Yes, sorry, wrote that last minute from memory (which failed me 😅) -- You are receiving this mail because: You are the assignee for the bug. You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Owen Leonard <oleonard@myacpl.org> changed: What |Removed |Added ---------------------------------------------------------------------------- Assignee|koha-bugs@lists.koha-commun |paul.derscheid@lmscloud.de |ity.org | -- You are receiving this mail because: You are the assignee for the bug. You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Katrin Fischer <katrin.fischer@bsz-bw.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|Needs Signoff |Failed QA Keywords| |dependency --- Comment #10 from Katrin Fischer <katrin.fischer@bsz-bw.de> --- * Apply patches * sudo apt-get install libauthen-webauthn-perl * updatedatabase * yarn build * restart_all * ran dbic, but no changes The QA checks and especially the unit tests failing. Am I missing something else? prove t/db_dependent/api/v1/webauthn.t t/db_dependent/api/v1/webauthn.t .. 1/4 # Failed test '404 Not Found' # at t/db_dependent/api/v1/webauthn.t line 60. # got: '500' # expected: '404' # Failed test '404 Not Found' # at t/db_dependent/api/v1/webauthn.t line 63. # got: '500' # expected: '404' # Looks like you failed 2 tests of 4. t/db_dependent/api/v1/webauthn.t .. 2/4 # Failed test 'POST /api/v1/webauthn/authenticate/challenge returns 404 without credentials' # at t/db_dependent/api/v1/webauthn.t line 67. t/db_dependent/api/v1/webauthn.t .. 3/4 # Looks like you failed 1 test of 4. t/db_dependent/api/v1/webauthn.t .. Dubious, test returned 1 (wstat 256, 0x100) Failed 1/4 subtests Test Summary Report ------------------- t/db_dependent/api/v1/webauthn.t (Wstat: 256 (exited 1) Tests: 4 Failed: 1) Failed test: 2 Non-zero exit status: 1 Files=1, Tests=4, 5 wallclock secs ( 0.00 usr 0.00 sys + 5.69 cusr 0.14 csys = 5.83 CPU) Result: FAIL For the test plan, can you explain this step a bit more? 6) Call authenticate_challenge for a patron without credentials and verify a 404 response. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #11 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Thanks for testing Katrin, maybe something has changed in main. I will take a look this afternoon. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #12 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Just took a look, applying the DBIC changes failed on my end, so that needs to be updated, but something else is broken as well. Will revisit this when I have an open time slot. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Paul Derscheid <paul.derscheid@lmscloud.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|Failed QA |Needs Signoff -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Paul Derscheid <paul.derscheid@lmscloud.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #185296|0 |1 is obsolete| | --- Comment #13 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Created attachment 190988 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=190988&action=edit [DO NOT PUSH] Bug 39601: DBIC changes -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Paul Derscheid <paul.derscheid@lmscloud.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #190988|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #14 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Created attachment 190989 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=190989&action=edit [DO NOT PUSH] Bug 39601: DBIC changes -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #15 from Paul Derscheid <paul.derscheid@lmscloud.de> --- I think the DBIC commit was incorrect in the previous iteration. Corrected it, tests pass now and the registration and authentication flows work in Chromium. Please have another look if you want, Katrin. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Katrin Fischer <katrin.fischer@bsz-bw.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|Needs Signoff |Failed QA --- Comment #16 from Katrin Fischer <katrin.fischer@bsz-bw.de> --- Good news first: the tests are passing now! 1) Failing QA tests: Result: [FAIL] Koha/Auth/WebAuthn.pm FAIL forbidden_patterns forbidden pattern: Use https:// instead of http:// for URLs (line 17) FAIL pod_coverage sub generate_challenge has no pod coverage. sub new has no pod coverage. sub verify_assertion has no pod coverage. sub verify_registration has no pod coverage. [PASS] Koha/REST/V1/Auth.pm [FAIL] Koha/REST/V1/Webauthn.pm FAIL forbidden_patterns forbidden pattern: Use https:// instead of http:// for URLs (line 16) forbidden pattern: Use of DateTime->now should certainly be replaced with dt_from_string (bug 24840) (line 257) forbidden pattern: Use of DateTime->now should certainly be replaced with dt_from_string (bug 24840) (line 378) FAIL pod_coverage sub authenticate has no pod coverage. sub authenticate_challenge has no pod coverage. sub register has no pod coverage. sub register_challenge has no pod coverage. FAIL tidiness File is not tidy, please run `perl misc/devel/tidy.pl Koha/REST/V1/Webauthn.pm` [PASS] Koha/Schema/Result/Borrower.pm [FAIL] Koha/Schema/Result/WebauthnCredential.pm FAIL forbidden_patterns forbidden pattern: The current_timestamp syntax has not been generated correctly (see bug 25040) (line 108) SKIP pod_coverage No POD exists [SKIP] Koha/WebauthnCredential.pm SKIP pod_coverage No POD exists [FAIL] Koha/WebauthnCredentials.pm FAIL pod_coverage sub 'object_class' has no pod coverage. [PASS] api/v1/swagger/paths/webauthn.yaml [PASS] api/v1/swagger/swagger.yaml [FAIL] installer/data/mysql/atomicupdate/bug_39601_add_passkey_support.pl FAIL file_permissions File must have the exec flag [FAIL] koha-tmpl/intranet-tmpl/prog/en/includes/auth-webauthn.inc FAIL valid_template : filter not found [FAIL] koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc FAIL tidiness File is not tidy, please run `perl misc/devel/tidy.pl koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc` FAIL valid_template : filter not found [FAIL] koha-tmpl/intranet-tmpl/prog/en/includes/passkey-register.inc FAIL tidiness File is not tidy, please run `perl misc/devel/tidy.pl koha-tmpl/intranet-tmpl/prog/en/includes/passkey-register.inc` FAIL valid_template : filter not found [PASS] koha-tmpl/intranet-tmpl/prog/en/modules/auth.tt [FAIL] koha-tmpl/intranet-tmpl/prog/js/passkey-register.js FAIL tidiness File is not tidy, please run `perl misc/devel/tidy.pl koha-tmpl/intranet-tmpl/prog/js/passkey-register.js` [FAIL] koha-tmpl/intranet-tmpl/prog/js/webauthn-login.js FAIL tidiness File is not tidy, please run `perl misc/devel/tidy.pl koha-tmpl/intranet-tmpl/prog/js/webauthn-login.js` [PASS] members/moremember.pl [FAIL] t/db_dependent/Koha/WebauthnCredentials.t FAIL file_permissions File must have the exec flag FAIL test_no_warnings 'use Test::NoWarnings' must be present in this test file. [FAIL] t/db_dependent/api/v1/webauthn.t FAIL file_permissions File must have the exec flag FAIL forbidden_patterns forbidden pattern: Use https:// instead of http:// for URLs (line 73) 2) Code review a) Spelling We have: More > Register Passkey Create passkey (button) Probably should be: Register passkey b) Translatability Leading spaces usually get lost, better to add this to the i element if possible: + <i class="fa fa-key"></i> + <span class="d-none d-sm-inline"> Passkey</span> c) Using text in JS + var userid = usernameText ? usernameText.replace('Username:', '').trim() : null; I am wondering about this line: Where is "Username:" coming from and could this be a translatable strong, so changing? 3) Testing I should admit that I haven't used passkeys so far, so this is a first and I am still trying to figure out how this works. a) Ubuntu, Firefox When I click on "Register Passkey" I see a note at the top of the browser: Touch your security key to continue with localhost. Don't have a security key to touch here, so got stuck at this step. Returning to Koha I gott a non-descriptive error message after hitting the button: Registration error: {msg} Maybe related to a timeout? b) Ubuntu, Chromium When I click on "Register Passkey" I see a poo-up with a QR code displayed. I tried to scan it with Google Authenticator, but the app keeps dying. It will also display the non-descriptive error message: Registration error: {msg} 4) Questions a) Is there a way to continue testing here without any hardware? (Yubikey or similar?) b) For the test plan, can you explain this step a bit more? Call authenticate_challenge for a patron without credentials and verify a 404 response. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Paul Derscheid <paul.derscheid@lmscloud.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|Failed QA |Needs Signoff -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Paul Derscheid <paul.derscheid@lmscloud.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #185295|0 |1 is obsolete| | --- Comment #17 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Created attachment 194696 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=194696&action=edit Bug 39601: Atomic update -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Paul Derscheid <paul.derscheid@lmscloud.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #185297|0 |1 is obsolete| | --- Comment #18 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Created attachment 194697 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=194697&action=edit Bug 39601: Add staff passkeys (WebAuthn) support Introduce staff passkey (WebAuthn) registration and authentication in Koha. Provides REST endpoints, persistence, UI hooks, and session integration so staff can register a passkey against a patron record and authenticate on the staff login screen using platform authenticators. Implementation: - New helper module Koha::Auth::WebAuthn encapsulating origin/RP ID derivation, challenge generation and validation, patron resolution, base64url encoding utilities, and session management - New controller Koha::REST::V1::Webauthn with endpoints: - POST /api/v1/webauthn/register/challenge - POST /api/v1/webauthn/register - POST /api/v1/webauthn/authenticate/challenge - POST /api/v1/webauthn/authenticate - New typed exception classes in Koha::Exceptions::WebAuthn - Use Authen::WebAuthn validate_registration/validate_assertion - Generate cryptographically secure challenges via Bytes::Random::Secure; store challenge, patron_id and timestamp in the session with 10-minute TTL and consume-after-use to prevent replay - Handle base64url consistently for WebAuthn fields; convert to/from standard base64 at the API boundary - Derive origin and rp_id from StaffClientBaseURL (or the request URL) to enforce correct WebAuthn origins; support reverse proxy headers and http->https origin upgrade - Persist credentials (credential_id, public_key as raw bytes) in the new webauthn_credentials table; update sign_count and last_used_date after successful authentication - Build allowCredentials from stored credential IDs for authentication challenges - On successful authentication, verify account is not locked, issue a staff session and set the CGISESSID cookie so the user is logged in to the staff interface Security: - Challenge TTL (10 min) and single-use consumption prevent replay attacks - Fail-closed patron match guard rejects if either patron ID is undefined - Explicit credential pubkey existence check before assertion validation - account_locked check before issuing session - $is_https derived from already-upgraded origin (not recomputed) API: - Add api/v1/swagger/paths/webauthn.yaml defining the WebAuthn endpoints, request/response schemas (including rp_id/rpId, allowCredentials) - Register endpoints require catalogue permission - Authenticate endpoints are public (no x-koha-authorization) since they are login endpoints DB: - Add webauthn_credentials table via installer/data/mysql/atomicupdate/ bug_39601_add_passkey_support.pl and kohastructure.sql - PK: webauthn_credential_id (per SQL7), COLLATE=utf8mb4_unicode_ci, column COMMENTs, ON UPDATE CASCADE, VARBINARY(1024) for credential_id - Date columns: created_date (timestamp), last_used_date (datetime) per SQL14 - Add Koha::WebauthnCredential(s) object classes UI: - Staff login: add JS helper (auth-webauthn.inc) to request a challenge, convert base64url to bytes, call navigator.credentials.get, send results with credentials: "same-origin", and redirect to mainpage on success - Patron page: add JS helper (passkey-register.inc) to request a registration challenge, include RS256 in pubKeyCredParams for compatibility, convert base64url to bytes, and submit attestation for storage - Modern JS throughout (const/let, arrow functions, JSDoc, loop-based toBase64 to avoid stack overflow on large buffers) - aria-label on passkey login button for accessibility - __x() for translatable strings with placeholders Tests: - t/db_dependent/api/v1/webauthn.t: verify challenge endpoints accept patron_id and userid; return 404 when no credentials; include a mocked registration negative path; follow Koha testing conventions - t/db_dependent/Koha/WebauthnCredentials.t: cover ORM add/search/update/ delete with per-subtest transaction isolation Test plan: 1) Apply patches 2) Run database updates to create webauthn_credentials (updatedatabase). 3) Set StaffClientBaseURL to your staff URL (e.g., http://<name>-intra.localhost when using ktd_proxy, haven't tested with unproxied ktd) and ensure the staff interface is served over the same origin. 4) As a staff user, open a patron record and click Register Passkey from More. Complete the OS-native passkey dialog. Verify a row is stored in webauthn_credentials. - This worked well in Zen (Firefox under the hood), less so with Chromium. - Unsure whether ungoogled Chromium supports using the system password manager, worked with a browser-based password manager, though. - Best to test with many browsers! 5) Navigate to the staff login page and choose Sign in with passkey. Verify a challenge is returned, the browser prompts, and you are logged into the staff interface (redirect to mainpage). 6) Call authenticate_challenge for a patron without credentials and verify a 404 response. 7) Run: - prove t/db_dependent/Koha/WebauthnCredentials.t - prove t/db_dependent/api/v1/webauthn.t 8) Sign off or review and FQA. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #19 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Created attachment 194698 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=194698&action=edit Bug 39601: [DO NOT PUSH] DBIC changes -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #20 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Thanks for another thorough review, Katrin. I've rewritten the entire patch set from scratch incorporating all your feedback plus additional improvements found during a full coding guidelines review. 1) QA test failures — All resolved in the rewrite: - Forbidden patterns (http:// URLs, DateTime->now, current_timestamp syntax), all fixed - POD coverage — added for all public subs - Tidiness — all files run through misc/devel/tidy.pl - Template filters — [% USE raw %] added where needed - File permissions — exec flags set on .t files and atomic update - Test::NoWarnings — added to WebauthnCredentials.t 2) Code review a) Spelling: Consistent "Register passkey" throughout. b) Translatability: Fixed. The space is now between </i> and <span>, not inside the translatable string: <i class="fa fa-key"></i> <span class="d-none d-sm-inline">Passkey</span> c) "Username:" in JS: Completely removed. Patron context now comes from hidden inputs populated by template variables. 3) Testing: "Registration error: {msg}" This was a JS i18n bug. I incorrectly used __().format() which only supports %s placeholders, but the strings had {name} placeholders. Changed to __x() which uses Koha.i18n.expand() for named placeholders. Error messages should now render correctly. 4) Answers to your questions a) Testing without hardware: - Chrome/Chromium: DevTools → More tools → WebAuthn. Create a virtual authenticator (check "Supports resident keys" and "Supports user verification"). This simulates a passkey without any hardware. - Firefox: Set security.webauth.webauthn_enable_softtoken = true in about:config. - Password managers like Bitwarden or 1Password can also act as passkey providers. b) "Call authenticate_challenge for a patron without credentials": This means: send POST /api/v1/webauthn/authenticate/challenge with a patron_id for a patron who has zero rows in webauthn_credentials, and verify you get HTTP 404 with {"error": "No credentials registered"}. The test suite already covers this (subtest 2 in webauthn.t). Additional improvements in this rewrite beyond QA fixes: - Extracted business logic to Koha::Auth::WebAuthn (thin controller pattern) - Added typed exceptions (Koha::Exceptions::WebAuthn) - Challenge TTL (10 min), single-use consumption, fail-closed patron matching - account_locked check before issuing session - Explicit credential pubkey lookup check before assertion validation - eval/$@ replaced with try/catch throughout - PK renamed to webauthn_credential_id (SQL7), column COMMENTs added (SQL11) - Date columns follow SQL14: created_date (timestamp), last_used_date (datetime) - COLLATE=utf8mb4_unicode_ci, ON UPDATE CASCADE - Table added to kohastructure.sql - Authenticate endpoints made public (no x-koha-authorization — they're login endpoints) - aria-label on passkey button for accessibility - Licence headers on test files -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Paul Derscheid <paul.derscheid@lmscloud.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #190989|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #21 from David Cook <dcook@prosentient.com.au> --- Hoping to look at this one more soon... -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Owen Leonard <oleonard@myacpl.org> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|Needs Signoff |Patch doesn't apply -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Paul Derscheid <paul.derscheid@lmscloud.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|Patch doesn't apply |Needs Signoff -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Paul Derscheid <paul.derscheid@lmscloud.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #194696|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Paul Derscheid <paul.derscheid@lmscloud.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #194697|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Paul Derscheid <paul.derscheid@lmscloud.de> changed: What |Removed |Added ---------------------------------------------------------------------------- Attachment #194698|0 |1 is obsolete| | -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #22 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Created attachment 199068 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=199068&action=edit Bug 39601: Atomic update Patch from commit 11f6cea -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #23 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Created attachment 199069 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=199069&action=edit Bug 39601: Add staff passkeys (WebAuthn) support Introduce staff passkey (WebAuthn) registration and authentication in Koha. Provides REST endpoints, persistence, UI hooks, and session integration so staff can register a passkey against a patron record and authenticate on the staff login screen using platform authenticators. Implementation: - New helper module Koha::Auth::WebAuthn encapsulating origin/RP ID derivation, challenge generation and validation, patron resolution, base64url encoding utilities, and session management - New controller Koha::REST::V1::Webauthn with endpoints: - POST /api/v1/webauthn/register/challenge - POST /api/v1/webauthn/register - POST /api/v1/webauthn/authenticate/challenge - POST /api/v1/webauthn/authenticate - New typed exception classes in Koha::Exceptions::WebAuthn - Use Authen::WebAuthn validate_registration/validate_assertion - Generate cryptographically secure challenges via Bytes::Random::Secure; store challenge, patron_id and timestamp in the session with 10-minute TTL and consume-after-use to prevent replay - Handle base64url consistently for WebAuthn fields; convert to/from standard base64 at the API boundary - Derive origin and rp_id from StaffClientBaseURL (or the request URL) to enforce correct WebAuthn origins; support reverse proxy headers and http->https origin upgrade - Persist credentials (credential_id, public_key as raw bytes) in the new webauthn_credentials table; update sign_count and last_used_date after successful authentication - Build allowCredentials from stored credential IDs for authentication challenges - On successful authentication, verify account is not locked, issue a staff session and set the CGISESSID cookie so the user is logged in to the staff interface Security: - Challenge TTL (10 min) and single-use consumption prevent replay attacks - Fail-closed patron match guard rejects if either patron ID is undefined - Explicit credential pubkey existence check before assertion validation - account_locked check before issuing session - $is_https derived from already-upgraded origin (not recomputed) API: - Add api/v1/swagger/paths/webauthn.yaml defining the WebAuthn endpoints, request/response schemas (including rp_id/rpId, allowCredentials) - Register endpoints require catalogue permission - Authenticate endpoints are public (no x-koha-authorization) since they are login endpoints DB: - Add webauthn_credentials table via installer/data/mysql/atomicupdate/ bug_39601_add_passkey_support.pl and kohastructure.sql - PK: webauthn_credential_id (per SQL7), COLLATE=utf8mb4_unicode_ci, column COMMENTs, ON UPDATE CASCADE, VARBINARY(1024) for credential_id - Date columns: created_date (timestamp), last_used_date (datetime) per SQL14 - Add Koha::WebauthnCredential(s) object classes UI: - Staff login: add JS helper (auth-webauthn.inc) to request a challenge, convert base64url to bytes, call navigator.credentials.get, send results with credentials: "same-origin", and redirect to mainpage on success - Patron page: add JS helper (passkey-register.inc) to request a registration challenge, include RS256 in pubKeyCredParams for compatibility, convert base64url to bytes, and submit attestation for storage - Modern JS throughout (const/let, arrow functions, JSDoc, loop-based toBase64 to avoid stack overflow on large buffers) - aria-label on passkey login button for accessibility - __x() for translatable strings with placeholders Tests: - t/db_dependent/api/v1/webauthn.t: verify challenge endpoints accept patron_id and userid; return 404 when no credentials; include a mocked registration negative path; follow Koha testing conventions - t/db_dependent/Koha/WebauthnCredentials.t: cover ORM add/search/update/ delete with per-subtest transaction isolation Test plan: 1) Apply patches 2) Run database updates to create webauthn_credentials (updatedatabase). 3) Set StaffClientBaseURL to your staff URL (e.g., http://<name>-intra.localhost when using ktd_proxy, haven't tested with unproxied ktd) and ensure the staff interface is served over the same origin. 4) As a staff user, open a patron record and click Register Passkey from More. Complete the OS-native passkey dialog. Verify a row is stored in webauthn_credentials. - This worked well in Zen (Firefox under the hood), less so with Chromium. - Unsure whether ungoogled Chromium supports using the system password manager, worked with a browser-based password manager, though. - Best to test with many browsers! 5) Navigate to the staff login page and choose Sign in with passkey. Verify a challenge is returned, the browser prompts, and you are logged into the staff interface (redirect to mainpage). 6) Call authenticate_challenge for a patron without credentials and verify a 404 response. 7) Run: - prove t/db_dependent/Koha/WebauthnCredentials.t - prove t/db_dependent/api/v1/webauthn.t 8) Sign off or review and FQA. -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #24 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Created attachment 199070 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=199070&action=edit Bug 39601: [DO NOT PUSH] DBIC changes Patch from commit b624525 -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 Jake Deery <jake.deery@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |jake.deery@openfifth.co.uk -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #25 from David Nind <david@davidnind.com> --- I attempted to test, but couldn't get things to work. Main issues: 1. Clicking on "Passkey" on the log in page: "Passkeys are not supported by this browser." 2. Can only see More > Register passkey for the koha patron - get this message when clicking on "Register passkey": "Registration error: Passkeys are not supported by this browser." May be something to do with staffClientBaseURL. I also think that the test plan needs updating to reflect all the comments, and to make sure things work as expected. Testing notes (using standard KTD (not proxied), Ubuntu 22.04 desktop, various browsers[1]): 1. I did this: - Applied patches - Installed required CPAN module: sudo apt-get install libauthen-webauthn-perl - Updated the database: updatedatabase - Rebuilt everything (not sure if this is required): yarn build - DBIC - not sure if it is needed, but ran dbic anyway: dbic - Restart everything: restart_all 2. Clicking on "Passkey" on the log in page: "Passkeys are not supported by this browser." 3. Can only see More > Register passkey for the koha patron - get this message when clicking on "Register passkey": "Registration error: Passkeys are not supported by this browser." [1] Browsers I attempted to test on =================================== 1. Firefox 152.04 2. LibreWolf (based on Firefox) 3. Google Chrome (150.0.7871.46) 4. Chromium (149.0.7827.196) KTD setup for domain name/URL ============================= 1. staffClientBaseURL: http://kohadev-intra.koha-testing:8081 2. KTD .env set up: KOHA_DOMAIN=.koha-testing KOHA_INSTANCE=kohadev KOHA_INTRANET_PORT=8081 KOHA_INTRANET_PREFIX= KOHA_INTRANET_SUFFIX=-intra 3. Local hosts file - /etc/hosts ... 127.0.0.1 koha-testing 127.0.0.1 kohadev-intra.koha-testing -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #26 from David Cook <dcook@prosentient.com.au> --- Sorry I haven't gotten to this yet. Mostly focused on bugs rather than enhancements atm :( -- You are receiving this mail because: You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=39601 --- Comment #27 from Paul Derscheid <paul.derscheid@lmscloud.de> --- Hi David, thanks for taking a look. It's troubling that it seems it did not work at all :/ -- You are receiving this mail because: You are watching all bug changes.
participants (1)
-
bugzilla-daemon@bugs.koha-community.org