diff --git a/src/appier_extras/parts/admin/templates/fluid/fido2.html.tpl b/src/appier_extras/parts/admin/templates/fluid/fido2.html.tpl
new file mode 100644
index 00000000..61089854
--- /dev/null
+++ b/src/appier_extras/parts/admin/templates/fluid/fido2.html.tpl
@@ -0,0 +1,21 @@
+{% extends "admin/admin.simple.html.tpl" %}
+{% block title %}FIDO2{% endblock %}
+{% block body_style %}{{ super() }} {% if background %}background:url({{ background }});{% endif %}{% endblock %}
+{% block content %}
+
+ {% if owner.logo_url %}
+
+ {% else %}
+
Security Key
+ {% endif %}
+
Logging in using Security Key
+ {% if error %}
+
{{ error }}
+ {% endif %}
+
+
+{% endblock %}
diff --git a/src/appier_extras/parts/admin/templates/fluid/fido2_register.html.tpl b/src/appier_extras/parts/admin/templates/fluid/fido2_register.html.tpl
new file mode 100644
index 00000000..48c9d908
--- /dev/null
+++ b/src/appier_extras/parts/admin/templates/fluid/fido2_register.html.tpl
@@ -0,0 +1,21 @@
+{% extends "admin/admin.simple.html.tpl" %}
+{% block title %}FIDO2 Register{% endblock %}
+{% block body_style %}{{ super() }} {% if background %}background:url({{ background }});{% endif %}{% endblock %}
+{% block content %}
+
+ {% if owner.logo_url %}
+
+ {% else %}
+
Security Key
+ {% endif %}
+
Registering Security Key
+ {% if error %}
+
{{ error }}
+ {% endif %}
+
+
+{% endblock %}
diff --git a/src/appier_extras/utils/__init__.py b/src/appier_extras/utils/__init__.py
index 4ab3d03e..2895a064 100644
--- a/src/appier_extras/utils/__init__.py
+++ b/src/appier_extras/utils/__init__.py
@@ -29,6 +29,7 @@
from . import image
from . import markdown
from . import net
+from . import serialization
from .format import SafeFormatter
from .image import resize_image
@@ -40,3 +41,4 @@
has_regex,
)
from .net import size_round_unit
+from .serialization import BytesEncoder, bytes_decoder
diff --git a/src/appier_extras/utils/serialization.py b/src/appier_extras/utils/serialization.py
new file mode 100644
index 00000000..1a3cff2d
--- /dev/null
+++ b/src/appier_extras/utils/serialization.py
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Hive Appier Framework
+# Copyright (c) 2008-2024 Hive Solutions Lda.
+#
+# This file is part of Hive Appier Framework.
+#
+# Hive Appier Framework is free software: you can redistribute it and/or modify
+# it under the terms of the Apache License as published by the Apache
+# Foundation, either version 2.0 of the License, or (at your option) any
+# later version.
+#
+# Hive Appier Framework is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# Apache License for more details.
+#
+# You should have received a copy of the Apache License along with
+# Hive Appier Framework. If not, see
.
+
+__author__ = "João Magalhães
"
+""" The author(s) of the module """
+
+__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda."
+""" The copyright for the module """
+
+__license__ = "Apache License, Version 2.0"
+""" The license for the module """
+
+import json
+import base64
+
+import appier
+
+
+class BytesEncoder(json.JSONEncoder):
+ """
+ Custom JSON encoder that makes sure that bytes
+ are properly encoded as Base64 strings.
+ """
+
+ def default(self, obj):
+ if isinstance(obj, appier.legacy.BYTES):
+ return base64.b64encode(obj).decode("utf-8")
+ return json.JSONEncoder.default(self, obj)
+
+
+def bytes_decoder(map):
+ for key, value in map.items():
+ if isinstance(value, str):
+ try:
+ decoded_value = base64.b64decode(value)
+ map[key] = decoded_value
+ except (ValueError, TypeError):
+ pass
+ return map