diff --git a/descope/auth.py b/descope/auth.py index eb2b909b7..5bb01d593 100644 --- a/descope/auth.py +++ b/descope/auth.py @@ -34,7 +34,6 @@ def __init__( self, project_id: str, public_key: str = None, - base_uri: str = None, skip_verify: bool = False, ): self.lock_public_keys = Lock() @@ -54,7 +53,7 @@ def __init__( if skip_verify: self.secure = False - self.base_url = base_uri or DEFAULT_BASE_URL + self.base_url = os.getenv("DESCOPE_BASE_URI", None) or DEFAULT_BASE_URL if not public_key: public_key = os.getenv("DESCOPE_PUBLIC_KEY", None) diff --git a/descope/descope_client.py b/descope/descope_client.py index fdccaf2ac..3a9c06b34 100644 --- a/descope/descope_client.py +++ b/descope/descope_client.py @@ -19,10 +19,9 @@ def __init__( self, project_id: str, public_key: str = None, - base_url: str = None, skip_verify: bool = False, ): - auth = Auth(project_id, public_key, base_url, skip_verify) + auth = Auth(project_id, public_key, skip_verify) self._auth = auth self._magiclink = MagicLink(auth) self._oauth = OAuth(auth) diff --git a/samples/decorators/flask_decorators.py b/samples/decorators/flask_decorators.py index abdb70a5b..bac1ec622 100644 --- a/samples/decorators/flask_decorators.py +++ b/samples/decorators/flask_decorators.py @@ -358,7 +358,7 @@ def decorated(*args, **kwargs): # (ignore return value as anyway we redirect) f(*args, **kwargs) - return redirect(redirect_url, 302) + return redirect(redirect_url["url"], 302) return decorated diff --git a/samples/otp_web_sample_app.py b/samples/otp_web_sample_app.py index c18b2a948..1cf10da0b 100644 --- a/samples/otp_web_sample_app.py +++ b/samples/otp_web_sample_app.py @@ -1,28 +1,47 @@ +import datetime import json import os import sys -from flask import Flask, Response, _request_ctx_stack, jsonify, request +from flask import Flask, Response, jsonify, request dir_name = os.path.dirname(__file__) sys.path.insert(0, os.path.join(dir_name, "../")) -from decorators.flask_decorators import ( # noqa: E402; - descope_logout, - descope_validate_auth, - descope_verify_code_by_email, +from descope import ( # noqa: E402 + REFRESH_SESSION_COOKIE_NAME, + SESSION_COOKIE_NAME, + AuthException, + DeliveryMethod, + DescopeClient, ) -from descope import AuthException # noqa: E402 -from descope import DeliveryMethod, DescopeClient # noqa: E402 - APP = Flask(__name__) PROJECT_ID = "" # init the DescopeClient -descope_client = DescopeClient( - PROJECT_ID, base_url="https://172.17.0.1:8443", skip_verify=True -) +descope_client = DescopeClient(PROJECT_ID, skip_verify=True) + + +def set_cookie_on_response(response: Response, data): + cookie_domain = data.get("cookieDomain", "") + if cookie_domain == "": + cookie_domain = None + + current_time = datetime.datetime.now() + expire_time = current_time + datetime.timedelta(days=30) + + return response.set_cookie( + key=data.get("cookieName", ""), + value=data.get("jwt", ""), + max_age=data.get("cookieMaxAge", int(expire_time.timestamp())), + expires=data.get("cookieExpiration", expire_time), + path=data.get("cookiePath", ""), + domain=cookie_domain, + secure=False, # True + httponly=True, + samesite="None", # "Strict", "Lax", "None" + ) class Error(Exception): @@ -52,8 +71,7 @@ def signup(): except AuthException: return Response("Unauthorized", 401) - response = "This is SignUp API handling" - return jsonify(message=response) + return Response("This is SignUp API handling", 200) @APP.route("/api/signin", methods=["POST"]) @@ -68,8 +86,7 @@ def signin(): except AuthException: return Response("Unauthorized, something went wrong when sending email", 401) - response = "This is SignIn API handling" - return jsonify(message=response) + return Response("This is SignIn API handling", 200) @APP.route("/api/signuporin", methods=["POST"]) @@ -84,8 +101,7 @@ def signuporin(): except AuthException: return Response("Unauthorized, something went wrong when sending email", 401) - response = "This is SignUpOrIn API handling" - return jsonify(message=response) + return Response("This is SignUpOrIn API handling", 200) @APP.route("/otp/verify", methods=["POST"]) @@ -101,32 +117,49 @@ def verify(): except AuthException: return Response("Unauthorized", 401) - response = Response(json.dumps(jwt_response), 200) - return response - - -@APP.route("/api/verify_by_decorator", methods=["POST"]) -@descope_verify_code_by_email(descope_client) -def verify_by_decorator(*args, **kwargs): - claims = _request_ctx_stack.top.claims + data = jwt_response["jwts"] + response = Response( + f"This is Verify code API handling, info example: {json.dumps(jwt_response)}", + 200, + ) + for _, cookieData in data.items(): + set_cookie_on_response(response, cookieData) - response = f"This is a code verification API, claims are: {claims}" - return jsonify(message=response) + return response # This needs authentication @APP.route("/api/private", methods=["POST"]) -@descope_validate_auth(descope_client) def private(): - response = "This is a private API and you must be authenticated to see this" - return jsonify(message=response) + cookies = request.cookies.copy() + session_token = cookies.get(SESSION_COOKIE_NAME, None) + refresh_token = cookies.get(REFRESH_SESSION_COOKIE_NAME, None) + try: + data = descope_client.validate_session_request(session_token, refresh_token) + except AuthException: + return Response("Access denied", 401) + + response = Response( + "This is a private API and you must be authenticated to see this", 200 + ) + for _, cookieData in data.items(): + set_cookie_on_response(response, cookieData) + return response @APP.route("/api/logout") -@descope_logout(descope_client) def logout(): - response = "Logged out" - return jsonify(message=response) + cookies = request.cookies.copy() + refresh_token = cookies.get(REFRESH_SESSION_COOKIE_NAME) + try: + descope_client.logout(refresh_token) + except AuthException as e: + return Response(f"Logout failed {e}", e.status_code) + + response = Response("This is Logout API handling", 200) + response.delete_cookie(SESSION_COOKIE_NAME) + response.delete_cookie(REFRESH_SESSION_COOKIE_NAME) + return response # This doesn't need authentication