Skip to content
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
21 changes: 16 additions & 5 deletions descope/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,21 +100,32 @@ def do_post(self, uri: str, body: dict, pswd: str = None) -> requests.Response:
return response

@staticmethod
def verify_delivery_method(method: DeliveryMethod, identifier: str) -> bool:
if identifier == "" or identifier is None:
def verify_delivery_method(
method: DeliveryMethod, identifier: str, user: dict
) -> bool:
if not identifier:
return False

if not user or not isinstance(user, dict):
return False

if method == DeliveryMethod.EMAIL:
if not user.get("email", None):
user["email"] = identifier
try:
validate_email(identifier)
validate_email(user["email"])
return True
except EmailNotValidError:
return False
elif method == DeliveryMethod.PHONE:
if not re.match(PHONE_REGEX, identifier):
if not user.get("phone", None):
user["phone"] = identifier
if not re.match(PHONE_REGEX, user["phone"]):
return False
elif method == DeliveryMethod.WHATSAPP:
if not re.match(PHONE_REGEX, identifier):
if not user.get("phone", None):
user["phone"] = identifier
if not re.match(PHONE_REGEX, user["phone"]):
return False
else:
return False
Expand Down
24 changes: 9 additions & 15 deletions descope/authmethod/magiclink.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from descope.auth import Auth
from descope.common import REFRESH_SESSION_COOKIE_NAME, DeliveryMethod, EndpointsV1
from descope.exceptions import ERROR_TYPE_INVALID_PUBLIC_KEY, AuthException
from descope.exceptions import ERROR_TYPE_INVALID_ARGUMENT, AuthException


