diff --git a/webauthn_demo.py b/webauthn_demo.py
index e20f3e02745ea7b7904f7e80ffe139e9044ee8d7..0672451b911cede1de3ed0429f41bd755931509d 100755
--- a/webauthn_demo.py
+++ b/webauthn_demo.py
@@ -14,6 +14,7 @@ from webauthn import (
 from webauthn.helpers.structs import AuthenticationCredential, RegistrationCredential
 
 HOSTNAME="diseasedmind.com"
+REQUIRE_USER_PRESENCE=False  # False to support Yubikeys, no PIN required
 
 # Our database is just this global dictionary so everything is forgotton
 # when the server restarts.
@@ -118,11 +119,12 @@ def POST_register(response):
     else:
         user_name, user_id, opts = database["reg_requests"][challenge]
         try:
-            verified_reg = verify_reg(opts, dumps(response), user_name)
-            if not verified_reg.user_verified:
-                raise Exception("Registration signature not valid")
+            verified_reg = verify_reg(opts, dumps(response), user_name,
+                                      REQUIRE_USER_PRESENCE)
+            if REQUIRE_USER_PRESENCE and not verified_reg.user_verified:
+                raise Exception("User presence required but not attested")
         except Exception as e:
-            print("Exception: %s" % e)
+            print("Exception (%s): %s" % (type(e), e))
             status = "400 Bad Request"
             content = b"Error %s" % str(e).encode()
         
@@ -155,7 +157,7 @@ def generate_registration(hostname, user_id, user_name, provider_id=None):
     return generate_registration_options(rp_id=hostname, rp_name=provider_id,
                                          user_id=user_id, user_name=user_name)
 
-def verify_reg(opts, response, user_name):
+def verify_reg(opts, response, user_name, user_verification_required):
     """
     Verifies the response from the browser.  If the user was verified, their
     credential_id, credential_type, and credential_public_key should all be
@@ -178,6 +180,10 @@ def verify_reg(opts, response, user_name):
     :param user_name: A user-friendly, readable name for the Relying Party.
                       This is shown to the user on their hardware device.
     :type user_name: string
+    :param user_verification_required: Flag controlling whether the user must
+                                       be verified (e.g. enter a PIN). This must
+                                       be False to support Yubikey.
+    :type user_verification_required: boolean
     :returns: Information about a verified attestation of which an RP can make use.
     :rtype: `py:VerifiedRegistration`
     """
@@ -192,9 +198,12 @@ def verify_reg(opts, response, user_name):
         expected_challenge=base64url_to_bytes(urlsafe_b64encode(expected_challenge).decode()),
         expected_origin="https://"+opts.rp.id,
         expected_rp_id=opts.rp.id,
-        require_user_verification=True
+        require_user_verification=user_verification_required
     )
-    if verified_reg.user_verified:
+
+    # If user verification is not required, or it is and we have it, register
+    # the user's device
+    if not user_verification_required or verified_reg.user_verified:
         register_device(verified_reg, opts, user_name)
     return verified_reg
 
@@ -280,7 +289,8 @@ def POST_auth(response):
     else:
         user_name, opts = database["auth_requests"][challenge]
         try:
-            verified_auth = verify_auth_response(opts, dumps(response), user_name)
+            verified_auth = verify_auth_response(opts, dumps(response),
+                                user_name, REQUIRE_USER_PRESENCE)
         except Exception as e:
             print("Exception: %s" % e)
             status = "400 Bad Request"
@@ -305,7 +315,7 @@ def generate_authentication(credentials, hostname):
     return generate_authentication_options(rp_id=hostname,
                                            allow_credentials=credentials)
 
-def verify_auth_response(opts, response, user_name):
+def verify_auth_response(opts, response, user_name, user_verification_required):
     """
     Verifies the authentication response is valid.  Raises an exception if the
     key used to sign the challenge was not in the list of allowed keys for this
@@ -323,6 +333,10 @@ def verify_auth_response(opts, response, user_name):
     :param user_name: A user-friendly, readable name for the Relying Party.
                       This is shown to the user on their hardware device.
     :type user_name: string
+    :param user_verification_required: Flag controlling whether the user must
+                                       be verified (e.g. enter a PIN). This must
+                                       be False to support Yubikey.
+    :type user_verification_required: boolean
     :returns: Verified authentication object
     :rtype: `py:VerifiedAuthentication`
     """
@@ -356,7 +370,7 @@ def verify_auth_response(opts, response, user_name):
         expected_origin="https://"+opts.rp_id,
         credential_public_key=pubkey,
         credential_current_sign_count=0,
-        require_user_verification=True,
+        require_user_verification=user_verification_required,
     )