Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Timeout functionality added to server #222

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/credential.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def request_uv(self, permissions, rd_id):
print("Authenticator supports User Verification")


server = Fido2Server({"id": "example.com", "name": "Example RP"}, attestation="direct")
server = Fido2Server({"id": "example.com", "name": "Example RP"}, attestation="direct", timeout=3600)

user = {"id": b"user_id", "name": "A. User"}

Expand Down
29 changes: 28 additions & 1 deletion fido2/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
from dataclasses import replace
from urllib.parse import urlparse
from typing import Sequence, Mapping, Optional, Callable, Union, Tuple, Any, overload
from time import time

import os
import logging
Expand Down Expand Up @@ -123,12 +124,17 @@ def _ignore_attestation(
) -> None:
"""Ignore attestation."""

def _get_timestamp() -> int:
"""Returns time current timestamp in seconds in UTC."""
return int(time())


class Fido2Server:
"""FIDO2 server.

:param rp: Relying party data as `PublicKeyCredentialRpEntity` instance.
:param attestation: (optional) Requirement on authenticator attestation.
:param timeout: (optional) Timeout for credential create and authenticate operations in seconds as 'int' value.
:param verify_origin: (optional) Alternative function to validate an origin.
:param verify_attestation: (optional) function to validate attestation, which is
invoked with attestation_object and client_data_hash. It should return nothing
Expand All @@ -140,12 +146,13 @@ def __init__(
self,
rp: PublicKeyCredentialRpEntity,
attestation: Optional[AttestationConveyancePreference] = None,
timeout: Optional[int] = None,
verify_origin: Optional[VerifyOrigin] = None,
verify_attestation: Optional[VerifyAttestation] = None,
):
self.rp = PublicKeyCredentialRpEntity.from_dict(rp)
self._verify = verify_origin or _verify_origin_for_rp(self.rp.id)
self.timeout = None
self.timeout = timeout
self.attestation = AttestationConveyancePreference(attestation)
self.allowed_algorithms = [
PublicKeyCredentialParameters(PublicKeyCredentialType.PUBLIC_KEY, alg)
Expand Down Expand Up @@ -290,6 +297,15 @@ def register_complete(self, state, *args, **kwargs):
"User verification required, but User Verified flag not set."
)

if (
self.timeout
and _get_timestamp()-state["timestamp"] > self.timeout
):
raise ValueError(
f"Request timed out. Timeout was set for {str(self.timeout)} seconds"
)


if self.attestation not in (None, AttestationConveyancePreference.NONE):
logger.debug(f"Verifying attestation of type {attestation_object.fmt}")
self._verify_attestation(attestation_object, client_data.hash)
Expand Down Expand Up @@ -422,6 +438,16 @@ def authenticate_complete(self, state, credentials, *args, **kwargs):
"User verification required, but user verified flag not set."
)

if (
self.timeout
and _get_timestamp()-state["timestamp"] > self.timeout
):
raise ValueError(
f"Request timed out. Timeout was set for {str(self.timeout)} seconds"
)



for cred in credentials:
if cred.credential_id == credential_id:
try:
Expand All @@ -439,6 +465,7 @@ def _make_internal_state(
return {
"challenge": websafe_encode(challenge),
"user_verification": user_verification,
"timestamp": _get_timestamp(),
}


Expand Down