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

Improve error messages when cryptography isn't installed #846

Merged
merged 3 commits into from
Jan 26, 2023
Merged
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
15 changes: 10 additions & 5 deletions jwt/api_jwk.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import time
from typing import Any, Optional

from .algorithms import get_default_algorithms
from .algorithms import get_default_algorithms, has_crypto, requires_cryptography
from .exceptions import InvalidKeyError, PyJWKError, PyJWKSetError
from .types import JWKDict

Expand Down Expand Up @@ -49,10 +49,13 @@ def __init__(self, jwk_data: JWKDict, algorithm: Optional[str] = None) -> None:
else:
raise InvalidKeyError(f"Unsupported kty: {kty}")

if not has_crypto and algorithm in requires_cryptography:
raise PyJWKError(f"{algorithm} requires 'cryptography' to be installed.")

self.Algorithm = self._algorithms.get(algorithm)

if not self.Algorithm:
raise PyJWKError(f"Unable to find a algorithm for key: {self._jwk_data}")
raise PyJWKError(f"Unable to find an algorithm for key: {self._jwk_data}")

self.key = self.Algorithm.from_jwk(self._jwk_data)

Expand All @@ -66,11 +69,11 @@ def from_json(data: str, algorithm: None = None) -> "PyJWK":
return PyJWK.from_dict(obj, algorithm)

@property
def key_type(self) -> str:
def key_type(self) -> Optional[str]:
return self._jwk_data.get("kty", None)

@property
def key_id(self) -> str:
def key_id(self) -> Optional[str]:
return self._jwk_data.get("kid", None)

@property
Expand All @@ -96,7 +99,9 @@ def __init__(self, keys: list[JWKDict]) -> None:
continue

if len(self.keys) == 0:
raise PyJWKSetError("The JWK Set did not contain any usable keys")
raise PyJWKSetError(
"The JWK Set did not contain any usable keys. Perhaps 'cryptography' is not installed?"
)

@staticmethod
def from_dict(obj: dict[str, Any]) -> "PyJWKSet":
Expand Down
8 changes: 7 additions & 1 deletion tests/test_api_jwk.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from jwt.api_jwk import PyJWK, PyJWKSet
from jwt.exceptions import InvalidKeyError, PyJWKError, PyJWKSetError

from .utils import crypto_required, key_path
from .utils import crypto_required, key_path, no_crypto_required

if has_crypto:
from jwt.algorithms import ECAlgorithm, HMACAlgorithm, OKPAlgorithm, RSAAlgorithm
Expand Down Expand Up @@ -207,6 +207,12 @@ def test_from_dict_should_throw_exception_if_arg_is_invalid(self):
with pytest.raises(InvalidKeyError):
PyJWK.from_dict(v)

@no_crypto_required
def test_missing_crypto_library_good_error_message(self):
with pytest.raises(PyJWKError) as exc:
PyJWK({"kty": "dummy"}, algorithm="RS256")
assert "cryptography" in str(exc.value)


@crypto_required
class TestPyJWKSet:
Expand Down