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: 21 additions & 0 deletions descope/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
COOKIE_DATA_NAME,
DEFAULT_BASE_URL,
PHONE_REGEX,
REFRESH_SESSION_COOKIE_NAME,
REFRESH_SESSION_TOKEN_NAME,
SESSION_TOKEN_NAME,
DeliveryMethod,
Expand Down Expand Up @@ -98,6 +99,22 @@ def do_post(self, uri: str, body: dict, pswd: str = None) -> requests.Response:
)
return response

def exchange_token(self, uri, code: str) -> dict:
if not code:
raise AuthException(
400,
ERROR_TYPE_INVALID_ARGUMENT,
"exchange code is empty",
)

params = Auth._compose_exchange_params(code)
response = self.do_get(uri, params, False)
resp = response.json()
jwt_response = self.generate_jwt_response(
resp, response.cookies.get(REFRESH_SESSION_COOKIE_NAME, None)
)
return jwt_response

@staticmethod
def verify_delivery_method(
method: DeliveryMethod, identifier: str, user: dict
Expand Down Expand Up @@ -206,6 +223,10 @@ def refresh_token(self, refresh_token: str) -> dict:
auth_info = self._generate_auth_info(resp, refresh_token)
return auth_info

@staticmethod
def _compose_exchange_params(code: str) -> dict:
return {"code": code}

@staticmethod
def _validate_and_load_public_key(public_key) -> Tuple[str, jwt.PyJWK, str]:
if isinstance(public_key, str):
Expand Down
31 changes: 0 additions & 31 deletions descope/authmethod/exchanger.py

This file was deleted.

11 changes: 8 additions & 3 deletions descope/authmethod/oauth.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
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_ARGUMENT, AuthException


class OAuth(Exchanger):
class OAuth:
_auth: Auth

def __init__(self, auth: Auth):
super().__init__(auth)
self._auth = auth

def start(self, provider: str, return_url: str = "") -> dict:
""" """
Expand All @@ -23,6 +24,10 @@ def start(self, provider: str, return_url: str = "") -> dict:

return response.json()

def exchange_token(self, code: str) -> dict:
uri = EndpointsV1.oauthExchangeTokenPath
return self._auth.exchange_token(uri, code)

@staticmethod
def _verify_provider(oauth_provider: str) -> str:
if oauth_provider == "" or oauth_provider is None:
Expand Down
11 changes: 8 additions & 3 deletions descope/authmethod/saml.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
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_ARGUMENT, AuthException


class SAML(Exchanger):
class SAML:
_auth: Auth

def __init__(self, auth: Auth):
super().__init__(auth)
self._auth = auth

def start(self, tenant: str, return_url: str = None) -> dict:
"""
Expand All @@ -28,6 +29,10 @@ def start(self, tenant: str, return_url: str = None) -> dict:

return response.json()

def exchange_token(self, code: str) -> dict:
uri = EndpointsV1.samlExchangeTokenPath
return self._auth.exchange_token(uri, code)

@staticmethod
def _compose_start_params(tenant: str, return_url: str) -> dict:
res = {"tenant": tenant}
Expand Down
16 changes: 8 additions & 8 deletions descope/authmethod/webauthn.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def sign_in_finish(self, transaction_id: str, response: str) -> dict:
)
return jwt_response

def add_device_start(self, identifier: str, refresh_token: str, origin: str):
def update_start(self, identifier: str, refresh_token: str, origin: str):
"""
Docs
"""
Expand All @@ -111,13 +111,13 @@ def add_device_start(self, identifier: str, refresh_token: str, origin: str):
400, ERROR_TYPE_INVALID_ARGUMENT, "Refresh token cannot be empty"
)

uri = EndpointsV1.deviceAddAuthWebauthnStart
body = WebauthN._compose_add_device_start_body(identifier, origin)
uri = EndpointsV1.updateAuthWebauthnStart
body = WebauthN._compose_update_start_body(identifier, origin)
response = self._auth.do_post(uri, body, refresh_token)

return response.json()

def add_device_finish(self, transaction_id: str, response: str) -> None:
def update_finish(self, transaction_id: str, response: str) -> None:
"""
Docs
"""
Expand All @@ -131,8 +131,8 @@ def add_device_finish(self, transaction_id: str, response: str) -> None:
400, ERROR_TYPE_INVALID_ARGUMENT, "Response cannot be empty"
)

uri = EndpointsV1.deviceAddAuthWebauthnFinish
body = WebauthN._compose_sign_up_in_finish_body(transaction_id, response)
uri = EndpointsV1.updateAuthWebauthnFinish
body = WebauthN._compose_update_finish_body(transaction_id, response)
self._auth.do_post(uri, body)

@staticmethod
Expand All @@ -155,12 +155,12 @@ def _compose_sign_up_in_finish_body(transaction_id: str, response: str) -> dict:
return {"transactionId": transaction_id, "response": response}

@staticmethod
def _compose_add_device_start_body(identifier: str, origin: str) -> dict:
def _compose_update_start_body(identifier: str, origin: str) -> dict:
body = {"externalId": identifier}
if origin:
body["origin"] = origin
return body

@staticmethod
def _compose_add_device_finish_body(transaction_id: str, response: str) -> dict:
def _compose_update_finish_body(transaction_id: str, response: str) -> dict:
return {"transactionId": transaction_id, "response": response}
49 changes: 24 additions & 25 deletions descope/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,43 +20,42 @@ class EndpointsV1:
logoutPath = "/v1/auth/logoutall"

# otp
signUpAuthOTPPath = "/v1/auth/signup/otp"
signInAuthOTPPath = "/v1/auth/signin/otp"
signUpOrInAuthOTPPath = "/v1/auth/sign-up-or-in/otp"
verifyCodeAuthPath = "/v1/auth/code/verify"
updateUserEmailOTPPath = "/v1/user/update/email/otp"
updateUserPhoneOTPPath = "/v1/user/update/phone/otp"
signUpAuthOTPPath = "/v1/auth/otp/signup"
signInAuthOTPPath = "/v1/auth/otp/signin"
signUpOrInAuthOTPPath = "/v1/auth/otp/signup-in"
verifyCodeAuthPath = "/v1/auth/otp/verify"
updateUserEmailOTPPath = "/v1/auth/otp/update/email"
updateUserPhoneOTPPath = "/v1/auth/otp/update/phone"

# magiclink
signUpAuthMagicLinkPath = "/v1/auth/signup/magiclink"
signInAuthMagicLinkPath = "/v1/auth/signin/magiclink"
signUpOrInAuthMagicLinkPath = "/v1/auth/sign-up-or-in/magiclink"
signUpAuthMagicLinkPath = "/v1/auth/magiclink/signup"
signInAuthMagicLinkPath = "/v1/auth/magiclink/signin"
signUpOrInAuthMagicLinkPath = "/v1/auth/magiclink/signup-in"
verifyMagicLinkAuthPath = "/v1/auth/magiclink/verify"
getSessionMagicLinkAuthPath = "/v1/auth/magiclink/session"
updateUserEmailMagicLinkPath = "/v1/user/update/email/magiclink"
updateUserPhoneMagicLinkPath = "/v1/user/update/phone/magiclink"
getSessionMagicLinkAuthPath = "/v1/auth/magiclink/pending-session"
updateUserEmailMagicLinkPath = "/v1/auth/magiclink/update/email"
updateUserPhoneMagicLinkPath = "/v1/auth/magiclink/update/phone"

# oauth
oauthStart = "/v1/oauth/authorize"
oauthStart = "/v1/auth/oauth/authorize"
oauthExchangeTokenPath = "/v1/auth/oauth/exchange"

# saml
authSAMLStart = "/v1/auth/saml/authorize"

# exchange (saml + oauth)
exchangeTokenPath = "/v1/auth/exchange"
samlExchangeTokenPath = "/v1/auth/saml/exchange"

# totp
signUpAuthTOTPPath = "/v1/auth/signup/totp"
verifyTOTPPath = "/v1/auth/verify/totp"
updateTOTPPath = "/v1/user/update/totp"
signUpAuthTOTPPath = "/v1/auth/totp/signup"
verifyTOTPPath = "/v1/auth/totp/verify"
updateTOTPPath = "/v1/auth/totp/update"

# webauthn
signUpAuthWebauthnStart = "/v1/webauthn/signup/start"
signUpAuthWebauthnFinish = "/v1/webauthn/signup/finish"
signInAuthWebauthnStart = "/v1/webauthn/signin/start"
signInAuthWebauthnFinish = "/v1/webauthn/signin/finish"
deviceAddAuthWebauthnStart = "/v1/webauthn/device/add/start"
deviceAddAuthWebauthnFinish = "/v1/webauthn/device/add/finish"
signUpAuthWebauthnStart = "/v1/auth/webauthn/signup/start"
signUpAuthWebauthnFinish = "/v1/auth/webauthn/signup/finish"
signInAuthWebauthnStart = "/v1/auth/webauthn/signin/start"
signInAuthWebauthnFinish = "/v1/auth/webauthn/signin/finish"
updateAuthWebauthnStart = "/v1/auth/webauthn/update/start"
updateAuthWebauthnFinish = "/v1/auth/webauthn/update/finish"


class DeliveryMethod(Enum):
Expand Down
1 change: 0 additions & 1 deletion descope/descope_client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import requests

from descope.auth import Auth # noqa: F401
from descope.authmethod.exchanger import Exchanger # noqa: F401
from descope.authmethod.magiclink import MagicLink # noqa: F401
from descope.authmethod.oauth import OAuth # noqa: F401
from descope.authmethod.otp import OTP # noqa: F401
Expand Down
8 changes: 4 additions & 4 deletions samples/webauthn_web_sample_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,19 @@ def webauthn_signin_finish():


@APP.route("/webauthn/device/add/start", methods=["POST"])
def webauthn_add_device_start():
def webauthn_update_start():
data = request.get_json()
refresh_token = request.cookies.get("DSR")
response = descope_client.webauthn.add_device_start(
response = descope_client.webauthn.update_start(
data["externalId"], refresh_token, data["origin"]
)
return response


@APP.route("/webauthn/device/add/finish", methods=["POST"])
def webauthn_add_device_finish():
def webauthn_update_finish():
data = request.get_json()
descope_client.webauthn.add_device_finish(data["transactionId"], data["response"])
descope_client.webauthn.update_finish(data["transactionId"], data["response"])
return jsonify("{}")


Expand Down
63 changes: 0 additions & 63 deletions tests/test_exchanger.py

This file was deleted.

8 changes: 4 additions & 4 deletions tests/test_magiclink.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,20 @@ def setUp(self) -> None:
def test_compose_urls(self):
self.assertEqual(
MagicLink._compose_signin_url(DeliveryMethod.PHONE),
"/v1/auth/signin/magiclink/sms",
"/v1/auth/magiclink/signin/sms",
)
self.assertEqual(
MagicLink._compose_signup_url(DeliveryMethod.WHATSAPP),
"/v1/auth/signup/magiclink/whatsapp",
"/v1/auth/magiclink/signup/whatsapp",
)
self.assertEqual(
MagicLink._compose_sign_up_or_in_url(DeliveryMethod.EMAIL),
"/v1/auth/sign-up-or-in/magiclink/email",
"/v1/auth/magiclink/signup-in/email",
)

self.assertEqual(
MagicLink._compose_update_phone_url(DeliveryMethod.PHONE),
"/v1/user/update/phone/magiclink/sms",
"/v1/auth/magiclink/update/phone/sms",
)

def test_compose_body(self):
Expand Down
Loading