From 19b09cab2dbdcfeb6db385d505738499beff8d8f Mon Sep 17 00:00:00 2001 From: Chris Tran Date: Mon, 6 Jan 2025 11:27:40 -0600 Subject: [PATCH] feat!: remove deprecated PassageError ctor (#131) --- passageidentity/auth.py | 35 +++++++++++++++++++++-------------- passageidentity/errors.py | 34 +++++++++------------------------- passageidentity/user.py | 6 ++++-- tests/errors_test.py | 24 ------------------------ 4 files changed, 34 insertions(+), 65 deletions(-) diff --git a/passageidentity/auth.py b/passageidentity/auth.py index e1f81ab..365fb8b 100644 --- a/passageidentity/auth.py +++ b/passageidentity/auth.py @@ -40,20 +40,27 @@ def validate_jwt(self, jwt: str) -> str: msg = "jwt is required." raise ValueError(msg) - try: - kid = pyjwt.get_unverified_header(jwt)["kid"] - public_key = self.jwks.get_signing_key(kid) - claims = pyjwt.decode( - jwt, - public_key, - audience=self.app_id, - algorithms=["RS256"], - ) - - return claims["sub"] - except Exception as e: - msg = f"JWT is not valid: {e}" - raise PassageError(msg) from e + header = pyjwt.get_unverified_header(jwt) + kid = header.get("kid") + + if kid is None: + msg = "kid is missing in the JWT header." + raise ValueError(msg) + + public_key = self.jwks.get_signing_key(kid) + claims = pyjwt.decode( + jwt, + public_key, + audience=self.app_id, + algorithms=["RS256"], + ) + + sub = claims.get("sub") + if sub is None: + msg = "sub is missing in the JWT claims." + raise ValueError(msg) + + return sub def create_magic_link(self, args: MagicLinkArgs, options: MagicLinkOptions | None = None) -> MagicLink: """Create a Magic Link for your app.""" diff --git a/passageidentity/errors.py b/passageidentity/errors.py index 7c959e2..217d5df 100644 --- a/passageidentity/errors.py +++ b/passageidentity/errors.py @@ -4,8 +4,6 @@ from typing import TYPE_CHECKING -import typing_extensions - if TYPE_CHECKING: from passageidentity.openapi_client.exceptions import ApiException @@ -13,28 +11,9 @@ class PassageError(Exception): """Error class for handling Passage errors.""" - @typing_extensions.deprecated( - "This should only be constructed by the Passage SDK. Use this type just for type checking.", - ) - def __init__( - self, - message: str, - status_code: int | None = None, - status_text: str | None = None, - body: dict | None = None, - error_code: str | None = None, - ) -> None: - """Initialize the error with a message, status code, status text, and optional body.""" - self.message = message - self.status_code = status_code - self.status_text = status_text - - self.error_code = error_code - self.error = None - - if body is not None: - self.error = body["error"] - self.error_code = body["code"] + message: str + status_code: int | None + error_code: str | None def __str__(self) -> str: """Return the error message.""" @@ -52,4 +31,9 @@ def from_response_error(cls, response_error: ApiException, message: str | None = error_code = None msg = str(response_error.body) - return cls(message=msg, status_code=response_error.status, error_code=error_code) + psg_error = cls() + psg_error.message = msg + psg_error.status_code = response_error.status + psg_error.error_code = error_code + + return psg_error diff --git a/passageidentity/user.py b/passageidentity/user.py index d5996c3..0f5b11a 100644 --- a/passageidentity/user.py +++ b/passageidentity/user.py @@ -6,6 +6,7 @@ from passageidentity.errors import PassageError from passageidentity.openapi_client.exceptions import ApiException +from passageidentity.openapi_client.models.model404_error import Model404Error from .openapi_client.api import ( TokensApi, @@ -63,8 +64,9 @@ def get_by_identifier(self, identifier: str) -> PassageUser: raise PassageError.from_response_error(e, msg) from e if len(users) == 0: - msg = "User not found." - raise PassageError(msg) + raise PassageError.from_response_error( + ApiException(status=404, data=Model404Error(code="user_not_found", error="User not found.")), + ) return self.get(users[0].id) diff --git a/tests/errors_test.py b/tests/errors_test.py index b87730c..6f03991 100644 --- a/tests/errors_test.py +++ b/tests/errors_test.py @@ -12,24 +12,6 @@ def __init__(self, status: int, data: Model400Error | None, body: str | None = N self.body = body -def test_error_with_all_values() -> None: - error = PassageError("some message", 400, "Bad Request", {"error": "some error", "code": "some_error_code"}) - assert error.message == "some message" - assert error.status_code == 400 - assert error.status_text == "Bad Request" - assert error.error == "some error" - assert error.error_code == "some_error_code" - - -def test_error_with_only_message() -> None: - error = PassageError("some message") - assert error.message == "some message" - assert error.status_code is None - assert error.status_text is None - assert error.error is None - assert error.error_code is None - - def test_from_response_error() -> None: response_error = MockApiException( status=400, @@ -40,8 +22,6 @@ def test_from_response_error() -> None: assert error.message == "some message: some error" assert error.status_code == 400 assert error.error_code == "invalid_request" - assert error.status_text is None - assert error.error is None def test_from_response_error_without_message() -> None: @@ -54,8 +34,6 @@ def test_from_response_error_without_message() -> None: assert error.message == "some error" assert error.status_code == 400 assert error.error_code == "invalid_request" - assert error.status_text is None - assert error.error is None def test_from_response_error_without_data() -> None: @@ -69,5 +47,3 @@ def test_from_response_error_without_data() -> None: assert error.message == "some error" assert error.status_code == 400 assert error.error_code is None - assert error.status_text is None - assert error.error is None