Files
notifications-admin/app/models/webauthn_credential.py
T

77 lines
2.2 KiB
Python
Raw Normal View History

2021-05-07 18:10:07 +01:00
import base64
from fido2 import cbor
from fido2.client import ClientData
2021-05-14 09:17:12 +01:00
from fido2.cose import UnsupportedKey
2021-05-07 18:10:07 +01:00
from fido2.ctap2 import AttestationObject, AttestedCredentialData
from flask import current_app
from app.models import JSONModel, ModelList
from app.notify_client.user_api_client import user_api_client
2021-05-14 09:17:12 +01:00
class RegistrationError(Exception):
pass
class WebAuthnCredential(JSONModel):
ALLOWED_PROPERTIES = {
'id',
'name',
2021-05-07 18:10:07 +01:00
'credential_data', # contains public key and credential ID for auth
'registration_response', # sent to API for later auditing (not used)
'created_at',
'updated_at'
}
2021-05-07 18:10:07 +01:00
@classmethod
def from_registration(cls, state, response):
server = current_app.webauthn_server
2021-05-14 09:17:12 +01:00
try:
auth_data = server.register_complete(
state,
ClientData(response["clientDataJSON"]),
AttestationObject(response["attestationObject"]),
)
except ValueError as e:
raise RegistrationError(e)
if isinstance(auth_data.credential_data.public_key, UnsupportedKey):
raise RegistrationError("Encryption algorithm not supported")
2021-05-07 18:10:07 +01:00
return cls({
'name': 'Unnamed key',
'credential_data': base64.b64encode(
cbor.encode(auth_data.credential_data),
).decode('utf-8'),
2021-05-07 18:10:07 +01:00
'registration_response': base64.b64encode(
cbor.encode(response),
).decode('utf-8')
2021-05-07 18:10:07 +01:00
})
def to_credential_data(self):
return AttestedCredentialData(
cbor.decode(base64.b64decode(self.credential_data.encode()))
2021-05-07 18:10:07 +01:00
)
def serialize(self):
return {
'name': self.name,
'credential_data': self.credential_data,
'registration_response': self.registration_response,
}
class WebAuthnCredentials(ModelList):
model = WebAuthnCredential
client_method = user_api_client.get_webauthn_credentials_for_user
@property
def as_cbor(self):
return [credential.to_credential_data() for credential in self]
def by_id(self, key_id):
return next((key for key in self if key.id == key_id), None)