class MagicLink:
Expand Down Expand Up @@ -90,11 +90,11 @@ def update_user_phone_cross_device(
def _sign_in(
self, method: DeliveryMethod, identifier: str, uri: str, cross_device: bool
) -> requests.Response:
if not self._auth.verify_delivery_method(method, identifier):
if not identifier:
raise AuthException(
400,
ERROR_TYPE_INVALID_PUBLIC_KEY,
f"Identifier {identifier} does not support delivery method {method}",
ERROR_TYPE_INVALID_ARGUMENT,
"Identifier is empty",
)

body = MagicLink._compose_signin_body(identifier, uri, cross_device)
Expand All @@ -110,11 +110,11 @@ def _sign_up(
cross_device: bool,
user: dict = None,
) -> requests.Response:
if not self._auth.verify_delivery_method(method, identifier):
if not self._auth.verify_delivery_method(method, identifier, user):
raise AuthException(
400,
ERROR_TYPE_INVALID_PUBLIC_KEY,
f"Identifier {identifier} does not support delivery method {method}",
ERROR_TYPE_INVALID_ARGUMENT,
f"Identifier {identifier} is not valid by delivery method {method}",
)

body = MagicLink._compose_signup_body(
Expand All @@ -126,12 +126,6 @@ def _sign_up(
def _sign_up_or_in(
self, method: DeliveryMethod, identifier: str, uri: str, cross_device: bool
) -> requests.Response:
if not self._auth.verify_delivery_method(method, identifier):
raise AuthException(
400,
ERROR_TYPE_INVALID_PUBLIC_KEY,
f"Identifier {identifier} does not support delivery method {method}",
)

body = MagicLink._compose_signin_body(identifier, uri, cross_device)
uri = MagicLink._compose_sign_up_or_in_url(method)
Expand All @@ -142,7 +136,7 @@ def _update_user_email(
) -> requests.Response:
if not identifier:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Identifier cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Identifier cannot be empty"
)

Auth.validate_email(email)
Expand All @@ -163,7 +157,7 @@ def _update_user_phone(
) -> requests.Response:
if not identifier:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Identifier cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Identifier cannot be empty"
)

Auth.validate_phone(method, phone)
Expand Down
4 changes: 2 additions & 2 deletions descope/authmethod/oauth.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from descope.auth import Auth
from descope.authmethod.exchanger import Exchanger # noqa: F401
from descope.common import EndpointsV1, OAuthProviders
from descope.exceptions import ERROR_TYPE_INVALID_PUBLIC_KEY, AuthException
from descope.exceptions import ERROR_TYPE_INVALID_ARGUMENT, AuthException


class OAuth(Exchanger):
Expand All @@ -13,7 +13,7 @@ def start(self, provider: str, return_url: str = "") -> dict:
if not self._verify_provider(provider):
raise AuthException(
400,
ERROR_TYPE_INVALID_PUBLIC_KEY,
ERROR_TYPE_INVALID_ARGUMENT,
f"Unknown OAuth provider: {provider}",
)

Expand Down
32 changes: 12 additions & 20 deletions descope/authmethod/otp.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from descope.auth import Auth
from descope.common import REFRESH_SESSION_COOKIE_NAME, DeliveryMethod, EndpointsV1
from descope.exceptions import ERROR_TYPE_INVALID_PUBLIC_KEY, AuthException
from descope.exceptions import ERROR_TYPE_INVALID_ARGUMENT, AuthException


class OTP:
Expand All @@ -23,12 +23,9 @@ def sign_in(self, method: DeliveryMethod, identifier: str) -> None:
Raise:
AuthException: raised if sign-in operation fails
"""

if not self._auth.verify_delivery_method(method, identifier):
if not identifier:
raise AuthException(
400,
ERROR_TYPE_INVALID_PUBLIC_KEY,
f"Identifier {identifier} does not support delivery method {method}",
400, ERROR_TYPE_INVALID_ARGUMENT, "Identifier cannot be empty"
)

uri = OTP._compose_signin_url(method)
Expand All @@ -53,11 +50,11 @@ def sign_up(
AuthException: raised if sign-up operation fails
"""

if not self._auth.verify_delivery_method(method, identifier):
if not self._auth.verify_delivery_method(method, identifier, user):
raise AuthException(
400,
ERROR_TYPE_INVALID_PUBLIC_KEY,
f"Identifier {identifier} is does not support delivery method {method}",
ERROR_TYPE_INVALID_ARGUMENT,
f"Identifier {identifier} is not valid by delivery method {method}",
)

uri = OTP._compose_signup_url(method)
Expand All @@ -78,11 +75,9 @@ def sign_up_or_in(self, method: DeliveryMethod, identifier: str) -> None:
Raise:
AuthException: raised if either the sign_up or sign_in operation fails
"""
if not self._auth.verify_delivery_method(method, identifier):
if not identifier:
raise AuthException(
400,
ERROR_TYPE_INVALID_PUBLIC_KEY,
f"Identifier {identifier} does not support delivery method {method}",
400, ERROR_TYPE_INVALID_ARGUMENT, "Identifier cannot be empty"
)

uri = OTP._compose_sign_up_or_in_url(method)
Expand All @@ -107,12 +102,9 @@ def verify_code(self, method: DeliveryMethod, identifier: str, code: str) -> dic
Raise:
AuthException: raised if the OTP code is not valid or if token verification failed
"""

if not self._auth.verify_delivery_method(method, identifier):
if not identifier:
raise AuthException(
400,
ERROR_TYPE_INVALID_PUBLIC_KEY,
f"Identifier {identifier} does not support delivery method {method}",
400, ERROR_TYPE_INVALID_ARGUMENT, "Identifier cannot be empty"
)

uri = OTP._compose_verify_code_url(method)
Expand Down Expand Up @@ -142,7 +134,7 @@ def update_user_email(

if not identifier:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Identifier cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Identifier cannot be empty"
)

Auth.validate_email(email)
Expand All @@ -169,7 +161,7 @@ def update_user_phone(

if not identifier:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Identifier cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Identifier cannot be empty"
)

Auth.validate_phone(method, phone)
Expand Down
6 changes: 3 additions & 3 deletions descope/authmethod/saml.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from descope.auth import Auth
from descope.authmethod.exchanger import Exchanger # noqa: F401
from descope.common import EndpointsV1
from descope.exceptions import ERROR_TYPE_INVALID_PUBLIC_KEY, AuthException
from descope.exceptions import ERROR_TYPE_INVALID_ARGUMENT, AuthException


class SAML(Exchanger):
Expand All @@ -14,12 +14,12 @@ def start(self, tenant: str, return_url: str = None) -> dict:
"""
if not tenant:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Tenant cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Tenant cannot be empty"
)

if not return_url:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Return url cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Return url cannot be empty"
)

uri = EndpointsV1.authSAMLStart
Expand Down
12 changes: 6 additions & 6 deletions descope/authmethod/totp.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from descope.auth import Auth
from descope.common import REFRESH_SESSION_COOKIE_NAME, EndpointsV1
from descope.exceptions import ERROR_TYPE_INVALID_PUBLIC_KEY, AuthException
from descope.exceptions import ERROR_TYPE_INVALID_ARGUMENT, AuthException


class TOTP:
Expand All @@ -16,7 +16,7 @@ def sign_up(self, identifier: str, user: dict = None) -> dict:

if not identifier:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Identifier cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Identifier cannot be empty"
)

uri = EndpointsV1.signUpAuthTOTPPath
Expand All @@ -37,12 +37,12 @@ def sign_in_code(self, identifier: str, code: str) -> dict:

if not identifier:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Identifier cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Identifier cannot be empty"
)

if not code:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Code cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Code cannot be empty"
)

uri = EndpointsV1.verifyTOTPPath
Expand All @@ -62,12 +62,12 @@ def update_user(self, identifier: str, refresh_token: str) -> None:

if not identifier:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Identifier cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Identifier cannot be empty"
)

if not refresh_token:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Refresh token cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Refresh token cannot be empty"
)

uri = EndpointsV1.updateTOTPPath
Expand Down
26 changes: 13 additions & 13 deletions descope/authmethod/webauthn.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from descope.auth import Auth
from descope.common import REFRESH_SESSION_COOKIE_NAME, EndpointsV1
from descope.exceptions import ERROR_TYPE_INVALID_PUBLIC_KEY, AuthException
from descope.exceptions import ERROR_TYPE_INVALID_ARGUMENT, AuthException


class WebauthN:
Expand All @@ -15,12 +15,12 @@ def sign_up_start(self, identifier: str, origin: str, user: dict = None) -> dict
"""
if not identifier:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Identifier cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Identifier cannot be empty"
)

