[#8356] multifactor fixes
diff --git a/Allura/allura/lib/multifactor.py b/Allura/allura/lib/multifactor.py
index ead22f3..c96cf61 100644
--- a/Allura/allura/lib/multifactor.py
+++ b/Allura/allura/lib/multifactor.py
@@ -108,7 +108,7 @@
 
     def verify(self, totp, code, user):
         code = code.replace(' ', '')  # Google authenticator puts a space in their codes
-        code = bytes(code)  # can't be unicode
+        code = six.ensure_binary(code)  # can't be text
 
         self.enforce_rate_limit(user)
 
@@ -233,7 +233,7 @@
 
     def dump(self):
         lines = []
-        lines.append(b32encode(self.key).replace('=', ''))
+        lines.append(six.ensure_text(b32encode(self.key)).replace('=', ''))
         for opt, value in six.iteritems(self.options):
             parts = ['"', opt]
             if value is not None:
diff --git a/Allura/allura/model/multifactor.py b/Allura/allura/model/multifactor.py
index 1486a8e..0d232b9 100644
--- a/Allura/allura/model/multifactor.py
+++ b/Allura/allura/model/multifactor.py
@@ -40,7 +40,7 @@
 
     _id = FieldProperty(S.ObjectId)
     user_id = FieldProperty(S.ObjectId, required=True)
-    key = FieldProperty(str, required=True)
+    key = FieldProperty(S.Binary, required=True)  # S.Binary ok?  ming 0.5.x make_safe doesn't know about bytes/Binary
 
 
 class RecoveryCode(MappedClass):
diff --git a/Allura/allura/tests/test_multifactor.py b/Allura/allura/tests/test_multifactor.py
index 9644352..e5e7834 100644
--- a/Allura/allura/tests/test_multifactor.py
+++ b/Allura/allura/tests/test_multifactor.py
@@ -98,25 +98,25 @@
         srv = GenericTotpService()
         totp = srv.Totp(key=self.sample_key)
         srv.verify(totp, '283 397', None)
-        srv.verify(totp, b'283397', None)
+        srv.verify(totp, '283397', None)
 
     @patch('allura.lib.multifactor.time')
     def test_verify_window(self, time):
         time.return_value = self.sample_time
         srv = GenericTotpService()
         totp = srv.Totp(key=self.sample_key)
-        srv.verify(totp, b'283397', None)
+        srv.verify(totp, '283397', None)
 
         time.return_value = self.sample_time + 30
-        srv.verify(totp, b'283397', None)
+        srv.verify(totp, '283397', None)
 
         time.return_value = self.sample_time + 60
         with assert_raises(InvalidToken):
-            srv.verify(totp, b'283397', None)
+            srv.verify(totp, '283397', None)
 
         time.return_value = self.sample_time - 30
         with assert_raises(InvalidToken):
-            srv.verify(totp, b'283397', None)
+            srv.verify(totp, '283397', None)
 
     def test_get_qr_code(self):
         srv = TotpService()
@@ -165,12 +165,12 @@
 
         # 4th attempt (good or bad) will trip over the default limit of 3 in 30s
         with assert_raises(InvalidToken):
-            srv.verify(totp, b'34dfvdasf', user)
+            srv.verify(totp, '34dfvdasf', user)
         with assert_raises(InvalidToken):
-            srv.verify(totp, b'234asdfsadf', user)
-        srv.verify(totp, b'283397', user)
+            srv.verify(totp, '234asdfsadf', user)
+        srv.verify(totp, '283397', user)
         with assert_raises(MultifactorRateLimitError):
-            srv.verify(totp, b'283397', user)
+            srv.verify(totp, '283397', user)
 
 
 class TestMongodbTotpService(TestAnyTotpServiceImplementation):