diff --git a/dev/packages.nix b/dev/packages.nix
index 69d8043..dcb99fb 100644
--- a/dev/packages.nix
+++ b/dev/packages.nix
@@ -10,6 +10,19 @@
     src = inputs.hydra;
     buildInputs = o.buildInputs ++ [ final.perlPackages.DBIxClassHelpers ];
   });
+  sk-libfido2 = prev.openssh.overrideAttrs (o: {
+    pname = "sk-libfido2";
+    # rebase of https://github.com/openssh/openssh-portable/commit/ca0697a90e5720ba4d76cb0ae9d5572b5260a16c
+    patches = o.patches ++ [ ./sk-libfido2.patch ];
+    configureFlags = o.configureFlags ++ [ "--with-security-key-standalone" ];
+    buildFlags = [
+      "PATHS="
+      "SK_STANDALONE=sk-libfido2.dylib"
+    ];
+    installPhase = "install sk-libfido2.dylib -Dt $out";
+    postInstall = null;
+    doCheck = false;
+  });
   sotp = final.buildGoModule rec {
     pname = "sotp";
     version = "e7f7c804b1641169ce850d8352fb07294881609e";
diff --git a/dev/sk-libfido2.patch b/dev/sk-libfido2.patch
new file mode 100644
index 0000000..4979dcc
--- /dev/null
+++ b/dev/sk-libfido2.patch
@@ -0,0 +1,180 @@
+commit 23d3b46546f5f6d74a2a99f4c902e52206822f96
+Author: Jeremy Stott <jeremy@stott.co.nz>
+Date:   Sat Oct 19 12:10:52 2024 +1300
+
+    Add make target for standalone sk-libfido2
+    
+    Add a Makefile target for sk-libfido2, the standalone fido2 security
+    key shared library, suitable for use with the SecurityKeyProvider
+    option.
+    
+    Add a new configure option `--with-security-key-standalone` that
+    optionally sets the shared library target sk-libfido2$(SHLIBEXT), and
+    adds it to $(TARGETS).
+    
+    misc.h is required when SK_STANDALONE is defined, because of the use
+    of `monotime_tv` in `sk_select_by_touch`.
+    
+    Sets the shared library extension for sk-libfido2 is by setting
+    `SHLIBEXT` depending on the platform in configure.ac.
+    
+    Add the shared library to the CI builds in the `sk` target config to
+    make sure it can compile under the same conditions as
+    `--with-security-key-builtin`.
+    
+    Add a libssh-pic.a static library that compiles with `-fPIC` reusing
+    .c.lo method in sk-dummy.so for use in the shared library sk-libfido2.
+    
+    Note, a separate static library libssh-pic.a is needed, since defining
+    -DSK_STANDALONE excludes some symbols needed in sshkey.lo.
+
+diff --git a/.github/configs b/.github/configs
+index 4f47f820b..da6d46d86 100755
+--- a/.github/configs
++++ b/.github/configs
+@@ -181,7 +181,7 @@ case "$config" in
+ 	CONFIGFLAGS="--with-selinux"
+ 	;;
+     sk)
+-	CONFIGFLAGS="--with-security-key-builtin"
++	CONFIGFLAGS="--with-security-key-builtin --with-security-key-standalone"
+         ;;
+     without-openssl)
+ 	LIBCRYPTOFLAGS="--without-openssl"
+diff --git a/.gitignore b/.gitignore
+index 41d505c46..333c7cd3c 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -12,6 +12,8 @@ survey.sh
+ **/*.o
+ **/*.lo
+ **/*.so
++**/*.dylib
++**/*.dll
+ **/*.out
+ **/*.a
+ **/*.un~
+diff --git a/Makefile.in b/Makefile.in
+index 4243006b0..9450e9991 100644
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -32,6 +32,7 @@ SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@
+ STRIP_OPT=@STRIP_OPT@
+ TEST_SHELL=@TEST_SHELL@
+ BUILDDIR=@abs_top_builddir@
++SK_STANDALONE=@SK_STANDALONE@
+ 
+ PATHS= -DSSHDIR=\"$(sysconfdir)\" \
+ 	-D_PATH_SSH_PROGRAM=\"$(SSH_PROGRAM)\" \
+@@ -71,7 +72,7 @@ MKDIR_P=@MKDIR_P@
+ 
+ .SUFFIXES: .lo
+ 
+-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) sshd-session$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-sk-helper$(EXEEXT)
++TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) sshd-session$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-sk-helper$(EXEEXT) $(SK_STANDALONE)
+ 
+ XMSS_OBJS=\
+ 	ssh-xmss.o \
+@@ -254,6 +255,16 @@ sftp$(EXEEXT): $(LIBCOMPAT) libssh.a $(SFTP_OBJS)
+ logintest: logintest.o $(LIBCOMPAT) libssh.a loginrec.o
+ 	$(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh $(LIBS)
+ 
++# compile libssh objects with -fPIC for use in the sk_libfido2 shared library
++LIBSSH_PIC_OBJS=$(LIBSSH_OBJS:.o=.lo)
++libssh-pic.a: $(LIBSSH_PIC_OBJS)
++	$(AR) rv $@ $(LIBSSH_PIC_OBJS)
++	$(RANLIB) $@
++
++$(SK_STANDALONE): sk-usbhid.c $(LIBCOMPAT) libssh-pic.a
++	$(CC) -o $@ -shared $(CFLAGS_NOPIE) $(CPPFLAGS) -DSK_STANDALONE $(PICFLAG) sk-usbhid.c \
++	libssh-pic.a $(LDFLAGS_NOPIE) -lopenbsd-compat $(LIBS) $(LIBFIDO2) $(CHANNELLIBS)
++
+ $(MANPAGES): $(MANPAGES_IN)
+ 	if test "$(MANTYPE)" = "cat"; then \
+ 		manpage=$(srcdir)/`echo $@ | sed 's/\.[1-9]\.out$$/\.0/'`; \
+@@ -313,7 +324,7 @@ distclean:	regressclean
+ 	rm -f *.o *.a $(TARGETS) logintest config.cache config.log
+ 	rm -f *.out core opensshd.init openssh.xml
+ 	rm -f Makefile buildpkg.sh config.h config.status
+-	rm -f survey.sh openbsd-compat/regress/Makefile *~ 
++	rm -f survey.sh openbsd-compat/regress/Makefile *~
+ 	rm -rf autom4te.cache
+ 	rm -f regress/check-perm
+ 	rm -f regress/mkdtemp
+diff --git a/configure.ac b/configure.ac
+index 9053a9a2b..1e8432bd6 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -619,6 +619,9 @@ SPP_MSG="no"
+ # the --with-solaris-privs option and --with-sandbox=solaris).
+ SOLARIS_PRIVS="no"
+ 
++# Default shared library extension
++SHLIBEXT=".so"
++
+ # Check for some target-specific stuff
+ case "$host" in
+ *-*-aix*)
+@@ -737,6 +740,7 @@ case "$host" in
+ 	# Cygwin defines optargs, optargs as declspec(dllimport) for historical
+ 	# reasons which cause compile warnings, so we disable those warnings.
+ 	OSSH_CHECK_CFLAG_COMPILE([-Wno-attributes])
++	SHLIBEXT=".dll"
+ 	;;
+ *-*-dgux*)
+ 	AC_DEFINE([IP_TOS_IS_BROKEN], [1],
+@@ -796,6 +800,7 @@ int main(void) { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
+ 	# cf. Apple bug 3710161 (not public, but searchable)
+ 	AC_DEFINE([BROKEN_POLL], [1],
+ 	    [System poll(2) implementation is broken])
++	SHLIBEXT=".dylib"
+ 	;;
+ *-*-dragonfly*)
+ 	SSHDLIBS="$SSHDLIBS"
+@@ -2084,6 +2089,12 @@ AC_ARG_WITH([security-key-builtin],
+ 	[ enable_sk_internal=$withval ]
+ )
+ 
++enable_sk_standalone=
++AC_ARG_WITH([security-key-standalone],
++	[  --with-security-key-standalone build standalone sk-libfido2 SecurityKeyProvider],
++	[ enable_sk_standalone=$withval ]
++)
++
+ enable_dsa=
+ AC_ARG_ENABLE([dsa-keys],
+ 	[  --enable-dsa-keys       enable DSA key support [no]],
+@@ -3321,6 +3332,16 @@ if test "x$enable_sk" = "xyes" -a "x$enable_sk_internal" != "xno" ; then
+ 	fi
+ fi
+ 
++# Check for standalone SecurityKeyProvider
++AC_MSG_CHECKING([whether to build standlone sk-libfido2])
++if test "x$enable_sk_standalone" = "xyes" ; then
++	AC_MSG_RESULT([yes])
++	AC_SUBST([SK_STANDALONE], [sk-libfido2$SHLIBEXT])
++else
++	AC_MSG_RESULT([no])
++	AC_SUBST([SK_STANDALONE], [""])
++fi
++
+ AC_CHECK_FUNCS([ \
+ 	arc4random \
+ 	arc4random_buf \
+diff --git a/sk-usbhid.c b/sk-usbhid.c
+index 812b28d83..36f089a57 100644
+--- a/sk-usbhid.c
++++ b/sk-usbhid.c
+@@ -77,10 +77,11 @@
+ #define FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID 0
+ #endif
+ 
++# include "misc.h"
++
+ #ifndef SK_STANDALONE
+ # include "log.h"
+ # include "xmalloc.h"
+-# include "misc.h"
+ /*
+  * If building as part of OpenSSH, then rename exported functions.
+  * This must be done before including sk-api.h.
diff --git a/modules/darwin/community-builder/default.nix b/modules/darwin/community-builder/default.nix
index 394a5f4..a964824 100644
--- a/modules/darwin/community-builder/default.nix
+++ b/modules/darwin/community-builder/default.nix
@@ -11,6 +11,10 @@
     ./users.nix
   ];
 
+  environment.etc."ssh/sshd_config.d/security-key.conf".text = ''
+    SecurityKeyProvider ${pkgs.sk-libfido2}/sk-libfido2.dylib
+  '';
+
   environment.etc.motd.text = config.nixCommunity.motd;
 
   environment.systemPackages = [