if not origin:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Origin cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Origin cannot be empty"
)

uri = EndpointsV1.signUpAuthWebauthnStart
Expand All @@ -35,12 +35,12 @@ def sign_up_finish(self, transactionID: str, response: str) -> dict:
"""
if not transactionID:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Transaction id cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Transaction id cannot be empty"
)

if not response:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Response cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Response cannot be empty"
)

uri = EndpointsV1.signUpAuthWebauthnFinish
Expand All @@ -59,12 +59,12 @@ def sign_in_start(self, identifier: str, origin: str) -> dict:
"""
if not identifier:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Identifier cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Identifier cannot be empty"
)

if not origin:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Origin cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Origin cannot be empty"
)

uri = EndpointsV1.signInAuthWebauthnStart
Expand All @@ -79,12 +79,12 @@ def sign_in_finish(self, transaction_id: str, response: str) -> dict:
"""
if not transaction_id:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Transaction id cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Transaction id cannot be empty"
)

if not response:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Response cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Response cannot be empty"
)

uri = EndpointsV1.signInAuthWebauthnFinish
Expand All @@ -103,12 +103,12 @@ def add_device_start(self, identifier: str, refresh_token: str, origin: str):
"""
if not identifier:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Identifier cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Identifier cannot be empty"
)

if not refresh_token:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Refresh token cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Refresh token cannot be empty"
)

uri = EndpointsV1.deviceAddAuthWebauthnStart
Expand All @@ -123,12 +123,12 @@ def add_device_finish(self, transaction_id: str, response: str) -> None:
"""
if not transaction_id:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Transaction id cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Transaction id cannot be empty"
)

if not response:
raise AuthException(
400, ERROR_TYPE_INVALID_PUBLIC_KEY, "Response cannot be empty"
400, ERROR_TYPE_INVALID_ARGUMENT, "Response cannot be empty"
)

uri = EndpointsV1.deviceAddAuthWebauthnFinish
Expand Down
6 changes: 4 additions & 2 deletions samples/otp_web_sample_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,10 @@ def signin():

try:
descope_client.otp.sign_in(DeliveryMethod.EMAIL, email)
except AuthException:
return Response("Unauthorized, something went wrong when sending email", 401)
except AuthException as ex:
return Response(
f"Unauthorized, something went wrong when sending email {ex}", 401
)

return Response("This is SignIn API handling", 200)

Expand Down
Loading