Skip to content

Commit 307b2a9

Browse files
authored
Merge pull request #351 from TeskaLabs/fix/fido-mds-error-handling
Improve error handling in FIDO MDS
2 parents ed18f4f + b123126 commit 307b2a9

File tree

2 files changed

+36
-11
lines changed

2 files changed

+36
-11
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
## v24.06
44

55
### Pre-releases
6+
- `v24.06-alpha5`
67
- `v24.06-alpha4`
78
- `v24.06-alpha3`
89
- `v24.06-alpha2`
910
- `v24.06-alpha1`
1011

1112
### Fix
13+
- Improve error handling in FIDO MDS (#351, `v24.06-alpha5`)
1214
- Fix typo in last login endpoint path (#346, `v24.06-alpha4`)
1315
- Fix the initialization of NoTenantsError (#346, `v24.06-alpha2`)
1416

seacatauth/authn/webauthn/service.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,14 @@
2828

2929
asab.Config.add_defaults({
3030
"seacatauth:webauthn": {
31-
"attestation": "direct"
31+
"attestation": "direct",
32+
33+
# Authenticator metadata source file, for details see https://fidoalliance.org/metadata
34+
# Possible values:
35+
# - HTTPS or HTTP address of a JWT MDS file (defaults to "https://mds3.fidoalliance.org")
36+
# - Relative or absolute path of a JWT MDS file
37+
# - Empty value or "DISABLED" (disables the metadata service)
38+
"metadata_service_url": "https://mds3.fidoalliance.org",
3239
}
3340
})
3441

@@ -37,7 +44,6 @@ class WebAuthnService(asab.Service):
3744
WebAuthnCredentialCollection = "wa"
3845
WebAuthnRegistrationChallengeCollection = "warc"
3946
FidoMetadataServiceCollection = "fms"
40-
FidoMetadataServiceUrl = "https://mds3.fidoalliance.org"
4147

4248
def __init__(self, app, service_name="seacatauth.WebAuthnService"):
4349
super().__init__(app, service_name)
@@ -69,41 +75,54 @@ def __init__(self, app, service_name="seacatauth.WebAuthnService"):
6975
webauthn.helpers.structs.COSEAlgorithmIdentifier(-7) # Es256
7076
]
7177

78+
self.FidoMetadataServiceUrl = asab.Config.get("seacatauth:webauthn", "metadata_service_url")
79+
if self.FidoMetadataServiceUrl in ("", "DISABLED"):
80+
self.FidoMetadataServiceUrl = None
7281
self._FidoMetadataByAAGUID: typing.Dict[int, dict] | None = None
7382

7483
app.PubSub.subscribe("Application.housekeeping!", self._on_housekeeping)
7584

7685

7786
async def initialize(self, app):
78-
await self._load_fido_metadata()
87+
try:
88+
await self._load_fido_metadata()
89+
except Exception as e:
90+
L.exception("Failed to fetch FIDO Alliance Metadata ({}: {})".format(e.__class__.__name__, e))
7991

8092

8193
async def _on_housekeeping(self, event_name):
8294
await self._delete_expired_challenges()
83-
await self._load_fido_metadata(only_if_empty=False)
95+
try:
96+
await self._load_fido_metadata(force_reload=True)
97+
except Exception as e:
98+
L.info("Failed to fetch FIDO Alliance Metadata: {}".format(e))
8499

85100

86-
async def _load_fido_metadata(self, only_if_empty=True):
101+
async def _load_fido_metadata(self, *, force_reload: bool = False):
87102
"""
88103
Download and decode FIDO metadata from FIDO Alliance Metadata Service (MDS) and prepare a lookup dictionary.
89104
"""
90-
if only_if_empty:
105+
if not self.FidoMetadataServiceUrl:
106+
return
107+
108+
if not force_reload:
91109
coll = await self.StorageService.collection(self.FidoMetadataServiceCollection)
92110
count = await coll.estimated_document_count()
93111
if count > 0:
94112
return
95113

96-
try:
114+
if self.FidoMetadataServiceUrl.startswith("https://") or self.FidoMetadataServiceUrl.startswith("http://"):
97115
async with aiohttp.ClientSession() as session:
98116
async with session.get(self.FidoMetadataServiceUrl) as resp:
99117
if resp.status != 200:
100118
text = await resp.text()
101119
L.error("Failed to fetch FIDO metadata:\n{}".format(text[:1000]))
102120
return
103121
jwt = await resp.text()
104-
except ConnectionError:
105-
L.error("Cannot connect to FIDO Alliance Metadata Service.")
106-
return
122+
else:
123+
# Load from local file
124+
with open(self.FidoMetadataServiceUrl) as f:
125+
jwt = f.read()
107126

108127
jwt = jwcrypto.jwt.JWT(jwt=jwt)
109128
cert_chain = jwt.token.jose_header.get("x5c", [])
@@ -179,7 +198,11 @@ async def create_webauthn_credential(
179198
L.log(asab.LOG_NOTICE, "WebAuthn credential created", struct_data={"wacid": wacid.hex()})
180199

181200
async def _get_authenticator_metadata(self, verified_registration):
182-
await self._load_fido_metadata()
201+
try:
202+
await self._load_fido_metadata()
203+
except Exception as e:
204+
L.info("Failed to fetch FIDO Alliance Metadata: {}".format(e))
205+
183206
aaguid = bytes.fromhex(verified_registration.aaguid.replace("-", ""))
184207
if aaguid == 0:
185208
# Authenticators with other identifiers than AAGUID are not supported

0 commit comments

Comments
 (0)